Skip to content

Commit 95b3459

Browse files
committed
build controller: fix start of serial builds on cancellation/deletion
1 parent 58c9ab7 commit 95b3459

File tree

2 files changed

+82
-1
lines changed

2 files changed

+82
-1
lines changed

pkg/build/controller/build/build_controller.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ func NewBuildController(params *BuildControllerParams) *BuildController {
213213
c.buildInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
214214
AddFunc: c.buildAdded,
215215
UpdateFunc: c.buildUpdated,
216+
DeleteFunc: c.buildDeleted,
216217
})
217218
params.ImageStreamInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
218219
AddFunc: c.imageStreamAdded,
@@ -1067,10 +1068,14 @@ func (bc *BuildController) handleBuildConfig(bcNamespace string, bcName string)
10671068
return err
10681069
}
10691070
glog.V(5).Infof("Build config %s/%s: has %d next builds, is running builds: %v", bcNamespace, bcName, len(nextBuilds), hasRunningBuilds)
1070-
if len(nextBuilds) == 0 && hasRunningBuilds {
1071+
if hasRunningBuilds {
10711072
glog.V(4).Infof("Build config %s/%s has running builds, will retry", bcNamespace, bcName)
10721073
return fmt.Errorf("build config %s/%s has running builds and cannot run more builds", bcNamespace, bcName)
10731074
}
1075+
if len(nextBuilds) == 0 {
1076+
glog.V(4).Infof("Build config %s/%s has no builds to run next, will retry", bcNamespace, bcName)
1077+
return fmt.Errorf("build config %s/%s has no builds to run next", bcNamespace, bcName)
1078+
}
10741079

10751080
// Enqueue any builds to build next
10761081
for _, build := range nextBuilds {
@@ -1174,6 +1179,29 @@ func (bc *BuildController) buildUpdated(old, cur interface{}) {
11741179
bc.enqueueBuild(build)
11751180
}
11761181

1182+
// buildDeleted is called by the build informer event handler whenever a build
1183+
// is deleted
1184+
func (bc *BuildController) buildDeleted(obj interface{}) {
1185+
build, ok := obj.(*buildapi.Build)
1186+
if !ok {
1187+
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
1188+
if !ok {
1189+
utilruntime.HandleError(fmt.Errorf("couldn't get object from tombstone: %+v", obj))
1190+
return
1191+
}
1192+
build, ok = tombstone.Obj.(*buildapi.Build)
1193+
if !ok {
1194+
utilruntime.HandleError(fmt.Errorf("tombstone contained object that is not a pod: %+v", obj))
1195+
return
1196+
}
1197+
}
1198+
// If the build was not in a complete state, poke the buildconfig to run the next build
1199+
if !buildutil.IsBuildComplete(build) {
1200+
bcName := buildutil.ConfigNameForBuild(build)
1201+
bc.enqueueBuildConfig(build.Namespace, bcName)
1202+
}
1203+
}
1204+
11771205
// enqueueBuild adds the given build to the buildQueue.
11781206
func (bc *BuildController) enqueueBuild(build *buildapi.Build) {
11791207
key := resourceName(build.Namespace, build.Name)

test/extended/builds/run_policy.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package builds
22

33
import (
4+
"fmt"
45
"strings"
56
"time"
67

@@ -310,6 +311,58 @@ var _ = g.Describe("[Feature:Builds][Slow] using build configuration runPolicy",
310311
})
311312
})
312313

314+
g.Describe("build configuration with Serial build run policy handling deletion", func() {
315+
g.It("starts the next build immediately after running one is deleted", func() {
316+
g.By("starting multiple builds")
317+
318+
bcName := "sample-serial-build"
319+
for i := 0; i < 3; i++ {
320+
_, _, err := exutil.StartBuild(oc, bcName)
321+
o.Expect(err).NotTo(o.HaveOccurred())
322+
}
323+
324+
buildWatch, err := oc.BuildClient().Build().Builds(oc.Namespace()).Watch(metav1.ListOptions{
325+
LabelSelector: buildutil.BuildConfigSelector(bcName).String(),
326+
})
327+
defer buildWatch.Stop()
328+
o.Expect(err).NotTo(o.HaveOccurred())
329+
330+
var deleteTime, deleteTime2 time.Time
331+
done := false
332+
var timedOut error
333+
for !done {
334+
select {
335+
case event := <-buildWatch.ResultChan():
336+
build := event.Object.(*buildapi.Build)
337+
if build.Status.Phase == buildapi.BuildPhasePending {
338+
if build.Name == "sample-serial-build-1" {
339+
err := oc.Run("delete").Args("build", "sample-serial-build-1", "--ignore-not-found").Execute()
340+
o.Expect(err).ToNot(o.HaveOccurred())
341+
deleteTime = time.Now()
342+
}
343+
if build.Name == "sample-serial-build-2" {
344+
duration := time.Now().Sub(deleteTime)
345+
o.Expect(duration).To(o.BeNumerically("<", 20*time.Second), "next build should have started less than 20s after deleted build")
346+
err := oc.Run("delete").Args("build", "sample-serial-build-2", "--ignore-not-found").Execute()
347+
o.Expect(err).ToNot(o.HaveOccurred())
348+
deleteTime2 = time.Now()
349+
}
350+
if build.Name == "sample-serial-build-3" {
351+
duration := time.Now().Sub(deleteTime2)
352+
o.Expect(duration).To(o.BeNumerically("<", 20*time.Second), "next build should have started less than 20s after deleted build")
353+
done = true
354+
}
355+
}
356+
case <-time.After(2 * time.Minute):
357+
// Give up waiting and finish after 2 minutes
358+
timedOut = fmt.Errorf("timed out waiting for pending build")
359+
done = true
360+
}
361+
}
362+
o.Expect(timedOut).NotTo(o.HaveOccurred())
363+
})
364+
})
365+
313366
g.Describe("build configuration with SerialLatestOnly build run policy", func() {
314367
g.It("runs the builds in serial order but cancel previous builds", func() {
315368
g.By("starting multiple builds")

0 commit comments

Comments
 (0)