Extend Logging & Report to WebHook Caller back if pulls are disabled (#369)

* Add more logging 
* Format Code
* Add TODOs
* Fix nits
* Delete two unused functions
* Report to WebHook Caller back if pulls are disabled
This commit is contained in:
6543 2021-09-27 23:32:08 +02:00 committed by GitHub
parent a94807efeb
commit f81bd8c656
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 67 additions and 64 deletions

View file

@ -54,7 +54,9 @@ import (
func loop(c *cli.Context) error { func loop(c *cli.Context) error {
// debug level if requested by user // debug level if requested by user
// TODO: format output & options to switch to json aka. option to add channels to send logs to
if c.Bool("debug") { if c.Bool("debug") {
logrus.SetReportCaller(true)
logrus.SetLevel(logrus.DebugLevel) logrus.SetLevel(logrus.DebugLevel)
} else { } else {
logrus.SetLevel(logrus.WarnLevel) logrus.SetLevel(logrus.WarnLevel)

View file

@ -79,7 +79,7 @@ func BlockTilQueueHasRunningItem(c *gin.Context) {
func PostHook(c *gin.Context) { func PostHook(c *gin.Context) {
remote_ := remote.FromContext(c) remote_ := remote.FromContext(c)
tmprepo, build, err := remote_.Hook(c.Request) tmpRepo, build, err := remote_.Hook(c.Request)
if err != nil { if err != nil {
logrus.Errorf("failure to parse hook. %s", err) logrus.Errorf("failure to parse hook. %s", err)
c.AbortWithError(400, err) c.AbortWithError(400, err)
@ -89,7 +89,7 @@ func PostHook(c *gin.Context) {
c.Writer.WriteHeader(200) c.Writer.WriteHeader(200)
return return
} }
if tmprepo == nil { if tmpRepo == nil {
logrus.Errorf("failure to ascertain repo from hook.") logrus.Errorf("failure to ascertain repo from hook.")
c.Writer.WriteHeader(400) c.Writer.WriteHeader(400)
return return
@ -104,14 +104,14 @@ func PostHook(c *gin.Context) {
return return
} }
repo, err := store.GetRepoOwnerName(c, tmprepo.Owner, tmprepo.Name) repo, err := store.GetRepoOwnerName(c, tmpRepo.Owner, tmpRepo.Name)
if err != nil { if err != nil {
logrus.Errorf("failure to find repo %s/%s from hook. %s", tmprepo.Owner, tmprepo.Name, err) logrus.Errorf("failure to find repo %s/%s from hook. %s", tmpRepo.Owner, tmpRepo.Name, err)
c.AbortWithError(404, err) c.AbortWithError(404, err)
return return
} }
if !repo.IsActive { if !repo.IsActive {
logrus.Errorf("ignoring hook. %s/%s is inactive.", tmprepo.Owner, tmprepo.Name) logrus.Errorf("ignoring hook. %s/%s is inactive.", tmpRepo.Owner, tmpRepo.Name)
c.AbortWithError(204, err) c.AbortWithError(204, err)
return return
} }
@ -139,6 +139,7 @@ func PostHook(c *gin.Context) {
if build.Event == model.EventPull && !repo.AllowPull { if build.Event == model.EventPull && !repo.AllowPull {
logrus.Infof("ignoring hook. repo %s is disabled for pull requests.", repo.FullName) logrus.Infof("ignoring hook. repo %s is disabled for pull requests.", repo.FullName)
c.Writer.Write([]byte("pulls are disabled on woodpecker for this repo"))
c.Writer.WriteHeader(204) c.Writer.WriteHeader(204)
return return
} }
@ -154,9 +155,14 @@ func PostHook(c *gin.Context) {
// may be stale. Therefore, we should refresh prior to dispatching // may be stale. Therefore, we should refresh prior to dispatching
// the build. // the build.
if refresher, ok := remote_.(remote.Refresher); ok { if refresher, ok := remote_.(remote.Refresher); ok {
ok, _ := refresher.Refresh(user) ok, err := refresher.Refresh(user)
if ok { if err != nil {
store.UpdateUser(c, user) logrus.Errorf("failed to refresh oauth2 token: %s", err)
} else if ok {
if err := store.UpdateUser(c, user); err != nil {
logrus.Errorf("error while updating user: %s", err)
// move forward
}
} }
} }
@ -286,12 +292,16 @@ func PostHook(c *gin.Context) {
queueBuild(build, repo, buildItems) queueBuild(build, repo, buildItems)
} }
// TODO: parse yaml once and not for each filter function
func branchFiltered(build *model.Build, remoteYamlConfigs []*remote.FileMeta) (bool, error) { func branchFiltered(build *model.Build, remoteYamlConfigs []*remote.FileMeta) (bool, error) {
logrus.Tracef("hook.branchFiltered(): build branch: '%s' build event: '%s' config count: %d", build.Branch, build.Event, len(remoteYamlConfigs))
for _, remoteYamlConfig := range remoteYamlConfigs { for _, remoteYamlConfig := range remoteYamlConfigs {
parsedPipelineConfig, err := yaml.ParseString(string(remoteYamlConfig.Data)) parsedPipelineConfig, err := yaml.ParseString(string(remoteYamlConfig.Data))
if err != nil { if err != nil {
logrus.Tracef("parse config '%s': %s", remoteYamlConfig.Name, err)
return false, err return false, err
} }
logrus.Tracef("config '%s': %#v", remoteYamlConfig.Name, parsedPipelineConfig)
if !parsedPipelineConfig.Branches.Match(build.Branch) && build.Event != model.EventTag && build.Event != model.EventDeploy { if !parsedPipelineConfig.Branches.Match(build.Branch) && build.Event != model.EventTag && build.Event != model.EventDeploy {
} else { } else {

View file

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

View file

@ -21,6 +21,8 @@ const (
EventDeploy = "deployment" EventDeploy = "deployment"
) )
// TODO: type StatusValue string
const ( const (
StatusSkipped = "skipped" StatusSkipped = "skipped"
StatusPending = "pending" StatusPending = "pending"

View file

@ -19,13 +19,6 @@ import (
"strings" "strings"
) )
type RepoLite struct {
Owner string `json:"owner"`
Name string `json:"name"`
FullName string `json:"full_name"`
Avatar string `json:"avatar_url"`
}
// Repo represents a repository. // Repo represents a repository.
// //
// swagger:model repo // swagger:model repo

View file

@ -289,15 +289,6 @@ var (
Name: "not_found_project", Name: "not_found_project",
} }
fakeRepos = []*model.RepoLite{
&model.RepoLite{
Owner: "demo1",
Name: "test1",
FullName: "demo1/test1",
Avatar: "/static/project_icon/scenery-5.png",
},
}
fakeBuild = &model.Build{ fakeBuild = &model.Build{
Commit: "4504a072cc", Commit: "4504a072cc",
} }

View file

@ -24,6 +24,10 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
) )
// TODO: use pagination
// TODO: use context
// TODO: add Driver() who return source forge back
type Remote interface { type Remote interface {
// Login authenticates the session and returns the // Login authenticates the session and returns the
// remote user details. // remote user details.

View file

@ -38,18 +38,6 @@ func Repo(c *gin.Context) *model.Repo {
return r return r
} }
func Repos(c *gin.Context) []*model.RepoLite {
v, ok := c.Get("repos")
if !ok {
return nil
}
r, ok := v.([]*model.RepoLite)
if !ok {
return nil
}
return r
}
func SetRepo() gin.HandlerFunc { func SetRepo() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
var ( var (
@ -93,7 +81,6 @@ func Perm(c *gin.Context) *model.Perm {
} }
func SetPerm() gin.HandlerFunc { func SetPerm() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
user := User(c) user := User(c)
repo := Repo(c) repo := Repo(c)

View file

@ -36,18 +36,6 @@ func User(c *gin.Context) *model.User {
return u return u
} }
func Token(c *gin.Context) *token.Token {
v, ok := c.Get("token")
if !ok {
return nil
}
u, ok := v.(*token.Token)
if !ok {
return nil
}
return u
}
func SetUser() gin.HandlerFunc { func SetUser() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
var user *model.User var user *model.User

View file

@ -17,8 +17,6 @@ package router
import ( import (
"net/http" "net/http"
"github.com/gin-gonic/gin"
"github.com/woodpecker-ci/woodpecker/server/api" "github.com/woodpecker-ci/woodpecker/server/api"
"github.com/woodpecker-ci/woodpecker/server/api/debug" "github.com/woodpecker-ci/woodpecker/server/api/debug"
"github.com/woodpecker-ci/woodpecker/server/api/metrics" "github.com/woodpecker-ci/woodpecker/server/api/metrics"
@ -26,6 +24,9 @@ import (
"github.com/woodpecker-ci/woodpecker/server/router/middleware/session" "github.com/woodpecker-ci/woodpecker/server/router/middleware/session"
"github.com/woodpecker-ci/woodpecker/server/router/middleware/token" "github.com/woodpecker-ci/woodpecker/server/router/middleware/token"
"github.com/woodpecker-ci/woodpecker/server/web" "github.com/woodpecker-ci/woodpecker/server/web"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
) )
// Load loads the router // Load loads the router
@ -34,6 +35,11 @@ func Load(serveHTTP func(w http.ResponseWriter, r *http.Request), middleware ...
e := gin.New() e := gin.New()
e.Use(gin.Recovery()) e.Use(gin.Recovery())
e.Use(func(c *gin.Context) {
logrus.Tracef("[%s] %s", c.Request.Method, c.Request.URL.String())
c.Next()
})
e.Use(header.NoCache) e.Use(header.NoCache)
e.Use(header.Options) e.Use(header.Options)
e.Use(header.Secure) e.Use(header.Secure)

View file

@ -7,6 +7,8 @@ import (
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
"github.com/woodpecker-ci/woodpecker/server/remote" "github.com/woodpecker-ci/woodpecker/server/remote"
"github.com/sirupsen/logrus"
) )
type configFetcher struct { type configFetcher struct {
@ -25,10 +27,14 @@ func NewConfigFetcher(remote remote.Remote, user *model.User, repo *model.Repo,
} }
} }
// Fetch
// TODO: dedupe code
func (cf *configFetcher) Fetch() (files []*remote.FileMeta, err error) { func (cf *configFetcher) Fetch() (files []*remote.FileMeta, err error) {
var file []byte var file []byte
config := strings.TrimSpace(cf.repo.Config) config := strings.TrimSpace(cf.repo.Config)
logrus.Tracef("Start Fetching config for '%s'", cf.repo.FullName)
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
select { select {
case <-time.After(time.Second * time.Duration(i)): case <-time.After(time.Second * time.Duration(i)):
@ -37,6 +43,7 @@ func (cf *configFetcher) Fetch() (files []*remote.FileMeta, err error) {
if !strings.HasSuffix(config, "/") { if !strings.HasSuffix(config, "/") {
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config) file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config)
if err == nil && len(file) != 0 { if err == nil && len(file) != 0 {
logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config)
return []*remote.FileMeta{{ return []*remote.FileMeta{{
Name: config, Name: config,
Data: file, Data: file,
@ -47,29 +54,37 @@ func (cf *configFetcher) Fetch() (files []*remote.FileMeta, err error) {
// or a folder // or a folder
files, err = cf.remote_.Dir(cf.user, cf.repo, cf.build, strings.TrimSuffix(config, "/")) files, err = cf.remote_.Dir(cf.user, cf.repo, cf.build, strings.TrimSuffix(config, "/"))
if err == nil { if err == nil {
logrus.Tracef("ConfigFetch[%s]: found %d files in '%s'", cf.repo.FullName, len(files), config)
return filterPipelineFiles(files), nil return filterPipelineFiles(files), nil
} }
} else { } else {
logrus.Tracef("ConfigFetch[%s]: user did not defined own config follow default procedure", cf.repo.FullName)
// no user defined config so try .woodpecker/*.yml -> .woodpecker.yml -> .drone.yml // no user defined config so try .woodpecker/*.yml -> .woodpecker.yml -> .drone.yml
// test .woodpecker/ folder // test .woodpecker/ folder
// if folder is not supported we will get a "Not implemented" error and continue // if folder is not supported we will get a "Not implemented" error and continue
files, err = cf.remote_.Dir(cf.user, cf.repo, cf.build, ".woodpecker") config = ".woodpecker"
files, err = cf.remote_.Dir(cf.user, cf.repo, cf.build, config)
logrus.Tracef("ConfigFetch[%s]: found %d files in '%s'", cf.repo.FullName, len(files), config)
files = filterPipelineFiles(files) files = filterPipelineFiles(files)
if err == nil && len(files) != 0 { if err == nil && len(files) != 0 {
return files, nil return files, nil
} }
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, ".woodpecker.yml") config = ".woodpecker.yml"
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config)
if err == nil && len(file) != 0 { if err == nil && len(file) != 0 {
logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config)
return []*remote.FileMeta{{ return []*remote.FileMeta{{
Name: ".woodpecker.yml", Name: ".woodpecker.yml",
Data: file, Data: file,
}}, nil }}, nil
} }
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, ".drone.yml") config = ".drone.yml"
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config)
if err == nil && len(file) != 0 { if err == nil && len(file) != 0 {
logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config)
return []*remote.FileMeta{{ return []*remote.FileMeta{{
Name: ".drone.yml", Name: ".drone.yml",
Data: file, Data: file,

View file

@ -19,6 +19,7 @@ import (
"net/http" "net/http"
"github.com/golang-jwt/jwt" "github.com/golang-jwt/jwt"
"github.com/sirupsen/logrus"
) )
type SecretFunc func(*Token) (string, error) type SecretFunc func(*Token) (string, error)
@ -31,7 +32,7 @@ const (
AgentToken = "agent" AgentToken = "agent"
) )
// Default algorithm used to sign JWT tokens. // SignerAlgo id default algorithm used to sign JWT tokens.
const SignerAlgo = "HS256" const SignerAlgo = "HS256"
type Token struct { type Token struct {
@ -39,7 +40,7 @@ type Token struct {
Text string Text string
} }
func Parse(raw string, fn SecretFunc) (*Token, error) { func parse(raw string, fn SecretFunc) (*Token, error) {
token := &Token{} token := &Token{}
parsed, err := jwt.Parse(raw, keyFunc(token, fn)) parsed, err := jwt.Parse(raw, keyFunc(token, fn))
if err != nil { if err != nil {
@ -51,21 +52,24 @@ func Parse(raw string, fn SecretFunc) (*Token, error) {
} }
func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) { func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) {
var token = r.Header.Get("Authorization") token := r.Header.Get("Authorization")
// first we attempt to get the token from the // first we attempt to get the token from the
// authorization header. // authorization header.
if len(token) != 0 { if len(token) != 0 {
token = r.Header.Get("Authorization") logrus.Tracef("token.ParseRequest: found token in header: %s", token)
fmt.Sscanf(token, "Bearer %s", &token) bearer := token
return Parse(token, fn) if _, err := fmt.Sscanf(token, "Bearer %s", &bearer); err != nil {
return nil, err
}
return parse(bearer, fn)
} }
// then we attempt to get the token from the // then we attempt to get the token from the
// access_token url query parameter // access_token url query parameter
token = r.FormValue("access_token") token = r.FormValue("access_token")
if len(token) != 0 { if len(token) != 0 {
return Parse(token, fn) return parse(token, fn)
} }
// and finally we attempt to get the token from // and finally we attempt to get the token from
@ -74,7 +78,7 @@ func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return Parse(cookie.Value, fn) return parse(cookie.Value, fn)
} }
func CheckCsrf(r *http.Request, fn SecretFunc) error { func CheckCsrf(r *http.Request, fn SecretFunc) error {
@ -88,7 +92,7 @@ func CheckCsrf(r *http.Request, fn SecretFunc) error {
// parse the raw CSRF token value and validate // parse the raw CSRF token value and validate
raw := r.Header.Get("X-CSRF-TOKEN") raw := r.Header.Get("X-CSRF-TOKEN")
_, err := Parse(raw, fn) _, err := parse(raw, fn)
return err return err
} }