34
34
import java .util .Collections ;
35
35
import java .util .Comparator ;
36
36
import java .util .HashMap ;
37
+ import java .util .HashSet ;
37
38
import java .util .List ;
38
39
import java .util .Map ;
39
40
import java .util .logging .Level ;
@@ -55,6 +56,18 @@ public class BuildWatcher extends BaseWatcher implements Watcher<Build> {
55
56
private static final Logger logger = Logger .getLogger (BuildWatcher .class
56
57
.getName ());
57
58
59
+ // the fabric8 classes like Build have equal/hashcode annotations that
60
+ // should allow
61
+ // us to index via the objects themselves;
62
+ // now that listing interval is 5 minutes (used to be 10 seconds), we have
63
+ // seen
64
+ // timing windows where if the build watch events come before build config
65
+ // watch events
66
+ // when both are created in a simultaneous fashion, there is an up to 5
67
+ // minute delay
68
+ // before the job run gets kicked off
69
+ private static final HashSet <Build > buildsWithNoBCList = new HashSet <Build >();
70
+
58
71
@ SuppressFBWarnings ("EI_EXPOSE_REP2" )
59
72
public BuildWatcher (String [] namespaces ) {
60
73
super (namespaces );
@@ -69,6 +82,11 @@ public void doRun() {
69
82
logger .fine ("No Openshift Token credential defined." );
70
83
return ;
71
84
}
85
+ // prior to finding new builds poke the BuildWatcher builds with
86
+ // no BC list and see if we
87
+ // can create job runs for premature builds we already know
88
+ // about
89
+ BuildWatcher .flushBuildsWithNoBCList ();
72
90
for (String namespace : namespaces ) {
73
91
try {
74
92
logger .fine ("listing Build resources" );
@@ -113,6 +131,8 @@ public void start() {
113
131
@ SuppressFBWarnings ("SF_SWITCH_NO_DEFAULT" )
114
132
@ Override
115
133
public synchronized void eventReceived (Action action , Build build ) {
134
+ if (!OpenShiftUtils .isPipelineStrategyBuild (build ))
135
+ return ;
116
136
try {
117
137
switch (action ) {
118
138
case ADDED :
@@ -151,6 +171,8 @@ public int compare(Build b1, Build b2) {
151
171
Map <BuildConfig , List <Build >> buildConfigBuildMap = new HashMap <>(
152
172
items .size ());
153
173
for (Build b : items ) {
174
+ if (!OpenShiftUtils .isPipelineStrategyBuild (b ))
175
+ continue ;
154
176
String buildConfigName = b .getStatus ().getConfig ().getName ();
155
177
if (StringUtils .isEmpty (buildConfigName )) {
156
178
continue ;
@@ -163,6 +185,9 @@ public int compare(Build b1, Build b2) {
163
185
.inNamespace (namespace ).withName (buildConfigName )
164
186
.get ();
165
187
if (bc == null ) {
188
+ // if the bc is not there via a REST get, then it is not
189
+ // going to be, and we are not handling manual creation
190
+ // of pipeline build objects, so don't bother with "no bc list"
166
191
continue ;
167
192
}
168
193
buildConfigMap .put (bcMapKey , bc );
@@ -185,11 +210,25 @@ public int compare(Build b1, Build b2) {
185
210
}
186
211
WorkflowJob job = getJobFromBuildConfig (bc );
187
212
if (job == null ) {
213
+ List <Build > builds = buildConfigBuilds .getValue ();
214
+ for (Build b : builds ) {
215
+ logger .info ("skipping listed new build "
216
+ + b .getMetadata ().getName ()
217
+ + " no job at this time" );
218
+ addBuildToNoBCList (b );
219
+ }
188
220
continue ;
189
221
}
190
222
BuildConfigProjectProperty bcp = job
191
223
.getProperty (BuildConfigProjectProperty .class );
192
224
if (bcp == null ) {
225
+ List <Build > builds = buildConfigBuilds .getValue ();
226
+ for (Build b : builds ) {
227
+ logger .info ("skipping listed new build "
228
+ + b .getMetadata ().getName ()
229
+ + " no prop at this time" );
230
+ addBuildToNoBCList (b );
231
+ }
193
232
continue ;
194
233
}
195
234
List <Build > builds = buildConfigBuilds .getValue ();
@@ -204,12 +243,20 @@ private static synchronized void modifyEventToJenkinsJobRun(Build build) {
204
243
WorkflowJob job = getJobFromBuild (build );
205
244
if (job != null ) {
206
245
cancelBuild (job , build );
246
+ } else {
247
+ removeBuildFromNoBCList (build );
207
248
}
249
+ } else {
250
+ // see if any pre-BC cached builds can now be flushed
251
+ flushBuildsWithNoBCList ();
208
252
}
209
253
}
210
254
211
255
public static synchronized boolean addEventToJenkinsJobRun (Build build )
212
256
throws IOException {
257
+ // should have been caught upstack, but just in case since public method
258
+ if (!OpenShiftUtils .isPipelineStrategyBuild (build ))
259
+ return false ;
213
260
BuildStatus status = build .getStatus ();
214
261
if (status != null ) {
215
262
if (isCancelled (status )) {
@@ -225,9 +272,48 @@ public static synchronized boolean addEventToJenkinsJobRun(Build build)
225
272
if (job != null ) {
226
273
return triggerJob (job , build );
227
274
}
275
+ logger .info ("skipping watch event for build "
276
+ + build .getMetadata ().getName () + " no job at this time" );
277
+ addBuildToNoBCList (build );
228
278
return false ;
229
279
}
230
280
281
+ public static synchronized void addBuildToNoBCList (Build build ) {
282
+ // should have been caught upstack, but just in case since public method
283
+ if (!OpenShiftUtils .isPipelineStrategyBuild (build ))
284
+ return ;
285
+ buildsWithNoBCList .add (build );
286
+ }
287
+
288
+ private static synchronized void removeBuildFromNoBCList (Build build ) {
289
+ buildsWithNoBCList .remove (build );
290
+ }
291
+
292
+ private static synchronized void clearNoBCList () {
293
+ buildsWithNoBCList .clear ();
294
+ }
295
+
296
+ // trigger any builds whose watch events arrived before the
297
+ // corresponding build config watch events
298
+ public static synchronized void flushBuildsWithNoBCList () {
299
+ HashSet <Build > clone = (HashSet <Build >) buildsWithNoBCList .clone ();
300
+ clearNoBCList ();
301
+ for (Build build : clone ) {
302
+ WorkflowJob job = getJobFromBuild (build );
303
+ if (job != null )
304
+ try {
305
+ logger .info ("triggering job run for previously skipped build "
306
+ + build .getMetadata ().getName ());
307
+ triggerJob (job , build );
308
+ } catch (IOException e ) {
309
+ logger .log (Level .WARNING , "flushCachedBuilds" , e );
310
+ }
311
+ else
312
+ addBuildToNoBCList (build );
313
+ }
314
+
315
+ }
316
+
231
317
// innerDeleteEventToJenkinsJobRun is the actual delete logic at the heart
232
318
// of deleteEventToJenkinsJobRun
233
319
// that is either in a sync block or not based on the presence of a BC uid
@@ -243,6 +329,10 @@ public Void call() throws Exception {
243
329
return null ;
244
330
}
245
331
});
332
+ } else {
333
+ // in case build was created and deleted quickly, prior to seeing BC
334
+ // event, clear out from pre-BC cache
335
+ removeBuildFromNoBCList (build );
246
336
}
247
337
}
248
338
0 commit comments