Merge pull request #69 from mhmxs/status-update

Status update
This commit is contained in:
Laszlo Fogas 2019-09-16 15:17:01 +02:00 committed by GitHub
commit 6bae1b6c83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 427 additions and 181 deletions

View file

@ -15,7 +15,7 @@ pipeline:
- go get -u golang.org/x/net/context/ctxhttp
- go get -u github.com/golang/protobuf/proto
- go get -u github.com/golang/protobuf/protoc-gen-go
- go test -cover $(go list ./... | grep -v /vendor/)
- go test -cover -timeout 30s $(go list ./... | grep -v /vendor/)
test_postgres:
image: golang:1.12.4
@ -23,7 +23,7 @@ pipeline:
- DATABASE_DRIVER=postgres
- DATABASE_CONFIG=host=postgres user=postgres dbname=postgres sslmode=disable
commands:
- go test github.com/laszlocph/woodpecker/store/datastore
- go test -timeout 30s github.com/laszlocph/woodpecker/store/datastore
test_mysql:
image: golang:1.12.4
@ -31,7 +31,7 @@ pipeline:
- DATABASE_DRIVER=mysql
- DATABASE_CONFIG=root@tcp(mysql:3306)/test?parseTime=true
commands:
- go test github.com/laszlocph/woodpecker/store/datastore
- go test -timeout 30s github.com/laszlocph/woodpecker/store/datastore
build:
image: golang:1.12.4

View file

@ -1,15 +1,6 @@
1. Install go 1.9 or later
2. Install dependencies
go get -u golang.org/x/net/context
go get -u golang.org/x/net/context/ctxhttp
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
3. Install binaries to $GOPATH/bin
go install github.com/laszlocph/woodpecker/cmd/drone-agent
go install github.com/laszlocph/woodpecker/cmd/drone-server
2. Execute `make deps` to download dependencies
3. Execute `make install` to compile project and install binaries to `GOPATH`
---

46
Makefile Normal file
View file

@ -0,0 +1,46 @@
export GO111MODULE=off
GOFILES_NOVENDOR = $(shell find . -type f -name '*.go' -not -path "./vendor/*" -not -path "./.git/*")
all: deps build
deps:
go get -u golang.org/x/net/context
go get -u golang.org/x/net/context/ctxhttp
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
formatcheck:
([ -z "$(shell gofmt -d $(GOFILES_NOVENDOR))" ]) || (echo "Source is unformatted"; exit 1)
format:
@gofmt -w ${GOFILES_NOVENDOR}
test-agent:
go test -timeout 30s github.com/laszlocph/woodpecker/cmd/drone-agent $(go list ./... | grep -v /vendor/)
test-server:
ifneq ($(shell uname), "Linux")
$(error Target OS is not Linux drone-server build skipped)
endif
go test -timeout 30s github.com/laszlocph/woodpecker/cmd/drone-server
test-lib:
go test -timeout 30s $(shell go list ./... | grep -v '/cmd/')
test: test-lib test-agent test-server
build-agent:
go build -o build/drone-agent github.com/laszlocph/woodpecker/cmd/drone-agent
build-server:
ifneq ($(shell uname), "Linux")
$(error Target OS is not Linux drone-server build skipped)
endif
go build -o build/drone-server github.com/laszlocph/woodpecker/cmd/drone-server
build: build-agent build-server
install:
go install github.com/laszlocph/woodpecker/cmd/drone-agent
go install github.com/laszlocph/woodpecker/cmd/drone-server

2
build/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
drone-agent
drone-server

View file

@ -11,6 +11,7 @@ import (
"strings"
"time"
"github.com/drone/envsubst"
"github.com/laszlocph/woodpecker/cncd/pipeline/pipeline"
"github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/backend/docker"
@ -20,7 +21,6 @@ import (
"github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/frontend/yaml/linter"
"github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/interrupt"
"github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/multipart"
"github.com/drone/envsubst"
"github.com/urfave/cli"
)
@ -275,7 +275,7 @@ var Command = cli.Command{
EnvVar: "DRONE_JOB_NUMBER",
},
cli.StringSliceFlag{
Name: "env, e",
Name: "env, e",
EnvVar: "DRONE_ENV",
},
},

View file

@ -12,4 +12,4 @@ pipeline:
test:
image: golang:1.8
commands:
- go test -cover github.com/laszlocph/woodpecker/cncd/pipeline/...
- go test -cover -timeout 30s github.com/laszlocph/woodpecker/cncd/pipeline/...

View file

@ -3,9 +3,9 @@ package yaml
import (
"path/filepath"
libcompose "github.com/docker/libcompose/yaml"
"github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/frontend"
"github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/frontend/yaml/types"
libcompose "github.com/docker/libcompose/yaml"
)
type (

View file

@ -25,4 +25,4 @@ func (b *BoolTrue) UnmarshalYAML(unmarshal func(interface{}) error) error {
// Bool returns the bool value.
func (b BoolTrue) Bool() bool {
return !b.value
}
}

View file

@ -3,8 +3,8 @@ package rpc
import (
"context"
"encoding/json"
"time"
"log"
"time"
"github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/rpc/proto"

View file

@ -6,6 +6,6 @@ import (
// Health defines a health-check connection.
type Health interface {
// Check returns if server is healthy or not
// Check returns if server is healthy or not
Check(c context.Context) (bool, error)
}

View file

@ -17,9 +17,9 @@ package bitbucketserver
import (
"testing"
"github.com/franela/goblin"
"github.com/laszlocph/woodpecker/model"
"github.com/laszlocph/woodpecker/remote/bitbucketserver/internal"
"github.com/franela/goblin"
"github.com/mrjones/oauth"
)

View file

@ -19,9 +19,9 @@ import (
"testing"
"code.gitea.io/sdk/gitea"
"github.com/franela/goblin"
"github.com/laszlocph/woodpecker/model"
"github.com/laszlocph/woodpecker/remote/gitea/fixtures"
"github.com/franela/goblin"
)
func Test_parse(t *testing.T) {

View file

@ -69,13 +69,13 @@ type pullRequestHook struct {
Email string `json:"email"`
Avatar string `json:"avatar_url"`
} `json:"user"`
Title string `json:"title"`
Body string `json:"body"`
State string `json:"state"`
URL string `json:"html_url"`
Mergeable bool `json:"mergeable"`
Merged bool `json:"merged"`
MergeBase string `json:"merge_base"`
Title string `json:"title"`
Body string `json:"body"`
State string `json:"state"`
URL string `json:"html_url"`
Mergeable bool `json:"mergeable"`
Merged bool `json:"merged"`
MergeBase string `json:"merge_base"`
Base struct {
Label string `json:"label"`
Ref string `json:"ref"`

View file

@ -17,8 +17,8 @@ package github
import (
"testing"
"github.com/laszlocph/woodpecker/model"
"github.com/google/go-github/github"
"github.com/laszlocph/woodpecker/model"
"github.com/franela/goblin"
)

View file

@ -19,9 +19,9 @@ import (
"net/http"
"testing"
"github.com/franela/goblin"
"github.com/laszlocph/woodpecker/model"
"github.com/laszlocph/woodpecker/remote/github/fixtures"
"github.com/franela/goblin"
)
func Test_parser(t *testing.T) {

View file

@ -19,9 +19,9 @@ import (
"net/http"
"testing"
"github.com/franela/goblin"
"github.com/laszlocph/woodpecker/model"
"github.com/laszlocph/woodpecker/remote/gitlab/testdata"
"github.com/franela/goblin"
)
func Test_Gitlab(t *testing.T) {

View file

@ -1,11 +1,11 @@
// Copyright 2018 Drone.IO Inc.
//
//
// 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.

View file

@ -1,11 +1,11 @@
// Copyright 2018 Drone.IO Inc.
//
//
// 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.

View file

@ -1,11 +1,11 @@
// Copyright 2018 Drone.IO Inc.
//
//
// 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.

View file

@ -1,11 +1,11 @@
// Copyright 2018 Drone.IO Inc.
//
//
// 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.

View file

@ -1,11 +1,11 @@
// Copyright 2018 Drone.IO Inc.
//
//
// 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.

View file

@ -19,9 +19,9 @@ import (
"net/http"
"testing"
"github.com/franela/goblin"
"github.com/laszlocph/woodpecker/model"
"github.com/laszlocph/woodpecker/remote/gitlab3/testdata"
"github.com/franela/goblin"
)
func Test_Gitlab(t *testing.T) {

View file

@ -1,11 +1,11 @@
// Copyright 2018 Drone.IO Inc.
//
//
// 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.

View file

@ -1,11 +1,11 @@
// Copyright 2018 Drone.IO Inc.
//
//
// 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.

View file

@ -1,11 +1,11 @@
// Copyright 2018 Drone.IO Inc.
//
//
// 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.

View file

@ -1,11 +1,11 @@
// Copyright 2018 Drone.IO Inc.
//
//
// 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.

View file

@ -1,11 +1,11 @@
// Copyright 2018 Drone.IO Inc.
//
//
// 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.

View file

@ -22,8 +22,8 @@ import (
"strings"
"time"
"github.com/laszlocph/woodpecker/model"
"github.com/gogits/go-gogs-client"
"github.com/laszlocph/woodpecker/model"
)
// helper function that converts a Gogs repository to a Drone repository.

View file

@ -68,14 +68,14 @@ type pullRequestHook struct {
Email string `json:"email"`
Avatar string `json:"avatar_url"`
} `json:"user"`
Title string `json:"title"`
Body string `json:"body"`
State string `json:"state"`
URL string `json:"html_url"`
Mergeable bool `json:"mergeable"`
Merged bool `json:"merged"`
MergeBase string `json:"merge_base"`
BaseBranch string `json:"base_branch"`
Title string `json:"title"`
Body string `json:"body"`
State string `json:"state"`
URL string `json:"html_url"`
Mergeable bool `json:"mergeable"`
Merged bool `json:"merged"`
MergeBase string `json:"merge_base"`
BaseBranch string `json:"base_branch"`
Base struct {
Label string `json:"label"`
Ref string `json:"ref"`

View file

@ -18,8 +18,8 @@ import (
"net/http"
"time"
"github.com/laszlocph/woodpecker/version"
"github.com/gin-gonic/gin"
"github.com/laszlocph/woodpecker/version"
)
// NoCache is a middleware function that appends headers

View file

@ -15,8 +15,8 @@
package middleware
import (
"github.com/laszlocph/woodpecker/remote"
"github.com/gin-gonic/gin"
"github.com/laszlocph/woodpecker/remote"
)
// Remote is a middleware function that initializes the Remote and attaches to

View file

@ -15,8 +15,8 @@
package session
import (
"github.com/laszlocph/woodpecker/shared/token"
"github.com/gin-gonic/gin"
"github.com/laszlocph/woodpecker/shared/token"
)
// AuthorizeAgent authorizes requsts from build agents to access the queue.

View file

@ -15,8 +15,8 @@
package middleware
import (
"github.com/laszlocph/woodpecker/version"
"github.com/gin-gonic/gin"
"github.com/laszlocph/woodpecker/version"
)
// Version is a middleware function that appends the Drone version information

View file

@ -19,6 +19,7 @@ import (
"context"
"fmt"
"io"
"log"
"net/http"
"strconv"
"time"
@ -185,14 +186,10 @@ func DeleteBuild(c *gin.Context) {
continue
}
proc.State = model.StatusKilled
proc.Stopped = time.Now().Unix()
if proc.Started == 0 {
proc.Started = proc.Stopped
}
proc.ExitCode = 137
// TODO cancel child procs
store.FromContext(c).ProcUpdate(proc)
if _, err = UpdateProcToStatusKilled(store.FromContext(c), *proc); err != nil {
log.Printf("error: done: cannot update proc_id %d state: %s", proc.ID, err)
}
Config.Services.Queue.Error(context.Background(), fmt.Sprint(proc.ID), queue.ErrCancel)
cancelled = true
@ -235,23 +232,19 @@ func ZombieKill(c *gin.Context) {
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
if _, err := UpdateProcToStatusKilled(store.FromContext(c), *proc); err != nil {
log.Printf("error: done: cannot update proc_id %d state: %s", proc.ID, err)
}
} else {
store.FromContext(c).ProcUpdate(proc)
}
}
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)
if _, err := UpdateToStatusKilled(store.FromContext(c), *build); err != nil {
c.AbortWithError(500, err)
return
}
c.String(204, "")
}
@ -275,9 +268,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 +279,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 +327,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 +376,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
}

120
server/buildStatus_test.go Normal file
View file

@ -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)
}
}

65
server/builsStatus.go Normal file
View file

@ -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)
}

View file

@ -20,9 +20,9 @@ import (
"strconv"
"strings"
"github.com/gin-gonic/gin"
"github.com/laszlocph/woodpecker/router/middleware/session"
"github.com/laszlocph/woodpecker/store"
"github.com/gin-gonic/gin"
)
// FileList gets a list file by build.

View file

@ -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)

View file

@ -18,8 +18,8 @@ import (
"errors"
"fmt"
"github.com/laszlocph/woodpecker/server"
"github.com/gin-gonic/gin"
"github.com/laszlocph/woodpecker/server"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

89
server/procStatus.go Normal file
View file

@ -0,0 +1,89 @@
// 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/cncd/pipeline/pipeline/rpc"
"github.com/laszlocph/woodpecker/model"
)
type UpdateProcStore interface {
ProcUpdate(*model.Proc) error
}
func UpdateProcStatus(store UpdateProcStore, proc model.Proc, state rpc.State, started int64) (*model.Proc, error) {
if state.Exited {
proc.Stopped = state.Finished
proc.ExitCode = state.ExitCode
proc.Error = state.Error
proc.State = model.StatusSuccess
if state.ExitCode != 0 || state.Error != "" {
proc.State = model.StatusFailure
}
if state.ExitCode == 137 {
proc.State = model.StatusKilled
}
} else {
proc.Started = state.Started
proc.State = model.StatusRunning
}
if proc.Started == 0 && proc.Stopped != 0 {
proc.Started = started
}
return &proc, store.ProcUpdate(&proc)
}
func UpdateProcToStatusStarted(store UpdateProcStore, proc model.Proc, state rpc.State) (*model.Proc, error) {
proc.Started = state.Started
proc.State = model.StatusRunning
return &proc, store.ProcUpdate(&proc)
}
func UpdateProcToStatusSkipped(store UpdateProcStore, proc model.Proc, stopped int64) (*model.Proc, error) {
proc.State = model.StatusSkipped
if proc.Started != 0 {
proc.State = model.StatusSuccess // for deamons that are killed
proc.Stopped = stopped
}
return &proc, store.ProcUpdate(&proc)
}
func UpdateProcStatusToDone(store UpdateProcStore, proc model.Proc, state rpc.State) (*model.Proc, error) {
proc.Stopped = state.Finished
proc.Error = state.Error
proc.ExitCode = state.ExitCode
if state.Started == 0 {
proc.State = model.StatusSkipped
} else {
proc.State = model.StatusSuccess
}
if proc.ExitCode != 0 || proc.Error != "" {
proc.State = model.StatusFailure
}
return &proc, store.ProcUpdate(&proc)
}
func UpdateProcToStatusKilled(store UpdateProcStore, proc model.Proc) (*model.Proc, error) {
proc.State = model.StatusKilled
proc.Stopped = time.Now().Unix()
if proc.Started == 0 {
proc.Started = proc.Stopped
}
proc.ExitCode = 137
return &proc, store.ProcUpdate(&proc)
}

View file

@ -179,27 +179,7 @@ func (s *RPC) Update(c context.Context, id string, state rpc.State) error {
return err
}
if state.Exited {
proc.Stopped = state.Finished
proc.ExitCode = state.ExitCode
proc.Error = state.Error
proc.State = model.StatusSuccess
if state.ExitCode != 0 || state.Error != "" {
proc.State = model.StatusFailure
}
if state.ExitCode == 137 {
proc.State = model.StatusKilled
}
} else {
proc.Started = state.Started
proc.State = model.StatusRunning
}
if proc.Started == 0 && proc.Stopped != 0 {
proc.Started = build.Started
}
if err := s.store.ProcUpdate(proc); err != nil {
if proc, err = UpdateProcStatus(s.store, *proc, state, build.Started); err != nil {
log.Printf("error: rpc.update: cannot update proc: %s", err)
}
@ -326,9 +306,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)
}
}
@ -348,9 +326,8 @@ func (s *RPC) Init(c context.Context, id string, state rpc.State) error {
s.pubsub.Publish(c, "topic/events", message)
}()
proc.Started = state.Started
proc.State = model.StatusRunning
return s.store.ProcUpdate(proc)
_, err = UpdateProcToStatusStarted(s.store, *proc, state)
return err
}
// Done implements the rpc.Done function
@ -378,7 +355,9 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error {
return err
}
s.updateProcState(proc, state)
if proc, err = UpdateProcStatusToDone(s.store, *proc, state); err != nil {
log.Printf("error: done: cannot update proc_id %d state: %s", proc.ID, err)
}
var queueErr error
if proc.Failing() {
@ -394,9 +373,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)
}
@ -444,32 +421,10 @@ func (s *RPC) Log(c context.Context, id string, line *rpc.Line) error {
return nil
}
func (s *RPC) updateProcState(proc *model.Proc, state rpc.State) {
proc.Stopped = state.Finished
proc.Error = state.Error
proc.ExitCode = state.ExitCode
if state.Started == 0 {
proc.State = model.StatusSkipped
} else {
proc.State = model.StatusSuccess
}
if proc.ExitCode != 0 || proc.Error != "" {
proc.State = model.StatusFailure
}
if err := s.store.ProcUpdate(proc); err != nil {
log.Printf("error: done: cannot update proc_id %d state: %s", proc.ID, err)
}
}
func (s *RPC) completeChildrenIfParentCompleted(procs []*model.Proc, completedProc *model.Proc) {
for _, p := range procs {
if p.Running() && p.PPID == completedProc.PID {
p.State = model.StatusSkipped
if p.Started != 0 {
p.State = model.StatusSuccess // for deamons that are killed
p.Stopped = completedProc.Stopped
}
if err := s.store.ProcUpdate(p); err != nil {
if _, err := UpdateProcToStatusSkipped(s.store, *p, completedProc.Stopped); err != nil {
log.Printf("error: done: cannot update proc_id %d child state: %s", p.ID, err)
}
}

View file

@ -24,10 +24,10 @@ import (
"path/filepath"
"time"
"github.com/laszlocph/woodpecker-ui/dist"
"github.com/laszlocph/woodpecker/model"
"github.com/laszlocph/woodpecker/shared/token"
"github.com/laszlocph/woodpecker/version"
"github.com/laszlocph/woodpecker-ui/dist"
"github.com/dimfeld/httptreemux"
)

View file

@ -15,9 +15,9 @@
package server
import (
"github.com/gin-gonic/gin"
"github.com/laszlocph/woodpecker/store"
"github.com/laszlocph/woodpecker/version"
"github.com/gin-gonic/gin"
)
// Health endpoint returns a 500 if the server state is unhealthy.

View file

@ -17,8 +17,8 @@ package datastore
import (
"testing"
"github.com/laszlocph/woodpecker/model"
"github.com/franela/goblin"
"github.com/laszlocph/woodpecker/model"
)
func TestRepos(t *testing.T) {

View file

@ -17,8 +17,8 @@ package datastore
import (
"testing"
"github.com/laszlocph/woodpecker/model"
"github.com/franela/goblin"
"github.com/laszlocph/woodpecker/model"
)
func TestUsers(t *testing.T) {

View file

@ -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)
}