diff --git a/server/build.go b/server/build.go index 74edae3e6..0dfffd46a 100644 --- a/server/build.go +++ b/server/build.go @@ -249,9 +249,10 @@ func ZombieKill(c *gin.Context) { 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) + if _, err := UpdateToStatusKilled(store.FromContext(c), *build); err != nil { + c.AbortWithError(500, err) + return + } c.String(204, "") } @@ -275,9 +276,6 @@ func PostApproval(c *gin.Context) { c.String(500, "cannot decline a build with status %s", build.Status) return } - build.Status = model.StatusPending - build.Reviewed = time.Now().Unix() - build.Reviewer = user.Login // fetch the build file from the database configs, err := Config.Storage.Config.ConfigsForBuild(build.ID) @@ -289,12 +287,12 @@ func PostApproval(c *gin.Context) { netrc, err := remote_.Netrc(user, repo) if err != nil { - c.String(500, "Failed to generate netrc file. %s", err) + c.String(500, "failed to generate netrc file. %s", err) return } - if uerr := store.UpdateBuild(c, build); err != nil { - c.String(500, "error updating build. %s", uerr) + if build, err = UpdateToStatusPending(store.FromContext(c), *build, user.Login); err != nil { + c.String(500, "error updating build. %s", err) return } @@ -337,11 +335,9 @@ func PostApproval(c *gin.Context) { } buildItems, err := b.Build() if err != nil { - build.Status = model.StatusError - build.Started = time.Now().Unix() - build.Finished = build.Started - build.Error = err.Error() - store.UpdateBuild(c, build) + if _, err = UpdateToStatusError(store.FromContext(c), *build, err); err != nil { + logrus.Errorf("Error setting error status of build for %s#%d. %s", repo.FullName, build.Number, err) + } return } build = setBuildStepsOnBuild(b.Curr, buildItems) @@ -388,12 +384,8 @@ func PostDecline(c *gin.Context) { c.String(500, "cannot decline a build with status %s", build.Status) return } - build.Status = model.StatusDeclined - build.Reviewed = time.Now().Unix() - build.Reviewer = user.Login - err = store.UpdateBuild(c, build) - if err != nil { + if _, err = UpdateToStatusDeclined(store.FromContext(c), *build, user.Login); err != nil { c.String(500, "error updating build. %s", err) return } diff --git a/server/buildStatus_test.go b/server/buildStatus_test.go new file mode 100644 index 000000000..59bd40d63 --- /dev/null +++ b/server/buildStatus_test.go @@ -0,0 +1,120 @@ +// Copyright 2019 mhmxs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "errors" + "testing" + "time" + + "github.com/laszlocph/woodpecker/model" +) + +const TIMEOUT = 3 * time.Second + +type mockUpdateBuildStore struct { +} + +func (m *mockUpdateBuildStore) UpdateBuild(build *model.Build) error { + return nil +} + +func TestUpdateToStatusRunning(t *testing.T) { + t.Parallel() + + build, _ := UpdateToStatusRunning(&mockUpdateBuildStore{}, model.Build{}, int64(1)) + + if model.StatusRunning != build.Status { + t.Errorf("Build status not equals '%s' != '%s'", model.StatusRunning, build.Status) + } else if int64(1) != build.Started { + t.Errorf("Build started not equals 1 != %d", build.Started) + } +} + +func TestUpdateToStatusPending(t *testing.T) { + t.Parallel() + + now := time.Now().Unix() + + build, _ := UpdateToStatusPending(&mockUpdateBuildStore{}, model.Build{}, "Reviewer") + + if model.StatusPending != build.Status { + t.Errorf("Build status not equals '%s' != '%s'", model.StatusPending, build.Status) + } else if "Reviewer" != build.Reviewer { + t.Errorf("Reviewer not equals 'Reviewer' != '%s'", build.Reviewer) + } else if now > build.Reviewed { + t.Errorf("Reviewed not updated %d !< %d", now, build.Reviewed) + } +} + +func TestUpdateToStatusDeclined(t *testing.T) { + t.Parallel() + + now := time.Now().Unix() + + build, _ := UpdateToStatusDeclined(&mockUpdateBuildStore{}, model.Build{}, "Reviewer") + + if model.StatusDeclined != build.Status { + t.Errorf("Build status not equals '%s' != '%s'", model.StatusDeclined, build.Status) + } else if "Reviewer" != build.Reviewer { + t.Errorf("Reviewer not equals 'Reviewer' != '%s'", build.Reviewer) + } else if now > build.Reviewed { + t.Errorf("Reviewed not updated %d !< %d", now, build.Reviewed) + } +} + +func TestUpdateToStatusToDone(t *testing.T) { + t.Parallel() + + build, _ := UpdateStatusToDone(&mockUpdateBuildStore{}, model.Build{}, "status", int64(1)) + + if "status" != build.Status { + t.Errorf("Build status not equals 'status' != '%s'", build.Status) + } else if int64(1) != build.Finished { + t.Errorf("Build finished not equals 1 != %d", build.Finished) + } +} + +func TestUpdateToStatusError(t *testing.T) { + t.Parallel() + + now := time.Now().Unix() + + build, _ := UpdateToStatusError(&mockUpdateBuildStore{}, model.Build{}, errors.New("error")) + + if "error" != build.Error { + t.Errorf("Build error not equals 'error' != '%s'", build.Error) + } else if model.StatusError != build.Status { + t.Errorf("Build status not equals '%s' != '%s'", model.StatusError, build.Status) + } else if now > build.Started { + t.Errorf("Started not updated %d !< %d", now, build.Started) + } else if build.Started != build.Finished { + t.Errorf("Build started and finished not equals %d != %d", build.Started, build.Finished) + } +} + +func TestUpdateToStatusKilled(t *testing.T) { + t.Parallel() + + now := time.Now().Unix() + + build, _ := UpdateToStatusKilled(&mockUpdateBuildStore{}, model.Build{}) + + if model.StatusKilled != build.Status { + t.Errorf("Build status not equals '%s' != '%s'", model.StatusKilled, build.Status) + } else if now > build.Finished { + t.Errorf("Finished not updated %d !< %d", now, build.Finished) + } +} diff --git a/server/builsStatus.go b/server/builsStatus.go new file mode 100644 index 000000000..b03b91736 --- /dev/null +++ b/server/builsStatus.go @@ -0,0 +1,65 @@ +// Copyright 2019 mhmxs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "time" + + "github.com/laszlocph/woodpecker/model" +) + +type UpdateBuildStore interface { + UpdateBuild(*model.Build) error +} + +func UpdateToStatusRunning(store UpdateBuildStore, build model.Build, started int64) (*model.Build, error) { + build.Status = model.StatusRunning + build.Started = started + return &build, store.UpdateBuild(&build) +} + +func UpdateToStatusPending(store UpdateBuildStore, build model.Build, reviewer string) (*model.Build, error) { + build.Reviewer = reviewer + build.Status = model.StatusPending + build.Reviewed = time.Now().Unix() + return &build, store.UpdateBuild(&build) +} + +func UpdateToStatusDeclined(store UpdateBuildStore, build model.Build, reviewer string) (*model.Build, error) { + build.Reviewer = reviewer + build.Status = model.StatusDeclined + build.Reviewed = time.Now().Unix() + return &build, store.UpdateBuild(&build) +} + +func UpdateStatusToDone(store UpdateBuildStore, build model.Build, status string, stopped int64) (*model.Build, error) { + build.Status = status + build.Finished = stopped + return &build, store.UpdateBuild(&build) +} + +func UpdateToStatusError(store UpdateBuildStore, build model.Build, err error) (*model.Build, error) { + build.Error = err.Error() + build.Status = model.StatusError + build.Started = time.Now().Unix() + build.Finished = build.Started + return &build, store.UpdateBuild(&build) +} + +func UpdateToStatusKilled(store UpdateBuildStore, build model.Build) (*model.Build, error) { + build.Status = model.StatusKilled + build.Finished = time.Now().Unix() + return &build, store.UpdateBuild(&build) +} diff --git a/server/hook.go b/server/hook.go index da3df0dbc..fdbe8f8dd 100644 --- a/server/hook.go +++ b/server/hook.go @@ -259,11 +259,9 @@ func PostHook(c *gin.Context) { } buildItems, err := b.Build() if err != nil { - build.Status = model.StatusError - build.Started = time.Now().Unix() - build.Finished = build.Started - build.Error = err.Error() - store.UpdateBuild(c, build) + if _, err = UpdateToStatusError(store.FromContext(c), *build, err); err != nil { + logrus.Errorf("Error setting error status of build for %s#%d. %s", repo.FullName, build.Number, err) + } return } build = setBuildStepsOnBuild(b.Curr, buildItems) diff --git a/server/rpc.go b/server/rpc.go index 34b301d2b..8126a4501 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -326,9 +326,7 @@ func (s *RPC) Init(c context.Context, id string, state rpc.State) error { } if build.Status == model.StatusPending { - build.Status = model.StatusRunning - build.Started = state.Started - if err := s.store.UpdateBuild(build); err != nil { + if build, err = UpdateToStatusRunning(s.store, *build, state.Started); err != nil { log.Printf("error: init: cannot update build_id %d state: %s", build.ID, err) } } @@ -394,9 +392,7 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { s.completeChildrenIfParentCompleted(procs, proc) if !isThereRunningStage(procs) { - build.Status = buildStatus(procs) - build.Finished = proc.Stopped - if err := s.store.UpdateBuild(build); err != nil { + if build, err = UpdateStatusToDone(s.store, *build, buildStatus(procs), proc.Stopped); err != nil { log.Printf("error: done: cannot update build_id %d final state: %s", build.ID, err) } diff --git a/store/store.go b/store/store.go index eca28fcc0..52cd13dcb 100644 --- a/store/store.go +++ b/store/store.go @@ -249,7 +249,3 @@ func GetBuildQueue(c context.Context) ([]*model.Feed, error) { func CreateBuild(c context.Context, build *model.Build, procs ...*model.Proc) error { return FromContext(c).CreateBuild(build, procs...) } - -func UpdateBuild(c context.Context, build *model.Build) error { - return FromContext(c).UpdateBuild(build) -}