From 7a4879c3e4755d88707f8bfc84579f4e05f8300a Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Tue, 1 Aug 2017 12:57:01 -0400 Subject: [PATCH] ability to force kill zombie builds --- .drone.yml | 4 ++-- router/router.go | 1 + server/build.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++ version/version.go | 2 +- 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index 80ade219e..7d3c2467e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -80,7 +80,7 @@ pipeline: image: plugins/docker repo: drone/drone secrets: [ docker_username, docker_password ] - tag: [ 0.8, 0.8.0 ] + tag: [ 0.8, 0.8.0, 0.8.0-rc.3 ] when: event: tag @@ -89,7 +89,7 @@ pipeline: repo: drone/agent dockerfile: Dockerfile.agent secrets: [ docker_username, docker_password ] - tag: [ 0.8, 0.8.0 ] + tag: [ 0.8, 0.8.0, 0.8.0-rc.3 ] when: event: tag diff --git a/router/router.go b/router/router.go index 3adbcb2ad..a05b65beb 100644 --- a/router/router.go +++ b/router/router.go @@ -106,6 +106,7 @@ func Load(mux *httptreemux.ContextMux, middleware ...gin.HandlerFunc) http.Handl repo.POST("/repair", session.MustRepoAdmin(), server.RepairRepo) repo.POST("/builds/:number", session.MustPush, server.PostBuild) + repo.DELETE("/builds/:number", session.MustAdmin(), server.ZombieKill) repo.POST("/builds/:number/approve", session.MustPush, server.PostApproval) repo.POST("/builds/:number/decline", session.MustPush, server.PostDecline) repo.DELETE("/builds/:number/:job", session.MustPush, server.DeleteBuild) diff --git a/server/build.go b/server/build.go index 45470e9d0..70d2e2b5a 100644 --- a/server/build.go +++ b/server/build.go @@ -144,6 +144,56 @@ func DeleteBuild(c *gin.Context) { c.String(204, "") } +// ZombieKill kills zombie processes stuck in an infinite pending +// or running state. This can only be invoked by administrators and +// may have negative effects. +func ZombieKill(c *gin.Context) { + repo := session.Repo(c) + + // parse the build number and job sequence number from + // the repquest parameter. + num, _ := strconv.Atoi(c.Params.ByName("number")) + + build, err := store.GetBuildNumber(c, repo, num) + if err != nil { + c.AbortWithError(404, err) + return + } + + procs, err := store.FromContext(c).ProcList(build) + if err != nil { + c.AbortWithError(404, err) + return + } + + if build.Status != model.StatusRunning { + c.String(400, "Cannot force cancel a non-running build") + return + } + + for _, proc := range procs { + if proc.Running() { + proc.State = model.StatusKilled + proc.ExitCode = 137 + proc.Stopped = time.Now().Unix() + if proc.Started == 0 { + proc.Started = proc.Stopped + } + } + } + + for _, proc := range procs { + store.FromContext(c).ProcUpdate(proc) + Config.Services.Queue.Error(context.Background(), fmt.Sprint(proc.ID), queue.ErrCancel) + } + + build.Status = model.StatusKilled + build.Finished = time.Now().Unix() + store.FromContext(c).UpdateBuild(build) + + c.String(204, "") +} + func PostApproval(c *gin.Context) { var ( remote_ = remote.FromContext(c) diff --git a/version/version.go b/version/version.go index bedfec3ac..d836006f5 100644 --- a/version/version.go +++ b/version/version.go @@ -10,7 +10,7 @@ var ( // VersionPatch is for backwards-compatible bug fixes VersionPatch int64 = 0 // VersionPre indicates prerelease - VersionPre string + VersionPre string = "rc.3" // VersionDev indicates development branch. Releases will be empty string. VersionDev string )