diff --git a/server/api/build.go b/server/api/build.go index fbacdb8e2..848bd6ce0 100644 --- a/server/api/build.go +++ b/server/api/build.go @@ -333,9 +333,9 @@ func PostApproval(c *gin.Context) { for _, item := range buildItems { uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number) if len(buildItems) > 1 { - err = remote_.Status(user, repo, build, uri, item.Proc) + err = remote_.Status(c, user, repo, build, uri, item.Proc) } else { - err = remote_.Status(user, repo, build, uri, nil) + err = remote_.Status(c, user, repo, build, uri, nil) } if err != nil { logrus.Errorf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err) @@ -373,7 +373,7 @@ func PostDecline(c *gin.Context) { } uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number) - err = remote_.Status(user, repo, build, uri, nil) + err = remote_.Status(c, user, repo, build, uri, nil) if err != nil { logrus.Errorf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err) } @@ -426,7 +426,7 @@ func PostBuild(c *gin.Context) { // may be stale. Therefore, we should refresh prior to dispatching // the job. if refresher, ok := remote_.(remote.Refresher); ok { - ok, _ := refresher.Refresh(user) + ok, _ := refresher.Refresh(c, user) if ok { store.UpdateUser(c, user) } diff --git a/server/api/hook.go b/server/api/hook.go index 82e837324..1ed03819f 100644 --- a/server/api/hook.go +++ b/server/api/hook.go @@ -155,7 +155,7 @@ func PostHook(c *gin.Context) { // may be stale. Therefore, we should refresh prior to dispatching // the build. if refresher, ok := remote_.(remote.Refresher); ok { - ok, err := refresher.Refresh(user) + ok, err := refresher.Refresh(c, user) if err != nil { logrus.Errorf("failed to refresh oauth2 token: %s", err) } else if ok { @@ -168,7 +168,7 @@ func PostHook(c *gin.Context) { // fetch the build file from the remote configFetcher := shared.NewConfigFetcher(remote_, user, repo, build) - remoteYamlConfigs, err := configFetcher.Fetch() + remoteYamlConfigs, err := configFetcher.Fetch(c) if err != nil { logrus.Errorf("error: %s: cannot find %s in %s: %s", repo.FullName, repo.Config, build.Ref, err) c.AbortWithError(404, err) @@ -278,9 +278,9 @@ func PostHook(c *gin.Context) { for _, item := range buildItems { uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number) if len(buildItems) > 1 { - err = remote_.Status(user, repo, build, uri, item.Proc) + err = remote_.Status(c, user, repo, build, uri, item.Proc) } else { - err = remote_.Status(user, repo, build, uri, nil) + err = remote_.Status(c, user, repo, build, uri, nil) } if err != nil { logrus.Errorf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err) diff --git a/server/api/repo.go b/server/api/repo.go index 19c036958..f695e5a91 100644 --- a/server/api/repo.go +++ b/server/api/repo.go @@ -75,13 +75,13 @@ func PostRepo(c *gin.Context) { sig, ) - err = r.Activate(user, repo, link) + err = r.Activate(c, user, repo, link) if err != nil { c.String(500, err.Error()) return } - from, err := r.Repo(user, repo.Owner, repo.Name) + from, err := r.Repo(c, user, repo.Owner, repo.Name) if err == nil { repo.Update(from) } @@ -187,7 +187,10 @@ func DeleteRepo(c *gin.Context) { } } - r.Deactivate(user, repo, server.Config.Server.Host) + if err := r.Deactivate(c, user, repo, server.Config.Server.Host); err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } c.JSON(200, repo) } @@ -212,14 +215,14 @@ func RepairRepo(c *gin.Context) { sig, ) - r.Deactivate(user, repo, host) - err = r.Activate(user, repo, link) + _ = r.Deactivate(c, user, repo, host) + err = r.Activate(c, user, repo, link) if err != nil { c.String(500, err.Error()) return } - from, err := r.Repo(user, repo.Owner, repo.Name) + from, err := r.Repo(c, user, repo.Owner, repo.Name) if err == nil { repo.Name = from.Name repo.Owner = from.Owner @@ -255,7 +258,7 @@ func MoveRepo(c *gin.Context) { return } - from, err := r.Repo(user, owner, name) + from, err := r.Repo(c, user, owner, name) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return @@ -298,8 +301,9 @@ func MoveRepo(c *gin.Context) { sig, ) - r.Deactivate(user, repo, host) - err = r.Activate(user, repo, link) + // TODO: check if we should handle that error + r.Deactivate(c, user, repo, host) + err = r.Activate(c, user, repo, link) if err != nil { c.String(500, err.Error()) return diff --git a/server/api/user.go b/server/api/user.go index 29f43687f..15e9ce5a3 100644 --- a/server/api/user.go +++ b/server/api/user.go @@ -54,7 +54,7 @@ func GetFeed(c *gin.Context) { Perms: store.FromContext(c), Match: shared.NamespaceFilter(config.OwnersWhitelist), } - if err := sync.Sync(user); err != nil { + if err := sync.Sync(c, user); err != nil { logrus.Debugf("sync error: %s: %s", user.Login, err) } else { logrus.Debugf("sync complete: %s", user.Login) @@ -100,7 +100,7 @@ func GetRepos(c *gin.Context) { Match: shared.NamespaceFilter(config.OwnersWhitelist), } - if err := sync.Sync(user); err != nil { + if err := sync.Sync(c, user); err != nil { logrus.Debugf("sync error: %s: %s", user.Login, err) } else { logrus.Debugf("sync complete: %s", user.Login) diff --git a/server/grpc/rpc.go b/server/grpc/rpc.go index 99d707d3f..7c126cb62 100644 --- a/server/grpc/rpc.go +++ b/server/grpc/rpc.go @@ -338,12 +338,12 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { } if !isMultiPipeline(procs) { - s.updateRemoteStatus(repo, build, nil) + s.updateRemoteStatus(c, repo, build, nil) } } if isMultiPipeline(procs) { - s.updateRemoteStatus(repo, build, proc) + s.updateRemoteStatus(c, repo, build, proc) } if err := s.logger.Close(c, id); err != nil { @@ -416,17 +416,17 @@ func buildStatus(procs []*model.Proc) string { return status } -func (s *RPC) updateRemoteStatus(repo *model.Repo, build *model.Build, proc *model.Proc) { +func (s *RPC) updateRemoteStatus(ctx context.Context, repo *model.Repo, build *model.Build, proc *model.Proc) { user, err := s.store.GetUser(repo.UserID) if err == nil { if refresher, ok := s.remote.(remote.Refresher); ok { - ok, _ := refresher.Refresh(user) + ok, _ := refresher.Refresh(ctx, user) if ok { s.store.UpdateUser(user) } } uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number) - err = s.remote.Status(user, repo, build, uri, proc) + err = s.remote.Status(ctx, user, repo, build, uri, proc) if err != nil { logrus.Errorf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err) } diff --git a/server/plugins/environments/filesystem.go b/server/plugins/environments/filesystem.go index 3a955dd76..173da1e75 100644 --- a/server/plugins/environments/filesystem.go +++ b/server/plugins/environments/filesystem.go @@ -9,13 +9,13 @@ type builtin struct { globals []*model.Environ } -// New returns a new local registry service. +// Filesystem returns a new local registry service. func Filesystem(params []string) model.EnvironService { var globals []*model.Environ for _, item := range params { - kvpair := strings.SplitN(item, ":", 2) - globals = append(globals, &model.Environ{Name: kvpair[0], Value: kvpair[1]}) + kvPair := strings.SplitN(item, ":", 2) + globals = append(globals, &model.Environ{Name: kvPair[0], Value: kvPair[1]}) } return &builtin{globals} } diff --git a/server/plugins/internal/http.go b/server/plugins/internal/http.go index 938bf3116..afb2cede3 100644 --- a/server/plugins/internal/http.go +++ b/server/plugins/internal/http.go @@ -2,6 +2,7 @@ package internal import ( "bytes" + "context" "encoding/json" "io" "io/ioutil" @@ -11,7 +12,7 @@ import ( // Send makes an http request to the given endpoint, writing the input // to the request body and unmarshaling the output from the response body. -func Send(method, path string, in, out interface{}) error { +func Send(ctx context.Context, method, path string, in, out interface{}) error { uri, err := url.Parse(path) if err != nil { return err @@ -29,7 +30,7 @@ func Send(method, path string, in, out interface{}) error { } // creates a new http request to bitbucket. - req, err := http.NewRequest(method, uri.String(), buf) + req, err := http.NewRequestWithContext(ctx, method, uri.String(), buf) if err != nil { return err } diff --git a/server/plugins/sender/plugin.go b/server/plugins/sender/plugin.go index aceb571c2..9578556fd 100644 --- a/server/plugins/sender/plugin.go +++ b/server/plugins/sender/plugin.go @@ -1,6 +1,7 @@ package sender import ( + "context" "fmt" "github.com/woodpecker-ci/woodpecker/server/model" @@ -22,7 +23,7 @@ func (p *plugin) SenderAllowed(user *model.User, repo *model.Repo, build *model. "build": build, "config": conf, } - err := internal.Send("POST", path, &data, nil) + err := internal.Send(context.TODO(), "POST", path, &data, nil) if err != nil { return false, err } @@ -31,21 +32,21 @@ func (p *plugin) SenderAllowed(user *model.User, repo *model.Repo, build *model. func (p *plugin) SenderCreate(repo *model.Repo, sender *model.Sender) error { path := fmt.Sprintf("%s/senders/%s/%s", p.endpoint, repo.Owner, repo.Name) - return internal.Send("POST", path, sender, nil) + return internal.Send(context.TODO(), "POST", path, sender, nil) } func (p *plugin) SenderUpdate(repo *model.Repo, sender *model.Sender) error { path := fmt.Sprintf("%s/senders/%s/%s", p.endpoint, repo.Owner, repo.Name) - return internal.Send("PUT", path, sender, nil) + return internal.Send(context.TODO(), "PUT", path, sender, nil) } func (p *plugin) SenderDelete(repo *model.Repo, login string) error { path := fmt.Sprintf("%s/senders/%s/%s/%s", p.endpoint, repo.Owner, repo.Name, login) - return internal.Send("DELETE", path, nil, nil) + return internal.Send(context.TODO(), "DELETE", path, nil, nil) } func (p *plugin) SenderList(repo *model.Repo) (out []*model.Sender, err error) { path := fmt.Sprintf("%s/senders/%s/%s", p.endpoint, repo.Owner, repo.Name) - err = internal.Send("GET", path, nil, out) + err = internal.Send(context.TODO(), "GET", path, nil, out) return out, err } diff --git a/server/queue/queue.go b/server/queue/queue.go index 50ce686e5..7d2f18c69 100644 --- a/server/queue/queue.go +++ b/server/queue/queue.go @@ -134,7 +134,7 @@ type Queue interface { // Push pushes a task to the tail of this queue. Push(c context.Context, task *Task) error - // Push pushes a task to the tail of this queue. + // PushAtOnce pushes a task to the tail of this queue. PushAtOnce(c context.Context, tasks []*Task) error // Poll retrieves and removes a task head of this queue. @@ -149,13 +149,13 @@ type Queue interface { // Error signals the task is complete with errors. Error(c context.Context, id string, err error) error - // Error signals the task is complete with errors. + // ErrorAtOnce signals the task is complete with errors. ErrorAtOnce(c context.Context, id []string, err error) error // Evict removes a pending task from the queue. Evict(c context.Context, id string) error - // Evict removes a pending task from the queue. + // EvictAtOnce removes a pending task from the queue. EvictAtOnce(c context.Context, id []string) error // Wait waits until the task is complete. @@ -164,9 +164,9 @@ type Queue interface { // Info returns internal queue information. Info(c context.Context) InfoT - // Stops the queue from handing out new work items in Poll + // Pause stops the queue from handing out new work items in Poll Pause() - // Starts the queue again, Poll returns new items + // Resume starts the queue again, Poll returns new items Resume() } diff --git a/server/remote/bitbucket/bitbucket.go b/server/remote/bitbucket/bitbucket.go index ee131439f..7929550ee 100644 --- a/server/remote/bitbucket/bitbucket.go +++ b/server/remote/bitbucket/bitbucket.go @@ -15,6 +15,7 @@ package bitbucket import ( + "context" "fmt" "net/http" "net/url" @@ -53,7 +54,7 @@ func New(client, secret string) remote.Remote { // Login authenticates an account with Bitbucket using the oauth2 protocol. The // Bitbucket account details are returned when the user is successfully authenticated. -func (c *config) Login(w http.ResponseWriter, req *http.Request) (*model.User, error) { +func (c *config) Login(ctx context.Context, w http.ResponseWriter, req *http.Request) (*model.User, error) { config := c.newConfig(server.Config.Server.Host) // get the OAuth errors @@ -72,12 +73,12 @@ func (c *config) Login(w http.ResponseWriter, req *http.Request) (*model.User, e return nil, nil } - token, err := config.Exchange(oauth2.NoContext, code) + token, err := config.Exchange(ctx, code) if err != nil { return nil, err } - client := internal.NewClient(c.API, config.Client(oauth2.NoContext, token)) + client := internal.NewClient(ctx, c.API, config.Client(ctx, token)) curr, err := client.FindCurrent() if err != nil { return nil, err @@ -87,8 +88,8 @@ func (c *config) Login(w http.ResponseWriter, req *http.Request) (*model.User, e // Auth uses the Bitbucket oauth2 access token and refresh token to authenticate // a session and return the Bitbucket account login. -func (c *config) Auth(token, secret string) (string, error) { - client := c.newClientToken(token, secret) +func (c *config) Auth(ctx context.Context, token, secret string) (string, error) { + client := c.newClientToken(ctx, token, secret) user, err := client.FindCurrent() if err != nil { return "", err @@ -98,10 +99,10 @@ func (c *config) Auth(token, secret string) (string, error) { // Refresh refreshes the Bitbucket oauth2 access token. If the token is // refreshed the user is updated and a true value is returned. -func (c *config) Refresh(user *model.User) (bool, error) { +func (c *config) Refresh(ctx context.Context, user *model.User) (bool, error) { config := c.newConfig("") source := config.TokenSource( - oauth2.NoContext, &oauth2.Token{RefreshToken: user.Secret}) + ctx, &oauth2.Token{RefreshToken: user.Secret}) token, err := source.Token() if err != nil || len(token.AccessToken) == 0 { @@ -115,12 +116,12 @@ func (c *config) Refresh(user *model.User) (bool, error) { } // Teams returns a list of all team membership for the Bitbucket account. -func (c *config) Teams(u *model.User) ([]*model.Team, error) { +func (c *config) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) { opts := &internal.ListTeamOpts{ PageLen: 100, Role: "member", } - resp, err := c.newClient(u).ListTeams(opts) + resp, err := c.newClient(ctx, u).ListTeams(opts) if err != nil { return nil, err } @@ -128,8 +129,8 @@ func (c *config) Teams(u *model.User) ([]*model.Team, error) { } // Repo returns the named Bitbucket repository. -func (c *config) Repo(u *model.User, owner, name string) (*model.Repo, error) { - repo, err := c.newClient(u).FindRepo(owner, name) +func (c *config) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) { + repo, err := c.newClient(ctx, u).FindRepo(owner, name) if err != nil { return nil, err } @@ -138,8 +139,8 @@ func (c *config) Repo(u *model.User, owner, name string) (*model.Repo, error) { // Repos returns a list of all repositories for Bitbucket account, including // organization repositories. -func (c *config) Repos(u *model.User) ([]*model.Repo, error) { - client := c.newClient(u) +func (c *config) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) { + client := c.newClient(ctx, u) var all []*model.Repo @@ -171,8 +172,8 @@ func (c *config) Repos(u *model.User) ([]*model.Repo, error) { // does not have an endpoint to access user permissions, we attempt to fetch // the repository hook list, which is restricted to administrators to calculate // administrative access to a repository. -func (c *config) Perm(u *model.User, owner, name string) (*model.Perm, error) { - client := c.newClient(u) +func (c *config) Perm(ctx context.Context, u *model.User, owner, name string) (*model.Perm, error) { + client := c.newClient(ctx, u) perms := new(model.Perm) repo, err := client.FindRepo(owner, name) @@ -200,40 +201,40 @@ func (c *config) Perm(u *model.User, owner, name string) (*model.Perm, error) { } // File fetches the file from the Bitbucket repository and returns its contents. -func (c *config) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { - config, err := c.newClient(u).FindSource(r.Owner, r.Name, b.Commit, f) +func (c *config) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { + config, err := c.newClient(ctx, u).FindSource(r.Owner, r.Name, b.Commit, f) if err != nil { return nil, err } return []byte(*config), err } -func (c *config) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { +func (c *config) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { return nil, fmt.Errorf("Not implemented") } // Status creates a build status for the Bitbucket commit. -func (c *config) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { +func (c *config) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { status := internal.BuildStatus{ State: convertStatus(b.Status), Desc: convertDesc(b.Status), Key: "Drone", Url: link, } - return c.newClient(u).CreateStatus(r.Owner, r.Name, b.Commit, &status) + return c.newClient(ctx, u).CreateStatus(r.Owner, r.Name, b.Commit, &status) } // Activate activates the repository by registering repository push hooks with // the Bitbucket repository. Prior to registering hook, previously created hooks // are deleted. -func (c *config) Activate(u *model.User, r *model.Repo, link string) error { +func (c *config) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error { rawurl, err := url.Parse(link) if err != nil { return err } - c.Deactivate(u, r, link) + c.Deactivate(ctx, u, r, link) - return c.newClient(u).CreateHook(r.Owner, r.Name, &internal.Hook{ + return c.newClient(ctx, u).CreateHook(r.Owner, r.Name, &internal.Hook{ Active: true, Desc: rawurl.Host, Events: []string{"repo:push"}, @@ -243,8 +244,8 @@ func (c *config) Activate(u *model.User, r *model.Repo, link string) error { // Deactivate deactives the repository be removing repository push hooks from // the Bitbucket repository. -func (c *config) Deactivate(u *model.User, r *model.Repo, link string) error { - client := c.newClient(u) +func (c *config) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + client := c.newClient(ctx, u) hooks, err := client.ListHooks(r.Owner, r.Name, &internal.ListOpts{}) if err != nil { @@ -274,13 +275,14 @@ func (c *config) Hook(req *http.Request) (*model.Repo, *model.Build, error) { } // helper function to return the bitbucket oauth2 client -func (c *config) newClient(u *model.User) *internal.Client { - return c.newClientToken(u.Token, u.Secret) +func (c *config) newClient(ctx context.Context, u *model.User) *internal.Client { + return c.newClientToken(ctx, u.Token, u.Secret) } // helper function to return the bitbucket oauth2 client -func (c *config) newClientToken(token, secret string) *internal.Client { +func (c *config) newClientToken(ctx context.Context, token, secret string) *internal.Client { return internal.NewClientToken( + ctx, c.API, c.Client, c.Secret, diff --git a/server/remote/bitbucket/bitbucket_test.go b/server/remote/bitbucket/bitbucket_test.go index a38b183c4..e642afc20 100644 --- a/server/remote/bitbucket/bitbucket_test.go +++ b/server/remote/bitbucket/bitbucket_test.go @@ -16,6 +16,7 @@ package bitbucket import ( "bytes" + "context" "net/http" "net/http/httptest" "testing" @@ -35,6 +36,7 @@ func Test_bitbucket(t *testing.T) { c := &config{URL: s.URL, API: s.URL} g := goblin.Goblin(t) + ctx := context.Background() g.Describe("Bitbucket client", func() { g.After(func() { @@ -61,13 +63,13 @@ func Test_bitbucket(t *testing.T) { g.It("Should redirect to authorize", func() { w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "", nil) - _, err := c.Login(w, r) + _, err := c.Login(ctx, w, r) g.Assert(err == nil).IsTrue() g.Assert(w.Code).Equal(http.StatusSeeOther) }) g.It("Should return authenticated user", func() { r, _ := http.NewRequest("GET", "?code=code", nil) - u, err := c.Login(nil, r) + u, err := c.Login(ctx, nil, r) g.Assert(err == nil).IsTrue() g.Assert(u.Login).Equal(fakeUser.Login) g.Assert(u.Token).Equal("2YotnFZFEjr1zCsicMWpAA") @@ -76,54 +78,48 @@ func Test_bitbucket(t *testing.T) { g.It("Should handle failure to exchange code", func() { w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "?code=code_bad_request", nil) - _, err := c.Login(w, r) + _, err := c.Login(ctx, w, r) g.Assert(err != nil).IsTrue() }) g.It("Should handle failure to resolve user", func() { r, _ := http.NewRequest("GET", "?code=code_user_not_found", nil) - _, err := c.Login(nil, r) + _, err := c.Login(ctx, nil, r) g.Assert(err != nil).IsTrue() }) g.It("Should handle authentication errors", func() { r, _ := http.NewRequest("GET", "?error=invalid_scope", nil) - _, err := c.Login(nil, r) + _, err := c.Login(ctx, nil, r) g.Assert(err != nil).IsTrue() }) }) g.Describe("Given an access token", func() { g.It("Should return the authenticated user", func() { - login, err := c.Auth( - fakeUser.Token, - fakeUser.Secret, - ) + login, err := c.Auth(ctx, fakeUser.Token, fakeUser.Secret) g.Assert(err == nil).IsTrue() g.Assert(login).Equal(fakeUser.Login) }) g.It("Should handle a failure to resolve user", func() { - _, err := c.Auth( - fakeUserNotFound.Token, - fakeUserNotFound.Secret, - ) + _, err := c.Auth(ctx, fakeUserNotFound.Token, fakeUserNotFound.Secret) g.Assert(err != nil).IsTrue() }) }) g.Describe("Given a refresh token", func() { g.It("Should return a refresh access token", func() { - ok, err := c.Refresh(fakeUserRefresh) + ok, err := c.Refresh(ctx, fakeUserRefresh) g.Assert(err == nil).IsTrue() g.Assert(ok).IsTrue() g.Assert(fakeUserRefresh.Token).Equal("2YotnFZFEjr1zCsicMWpAA") g.Assert(fakeUserRefresh.Secret).Equal("tGzv3JOkF0XG5Qx2TlKWIA") }) g.It("Should handle an empty access token", func() { - ok, err := c.Refresh(fakeUserRefreshEmpty) + ok, err := c.Refresh(ctx, fakeUserRefreshEmpty) g.Assert(err == nil).IsFalse() g.Assert(ok).IsFalse() }) g.It("Should handle a failure to refresh", func() { - ok, err := c.Refresh(fakeUserRefreshFail) + ok, err := c.Refresh(ctx, fakeUserRefreshFail) g.Assert(err != nil).IsTrue() g.Assert(ok).IsFalse() }) @@ -131,61 +127,37 @@ func Test_bitbucket(t *testing.T) { g.Describe("When requesting a repository", func() { g.It("Should return the details", func() { - repo, err := c.Repo( - fakeUser, - fakeRepo.Owner, - fakeRepo.Name, - ) + repo, err := c.Repo(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name) g.Assert(err == nil).IsTrue() g.Assert(repo.FullName).Equal(fakeRepo.FullName) }) g.It("Should handle not found errors", func() { - _, err := c.Repo( - fakeUser, - fakeRepoNotFound.Owner, - fakeRepoNotFound.Name, - ) + _, err := c.Repo(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) g.Assert(err != nil).IsTrue() }) }) g.Describe("When requesting repository permissions", func() { g.It("Should handle not found errors", func() { - _, err := c.Perm( - fakeUser, - fakeRepoNotFound.Owner, - fakeRepoNotFound.Name, - ) + _, err := c.Perm(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) g.Assert(err != nil).IsTrue() }) g.It("Should authorize read access", func() { - perm, err := c.Perm( - fakeUser, - fakeRepoReadOnly.Owner, - fakeRepoReadOnly.Name, - ) + perm, err := c.Perm(ctx, fakeUser, fakeRepoReadOnly.Owner, fakeRepoReadOnly.Name) g.Assert(err == nil).IsTrue() g.Assert(perm.Pull).IsTrue() g.Assert(perm.Push).IsFalse() g.Assert(perm.Admin).IsFalse() }) g.It("Should authorize write access", func() { - perm, err := c.Perm( - fakeUser, - fakeRepoWriteOnly.Owner, - fakeRepoWriteOnly.Name, - ) + perm, err := c.Perm(ctx, fakeUser, fakeRepoWriteOnly.Owner, fakeRepoWriteOnly.Name) g.Assert(err == nil).IsTrue() g.Assert(perm.Pull).IsTrue() g.Assert(perm.Push).IsTrue() g.Assert(perm.Admin).IsFalse() }) g.It("Should authorize admin access", func() { - perm, err := c.Perm( - fakeUser, - fakeRepoAdmin.Owner, - fakeRepoAdmin.Name, - ) + perm, err := c.Perm(ctx, fakeUser, fakeRepoAdmin.Owner, fakeRepoAdmin.Name) g.Assert(err == nil).IsTrue() g.Assert(perm.Pull).IsTrue() g.Assert(perm.Push).IsTrue() @@ -195,67 +167,67 @@ func Test_bitbucket(t *testing.T) { g.Describe("When requesting user repositories", func() { g.It("Should return the details", func() { - repos, err := c.Repos(fakeUser) + repos, err := c.Repos(ctx, fakeUser) g.Assert(err == nil).IsTrue() g.Assert(repos[0].FullName).Equal(fakeRepo.FullName) }) g.It("Should handle organization not found errors", func() { - _, err := c.Repos(fakeUserNoTeams) + _, err := c.Repos(ctx, fakeUserNoTeams) g.Assert(err != nil).IsTrue() }) g.It("Should handle not found errors", func() { - _, err := c.Repos(fakeUserNoRepos) + _, err := c.Repos(ctx, fakeUserNoRepos) g.Assert(err != nil).IsTrue() }) }) g.Describe("When requesting user teams", func() { g.It("Should return the details", func() { - teams, err := c.Teams(fakeUser) + teams, err := c.Teams(ctx, fakeUser) g.Assert(err == nil).IsTrue() g.Assert(teams[0].Login).Equal("superfriends") g.Assert(teams[0].Avatar).Equal("http://i.imgur.com/ZygP55A.jpg") }) g.It("Should handle not found error", func() { - _, err := c.Teams(fakeUserNoTeams) + _, err := c.Teams(ctx, fakeUserNoTeams) g.Assert(err != nil).IsTrue() }) }) g.Describe("When downloading a file", func() { g.It("Should return the bytes", func() { - raw, err := c.File(fakeUser, fakeRepo, fakeBuild, "file") + raw, err := c.File(ctx, fakeUser, fakeRepo, fakeBuild, "file") g.Assert(err == nil).IsTrue() g.Assert(len(raw) != 0).IsTrue() }) g.It("Should handle not found error", func() { - _, err := c.File(fakeUser, fakeRepo, fakeBuild, "file_not_found") + _, err := c.File(ctx, fakeUser, fakeRepo, fakeBuild, "file_not_found") g.Assert(err != nil).IsTrue() }) }) g.Describe("When activating a repository", func() { g.It("Should error when malformed hook", func() { - err := c.Activate(fakeUser, fakeRepo, "%gh&%ij") + err := c.Activate(ctx, fakeUser, fakeRepo, "%gh&%ij") g.Assert(err != nil).IsTrue() }) g.It("Should create the hook", func() { - err := c.Activate(fakeUser, fakeRepo, "http://127.0.0.1") + err := c.Activate(ctx, fakeUser, fakeRepo, "http://127.0.0.1") g.Assert(err == nil).IsTrue() }) }) g.Describe("When deactivating a repository", func() { g.It("Should error when listing hooks fails", func() { - err := c.Deactivate(fakeUser, fakeRepoNoHooks, "http://127.0.0.1") + err := c.Deactivate(ctx, fakeUser, fakeRepoNoHooks, "http://127.0.0.1") g.Assert(err != nil).IsTrue() }) g.It("Should successfully remove hooks", func() { - err := c.Deactivate(fakeUser, fakeRepo, "http://127.0.0.1") + err := c.Deactivate(ctx, fakeUser, fakeRepo, "http://127.0.0.1") g.Assert(err == nil).IsTrue() }) g.It("Should successfully deactivate when hook already removed", func() { - err := c.Deactivate(fakeUser, fakeRepoEmptyHook, "http://127.0.0.1") + err := c.Deactivate(ctx, fakeUser, fakeRepoEmptyHook, "http://127.0.0.1") g.Assert(err == nil).IsTrue() }) }) @@ -283,7 +255,7 @@ func Test_bitbucket(t *testing.T) { }) g.It("Should update the status", func() { - err := c.Status(fakeUser, fakeRepo, fakeBuild, "http://127.0.0.1", nil) + err := c.Status(ctx, fakeUser, fakeRepo, fakeBuild, "http://127.0.0.1", nil) g.Assert(err == nil).IsTrue() }) diff --git a/server/remote/bitbucket/internal/client.go b/server/remote/bitbucket/internal/client.go index 59cf916e1..cd59d89ed 100644 --- a/server/remote/bitbucket/internal/client.go +++ b/server/remote/bitbucket/internal/client.go @@ -16,6 +16,7 @@ package internal import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -50,19 +51,24 @@ const ( type Client struct { *http.Client base string + ctx context.Context } -func NewClient(url string, client *http.Client) *Client { - return &Client{client, url} +func NewClient(ctx context.Context, url string, client *http.Client) *Client { + return &Client{ + Client: client, + base: url, + ctx: ctx, + } } -func NewClientToken(url, client, secret string, token *oauth2.Token) *Client { +func NewClientToken(ctx context.Context, url, client, secret string, token *oauth2.Token) *Client { config := &oauth2.Config{ ClientID: client, ClientSecret: secret, Endpoint: bitbucket.Endpoint, } - return NewClient(url, config.Client(oauth2.NoContext, token)) + return NewClient(ctx, url, config.Client(ctx, token)) } func (c *Client) FindCurrent() (*Account, error) { @@ -172,7 +178,6 @@ func (c *Client) GetPermission(fullName string) (*RepoPerm, error) { } func (c *Client) do(rawurl, method string, in, out interface{}) (*string, error) { - uri, err := url.Parse(rawurl) if err != nil { return nil, err @@ -190,7 +195,7 @@ func (c *Client) do(rawurl, method string, in, out interface{}) (*string, error) } // creates a new http request to bitbucket. - req, err := http.NewRequest(method, uri.String(), buf) + req, err := http.NewRequestWithContext(c.ctx, method, uri.String(), buf) if err != nil { return nil, err } diff --git a/server/remote/bitbucketserver/bitbucketserver.go b/server/remote/bitbucketserver/bitbucketserver.go index 5413c8e61..b1e08ec52 100644 --- a/server/remote/bitbucketserver/bitbucketserver.go +++ b/server/remote/bitbucketserver/bitbucketserver.go @@ -18,6 +18,7 @@ package bitbucketserver // quality or security standards expected of this project. Please use with caution. import ( + "context" "crypto/rsa" "crypto/tls" "crypto/x509" @@ -103,7 +104,7 @@ func New(opts Opts) (remote.Remote, error) { return config, nil } -func (c *Config) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) { +func (c *Config) Login(ctx context.Context, res http.ResponseWriter, req *http.Request) (*model.User, error) { requestToken, u, err := c.Consumer.GetRequestTokenAndUrl("oob") if err != nil { return nil, err @@ -119,7 +120,7 @@ func (c *Config) Login(res http.ResponseWriter, req *http.Request) (*model.User, return nil, err } - client := internal.NewClientWithToken(c.URL, c.Consumer, accessToken.Token) + client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, accessToken.Token) user, err := client.FindCurrentUser() if err != nil { @@ -131,12 +132,12 @@ func (c *Config) Login(res http.ResponseWriter, req *http.Request) (*model.User, } // Auth is not supported by the Stash driver. -func (*Config) Auth(token, secret string) (string, error) { +func (*Config) Auth(ctx context.Context, token, secret string) (string, error) { return "", fmt.Errorf("Not Implemented") } // Teams is not supported by the Stash driver. -func (*Config) Teams(u *model.User) ([]*model.Team, error) { +func (*Config) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) { var teams []*model.Team return teams, nil } @@ -146,16 +147,16 @@ func (*Config) TeamPerm(u *model.User, org string) (*model.Perm, error) { return nil, nil } -func (c *Config) Repo(u *model.User, owner, name string) (*model.Repo, error) { - repo, err := internal.NewClientWithToken(c.URL, c.Consumer, u.Token).FindRepo(owner, name) +func (c *Config) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) { + repo, err := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token).FindRepo(owner, name) if err != nil { return nil, err } return convertRepo(repo), nil } -func (c *Config) Repos(u *model.User) ([]*model.Repo, error) { - repos, err := internal.NewClientWithToken(c.URL, c.Consumer, u.Token).FindRepos() +func (c *Config) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) { + repos, err := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token).FindRepos() if err != nil { return nil, err } @@ -167,24 +168,24 @@ func (c *Config) Repos(u *model.User) ([]*model.Repo, error) { return all, nil } -func (c *Config) Perm(u *model.User, owner, repo string) (*model.Perm, error) { - client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token) +func (c *Config) Perm(ctx context.Context, u *model.User, owner, repo string) (*model.Perm, error) { + client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token) return client.FindRepoPerms(owner, repo) } -func (c *Config) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { - client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token) +func (c *Config) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { + client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token) return client.FindFileForRepo(r.Owner, r.Name, f, b.Ref) } -func (c *Config) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { +func (c *Config) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { return nil, fmt.Errorf("Not implemented") } // Status is not supported by the bitbucketserver driver. -func (c *Config) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { +func (c *Config) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { status := internal.BuildStatus{ State: convertStatus(b.Status), Desc: convertDesc(b.Status), @@ -193,7 +194,7 @@ func (c *Config) Status(u *model.User, r *model.Repo, b *model.Build, link strin Url: link, } - client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token) + client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token) return client.CreateStatus(b.Commit, &status) } @@ -217,14 +218,14 @@ func (c *Config) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error) { }, nil } -func (c *Config) Activate(u *model.User, r *model.Repo, link string) error { - client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token) +func (c *Config) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token) return client.CreateHook(r.Owner, r.Name, link) } -func (c *Config) Deactivate(u *model.User, r *model.Repo, link string) error { - client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token) +func (c *Config) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token) return client.DeleteHook(r.Owner, r.Name, link) } diff --git a/server/remote/bitbucketserver/internal/client.go b/server/remote/bitbucketserver/internal/client.go index 6faafd48a..8746e9eaa 100644 --- a/server/remote/bitbucketserver/internal/client.go +++ b/server/remote/bitbucketserver/internal/client.go @@ -16,6 +16,7 @@ package internal import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -48,20 +49,27 @@ type Client struct { client *http.Client base string accessToken string + ctx context.Context } -func NewClientWithToken(url string, consumer *oauth.Consumer, AccessToken string) *Client { +func NewClientWithToken(ctx context.Context, url string, consumer *oauth.Consumer, AccessToken string) *Client { var token oauth.AccessToken token.Token = AccessToken client, err := consumer.MakeHttpClient(&token) if err != nil { log.Error(err) } - return &Client{client, url, AccessToken} + + return &Client{ + client: client, + base: url, + accessToken: AccessToken, + ctx: ctx, + } } func (c *Client) FindCurrentUser() (*User, error) { - CurrentUserIdResponse, err := c.client.Get(fmt.Sprintf(currentUserId, c.base)) + CurrentUserIdResponse, err := c.doGet(fmt.Sprintf(currentUserId, c.base)) if CurrentUserIdResponse != nil { defer CurrentUserIdResponse.Body.Close() } @@ -75,7 +83,7 @@ func (c *Client) FindCurrentUser() (*User, error) { } login := string(bits) - CurrentUserResponse, err := c.client.Get(fmt.Sprintf(pathUser, c.base, login)) + CurrentUserResponse, err := c.doGet(fmt.Sprintf(pathUser, c.base, login)) if CurrentUserResponse != nil { defer CurrentUserResponse.Body.Close() } @@ -100,7 +108,7 @@ func (c *Client) FindCurrentUser() (*User, error) { func (c *Client) FindRepo(owner string, name string) (*Repo, error) { urlString := fmt.Sprintf(pathRepo, c.base, owner, name) - response, err := c.client.Get(urlString) + response, err := c.doGet(urlString) if response != nil { defer response.Body.Close() } @@ -128,7 +136,7 @@ func (c *Client) FindRepoPerms(owner string, repo string) (*model.Perm, error) { return perms, err } // Must have admin to be able to list hooks. If have access the enable perms - resp, err := c.client.Get(fmt.Sprintf(pathHook, c.base, owner, repo, hookName)) + resp, err := c.doGet(fmt.Sprintf(pathHook, c.base, owner, repo, hookName)) if resp != nil { defer resp.Body.Close() } @@ -141,7 +149,7 @@ func (c *Client) FindRepoPerms(owner string, repo string) (*model.Perm, error) { } func (c *Client) FindFileForRepo(owner string, repo string, fileName string, ref string) ([]byte, error) { - response, err := c.client.Get(fmt.Sprintf(pathSource, c.base, owner, repo, fileName, ref)) + response, err := c.doGet(fmt.Sprintf(pathSource, c.base, owner, repo, fileName, ref)) if response != nil { defer response.Body.Close() } @@ -203,7 +211,7 @@ func (c *Client) DeleteHook(owner string, name string, link string) error { func (c *Client) GetHookDetails(owner string, name string) (*HookPluginDetails, error) { urlString := fmt.Sprintf(pathHookDetails, c.base, owner, name, hookName) - response, err := c.client.Get(urlString) + response, err := c.doGet(urlString) if response != nil { defer response.Body.Close() } @@ -218,7 +226,7 @@ func (c *Client) GetHookDetails(owner string, name string) (*HookPluginDetails, func (c *Client) GetHooks(owner string, name string) (*HookSettings, error) { urlString := fmt.Sprintf(pathHookSettings, c.base, owner, name, hookName) - response, err := c.client.Get(urlString) + response, err := c.doGet(urlString) if response != nil { defer response.Body.Close() } @@ -233,9 +241,22 @@ func (c *Client) GetHooks(owner string, name string) (*HookSettings, error) { //TODO: make these as as general do with the action +//Helper function to help create get +func (c *Client) doGet(url string) (*http.Response, error) { + request, err := http.NewRequestWithContext(c.ctx, "GET", url, nil) + if err != nil { + return nil, err + } + request.Header.Add("Content-Type", "application/json") + return c.client.Do(request) +} + //Helper function to help create the hook func (c *Client) doPut(url string, body []byte) error { - request, err := http.NewRequest("PUT", url, bytes.NewBuffer(body)) + request, err := http.NewRequestWithContext(c.ctx, "PUT", url, bytes.NewBuffer(body)) + if err != nil { + return err + } request.Header.Add("Content-Type", "application/json") response, err := c.client.Do(request) if response != nil { @@ -258,7 +279,10 @@ func (c *Client) doPost(url string, status *BuildStatus) error { return err } } - request, err := http.NewRequest("POST", url, buf) + request, err := http.NewRequestWithContext(c.ctx, "POST", url, buf) + if err != nil { + return err + } request.Header.Add("Content-Type", "application/json") response, err := c.client.Do(request) if response != nil { @@ -269,7 +293,10 @@ func (c *Client) doPost(url string, status *BuildStatus) error { //Helper function to do delete on the hook func (c *Client) doDelete(url string) error { - request, err := http.NewRequest("DELETE", url, nil) + request, err := http.NewRequestWithContext(c.ctx, "DELETE", url, nil) + if err != nil { + return err + } response, err := c.client.Do(request) if response != nil { defer response.Body.Close() @@ -281,7 +308,7 @@ func (c *Client) doDelete(url string) error { func (c *Client) paginatedRepos(start int) ([]*Repo, error) { limit := 1000 requestUrl := fmt.Sprintf(pathRepos, c.base, strconv.Itoa(start), strconv.Itoa(limit)) - response, err := c.client.Get(requestUrl) + response, err := c.doGet(requestUrl) if response != nil { defer response.Body.Close() } diff --git a/server/remote/coding/coding.go b/server/remote/coding/coding.go index e22ba15a6..67e9db318 100644 --- a/server/remote/coding/coding.go +++ b/server/remote/coding/coding.go @@ -15,6 +15,7 @@ package coding import ( + "context" "crypto/tls" "fmt" "net/http" @@ -25,7 +26,6 @@ import ( "github.com/woodpecker-ci/woodpecker/server/remote" "github.com/woodpecker-ci/woodpecker/server/remote/coding/internal" - "golang.org/x/net/context" "golang.org/x/oauth2" ) @@ -78,7 +78,7 @@ type Coding struct { // Login authenticates the session and returns the // remote user details. -func (c *Coding) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) { +func (c *Coding) Login(ctx context.Context, res http.ResponseWriter, req *http.Request) (*model.User, error) { config := c.newConfig(server.Config.Server.Host) // get the OAuth errors @@ -97,12 +97,12 @@ func (c *Coding) Login(res http.ResponseWriter, req *http.Request) (*model.User, return nil, nil } - token, err := config.Exchange(c.newContext(), code) + token, err := config.Exchange(c.newContext(ctx), code) if err != nil { return nil, err } - user, err := c.newClientToken(token.AccessToken, token.RefreshToken).GetCurrentUser() + user, err := c.newClientToken(ctx, token.AccessToken).GetCurrentUser() if err != nil { return nil, err } @@ -119,8 +119,8 @@ func (c *Coding) Login(res http.ResponseWriter, req *http.Request) (*model.User, // Auth authenticates the session and returns the remote user // login for the given token and secret -func (c *Coding) Auth(token, secret string) (string, error) { - user, err := c.newClientToken(token, secret).GetCurrentUser() +func (c *Coding) Auth(ctx context.Context, token, secret string) (string, error) { + user, err := c.newClientToken(ctx, token).GetCurrentUser() if err != nil { return "", err } @@ -130,9 +130,9 @@ func (c *Coding) Auth(token, secret string) (string, error) { // Refresh refreshes an oauth token and expiration for the given // user. It returns true if the token was refreshed, false if the // token was not refreshed, and error if it failed to refersh. -func (c *Coding) Refresh(u *model.User) (bool, error) { +func (c *Coding) Refresh(ctx context.Context, u *model.User) (bool, error) { config := c.newConfig("") - source := config.TokenSource(c.newContext(), &oauth2.Token{RefreshToken: u.Secret}) + source := config.TokenSource(c.newContext(ctx), &oauth2.Token{RefreshToken: u.Secret}) token, err := source.Token() if err != nil || len(token.AccessToken) == 0 { return false, err @@ -145,9 +145,9 @@ func (c *Coding) Refresh(u *model.User) (bool, error) { } // Teams fetches a list of team memberships from the remote system. -func (c *Coding) Teams(u *model.User) ([]*model.Team, error) { +func (c *Coding) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) { // EMPTY: not implemented in Coding OAuth API - return nil, nil + return nil, fmt.Errorf("Not implemented") } // TeamPerm fetches the named organization permissions from @@ -158,12 +158,13 @@ func (c *Coding) TeamPerm(u *model.User, org string) (*model.Perm, error) { } // Repo fetches the named repository from the remote system. -func (c *Coding) Repo(u *model.User, owner, repo string) (*model.Repo, error) { - project, err := c.newClient(u).GetProject(owner, repo) +func (c *Coding) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) { + client := c.newClient(ctx, u) + project, err := client.GetProject(owner, name) if err != nil { return nil, err } - depot, err := c.newClient(u).GetDepot(owner, repo) + depot, err := client.GetDepot(owner, name) if err != nil { return nil, err } @@ -181,15 +182,16 @@ func (c *Coding) Repo(u *model.User, owner, repo string) (*model.Repo, error) { } // Repos fetches a list of repos from the remote system. -func (c *Coding) Repos(u *model.User) ([]*model.Repo, error) { - projectList, err := c.newClient(u).GetProjectList() +func (c *Coding) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) { + client := c.newClient(ctx, u) + projectList, err := client.GetProjectList() if err != nil { return nil, err } repos := make([]*model.Repo, 0) for _, project := range projectList { - depot, err := c.newClient(u).GetDepot(project.Owner, project.Name) + depot, err := client.GetDepot(project.Owner, project.Name) if err != nil { return nil, err } @@ -211,8 +213,8 @@ func (c *Coding) Repos(u *model.User) ([]*model.Repo, error) { // Perm fetches the named repository permissions from // the remote system for the specified user. -func (c *Coding) Perm(u *model.User, owner, repo string) (*model.Perm, error) { - project, err := c.newClient(u).GetProject(owner, repo) +func (c *Coding) Perm(ctx context.Context, u *model.User, owner, repo string) (*model.Perm, error) { + project, err := c.newClient(ctx, u).GetProject(owner, repo) if err != nil { return nil, err } @@ -228,20 +230,20 @@ func (c *Coding) Perm(u *model.User, owner, repo string) (*model.Perm, error) { // File fetches a file from the remote repository and returns in string // format. -func (c *Coding) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { - data, err := c.newClient(u).GetFile(r.Owner, r.Name, b.Commit, f) +func (c *Coding) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { + data, err := c.newClient(ctx, u).GetFile(r.Owner, r.Name, b.Commit, f) if err != nil { return nil, err } return data, nil } -func (c *Coding) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { +func (c *Coding) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { return nil, fmt.Errorf("Not implemented") } // Status sends the commit status to the remote system. -func (c *Coding) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { +func (c *Coding) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { // EMPTY: not implemented in Coding OAuth API return nil } @@ -264,14 +266,14 @@ func (c *Coding) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { } // Activate activates a repository by creating the post-commit hook. -func (c *Coding) Activate(u *model.User, r *model.Repo, link string) error { - return c.newClient(u).AddWebhook(r.Owner, r.Name, link) +func (c *Coding) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + return c.newClient(ctx, u).AddWebhook(r.Owner, r.Name, link) } // Deactivate deactivates a repository by removing all previously created // post-commit hooks matching the given link. -func (c *Coding) Deactivate(u *model.User, r *model.Repo, link string) error { - return c.newClient(u).RemoveWebhook(r.Owner, r.Name, link) +func (c *Coding) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + return c.newClient(ctx, u).RemoveWebhook(r.Owner, r.Name, link) } // Hook parses the post-commit hook from the Request body and returns the @@ -286,11 +288,11 @@ func (c *Coding) Hook(r *http.Request) (*model.Repo, *model.Build, error) { // helper function to return the Coding oauth2 context using an HTTPClient that // disables TLS verification if disabled in the remote settings. -func (c *Coding) newContext() context.Context { +func (c *Coding) newContext(ctx context.Context) context.Context { if !c.SkipVerify { - return oauth2.NoContext + return ctx } - return context.WithValue(nil, oauth2.HTTPClient, &http.Client{ + return context.WithValue(ctx, oauth2.HTTPClient, &http.Client{ Transport: &http.Transport{ Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{ @@ -315,12 +317,12 @@ func (c *Coding) newConfig(redirect string) *oauth2.Config { } // helper function to return the Coding oauth2 client -func (c *Coding) newClient(u *model.User) *internal.Client { - return c.newClientToken(u.Token, u.Secret) +func (c *Coding) newClient(ctx context.Context, u *model.User) *internal.Client { + return c.newClientToken(ctx, u.Token) } // helper function to return the Coding oauth2 client -func (c *Coding) newClientToken(token, secret string) *internal.Client { +func (c *Coding) newClientToken(ctx context.Context, token string) *internal.Client { client := &http.Client{ Transport: &http.Transport{ Proxy: http.ProxyFromEnvironment, @@ -329,7 +331,7 @@ func (c *Coding) newClientToken(token, secret string) *internal.Client { }, }, } - return internal.NewClient(c.URL, "/api", token, "drone", client) + return internal.NewClient(ctx, c.URL, "/api", token, "drone", client) } func (c *Coding) resourceLink(resourcePath string) string { diff --git a/server/remote/coding/coding_test.go b/server/remote/coding/coding_test.go index 78df27ed3..380896dc3 100644 --- a/server/remote/coding/coding_test.go +++ b/server/remote/coding/coding_test.go @@ -16,6 +16,7 @@ package coding import ( "bytes" + "context" "net/http" "net/http/httptest" "testing" @@ -33,6 +34,7 @@ func Test_coding(t *testing.T) { s := httptest.NewServer(fixtures.Handler()) c := &Coding{URL: s.URL} + ctx := context.Background() g := goblin.Goblin(t) g.Describe("Coding", func() { @@ -67,13 +69,13 @@ func Test_coding(t *testing.T) { g.It("Should redirect to authorize", func() { w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "", nil) - _, err := c.Login(w, r) + _, err := c.Login(ctx, w, r) g.Assert(err == nil).IsTrue() g.Assert(w.Code).Equal(http.StatusSeeOther) }) g.It("Should return authenticated user", func() { r, _ := http.NewRequest("GET", "?code=code", nil) - u, err := c.Login(nil, r) + u, err := c.Login(ctx, nil, r) g.Assert(err == nil).IsTrue() g.Assert(u.Login).Equal(fakeUser.Login) g.Assert(u.Token).Equal(fakeUser.Token) @@ -83,43 +85,33 @@ func Test_coding(t *testing.T) { g.Describe("Given an access token", func() { g.It("Should return the anthenticated user", func() { - login, err := c.Auth( - fakeUser.Token, - fakeUser.Secret, - ) + login, err := c.Auth(ctx, fakeUser.Token, fakeUser.Secret) g.Assert(err == nil).IsTrue() g.Assert(login).Equal(fakeUser.Login) }) g.It("Should handle a failure to resolve user", func() { - _, err := c.Auth( - fakeUserNotFound.Token, - fakeUserNotFound.Secret, - ) + _, err := c.Auth(ctx, fakeUserNotFound.Token, fakeUserNotFound.Secret) g.Assert(err != nil).IsTrue() }) }) g.Describe("Given a refresh token", func() { g.It("Should return a refresh access token", func() { - ok, err := c.Refresh(fakeUserRefresh) + ok, err := c.Refresh(ctx, fakeUserRefresh) g.Assert(err == nil).IsTrue() g.Assert(ok).IsTrue() g.Assert(fakeUserRefresh.Token).Equal("VDZupx0usVRV4oOd1FCu4xUxgk8SY0TK") g.Assert(fakeUserRefresh.Secret).Equal("BenBQq7TWZ7Cp0aUM47nQjTz2QHNmTWcPctB609n") }) g.It("Should handle an invalid refresh token", func() { - ok, _ := c.Refresh(fakeUserRefreshInvalid) + ok, _ := c.Refresh(ctx, fakeUserRefreshInvalid) g.Assert(ok).IsFalse() }) }) g.Describe("When requesting a repository", func() { g.It("Should return the details", func() { - repo, err := c.Repo( - fakeUser, - fakeRepo.Owner, - fakeRepo.Name, - ) + repo, err := c.Repo(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name) g.Assert(err == nil).IsTrue() g.Assert(repo.FullName).Equal(fakeRepo.FullName) g.Assert(repo.Avatar).Equal(s.URL + fakeRepo.Avatar) @@ -130,57 +122,49 @@ func Test_coding(t *testing.T) { g.Assert(repo.IsPrivate).Equal(fakeRepo.IsPrivate) }) g.It("Should handle not found errors", func() { - _, err := c.Repo( - fakeUser, - fakeRepoNotFound.Owner, - fakeRepoNotFound.Name, - ) + _, err := c.Repo(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) g.Assert(err != nil).IsTrue() }) }) g.Describe("When requesting repository permissions", func() { g.It("Should authorize admin access for project owner", func() { - perm, err := c.Perm(fakeUser, "demo1", "perm_owner") + perm, err := c.Perm(ctx, fakeUser, "demo1", "perm_owner") g.Assert(err == nil).IsTrue() g.Assert(perm.Pull).IsTrue() g.Assert(perm.Push).IsTrue() g.Assert(perm.Admin).IsTrue() }) g.It("Should authorize admin access for project admin", func() { - perm, err := c.Perm(fakeUser, "demo1", "perm_admin") + perm, err := c.Perm(ctx, fakeUser, "demo1", "perm_admin") g.Assert(err == nil).IsTrue() g.Assert(perm.Pull).IsTrue() g.Assert(perm.Push).IsTrue() g.Assert(perm.Admin).IsTrue() }) g.It("Should authorize read access for project member", func() { - perm, err := c.Perm(fakeUser, "demo1", "perm_member") + perm, err := c.Perm(ctx, fakeUser, "demo1", "perm_member") g.Assert(err == nil).IsTrue() g.Assert(perm.Pull).IsTrue() g.Assert(perm.Push).IsTrue() g.Assert(perm.Admin).IsFalse() }) g.It("Should authorize no access for project guest", func() { - perm, err := c.Perm(fakeUser, "demo1", "perm_guest") + perm, err := c.Perm(ctx, fakeUser, "demo1", "perm_guest") g.Assert(err == nil).IsTrue() g.Assert(perm.Pull).IsFalse() g.Assert(perm.Push).IsFalse() g.Assert(perm.Admin).IsFalse() }) g.It("Should handle not found errors", func() { - _, err := c.Perm( - fakeUser, - fakeRepoNotFound.Owner, - fakeRepoNotFound.Name, - ) + _, err := c.Perm(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) g.Assert(err != nil).IsTrue() }) }) g.Describe("When downloading a file", func() { g.It("Should return file for specified build", func() { - data, err := c.File(fakeUser, fakeRepo, fakeBuild, ".drone.yml") + data, err := c.File(ctx, fakeUser, fakeRepo, fakeBuild, ".drone.yml") g.Assert(err == nil).IsTrue() g.Assert(string(data)).Equal("pipeline:\n test:\n image: golang:1.6\n commands:\n - go test\n") }) @@ -213,22 +197,22 @@ func Test_coding(t *testing.T) { g.Describe("When activating a repository", func() { g.It("Should create the hook", func() { - err := c.Activate(fakeUser, fakeRepo, "http://127.0.0.1") + err := c.Activate(ctx, fakeUser, fakeRepo, "http://127.0.0.1") g.Assert(err == nil).IsTrue() }) g.It("Should update the hook when exists", func() { - err := c.Activate(fakeUser, fakeRepo, "http://127.0.0.2") + err := c.Activate(ctx, fakeUser, fakeRepo, "http://127.0.0.2") g.Assert(err == nil).IsTrue() }) }) g.Describe("When deactivating a repository", func() { g.It("Should successfully remove hook", func() { - err := c.Deactivate(fakeUser, fakeRepo, "http://127.0.0.3") + err := c.Deactivate(ctx, fakeUser, fakeRepo, "http://127.0.0.3") g.Assert(err == nil).IsTrue() }) g.It("Should successfully deactivate when hook already removed", func() { - err := c.Deactivate(fakeUser, fakeRepo, "http://127.0.0.4") + err := c.Deactivate(ctx, fakeUser, fakeRepo, "http://127.0.0.4") g.Assert(err == nil).IsTrue() }) }) diff --git a/server/remote/coding/internal/coding.go b/server/remote/coding/internal/coding.go index 430241f4a..c883c7adb 100644 --- a/server/remote/coding/internal/coding.go +++ b/server/remote/coding/internal/coding.go @@ -15,6 +15,7 @@ package internal import ( + "context" "encoding/json" "fmt" "io/ioutil" @@ -29,6 +30,7 @@ type Client struct { token string agent string client *http.Client + ctx context.Context } type GenericAPIResponse struct { @@ -36,13 +38,14 @@ type GenericAPIResponse struct { Data json.RawMessage `json:"data,omitempty"` } -func NewClient(baseURL, apiPath, token, agent string, client *http.Client) *Client { +func NewClient(ctx context.Context, baseURL, apiPath, token, agent string, client *http.Client) *Client { return &Client{ baseURL: baseURL, apiPath: apiPath, token: token, agent: agent, client: client, + ctx: ctx, } } @@ -63,10 +66,10 @@ func (c *Client) Do(method, u string, params url.Values) ([]byte, error) { var req *http.Request var err error if method != "GET" { - req, err = http.NewRequest(method, rawURL+"?access_token="+c.token, strings.NewReader(params.Encode())) + req, err = http.NewRequestWithContext(c.ctx, method, rawURL+"?access_token="+c.token, strings.NewReader(params.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8") } else { - req, err = http.NewRequest("GET", rawURL+"?"+params.Encode(), nil) + req, err = http.NewRequestWithContext(c.ctx, "GET", rawURL+"?"+params.Encode(), nil) } if err != nil { return nil, fmt.Errorf("fail to create request for url %q: %v", rawURL, err) diff --git a/server/remote/gitea/gitea.go b/server/remote/gitea/gitea.go index 483319ee5..227a32b0d 100644 --- a/server/remote/gitea/gitea.go +++ b/server/remote/gitea/gitea.go @@ -91,7 +91,7 @@ func New(opts Opts) (remote.Remote, error) { // Login authenticates an account with Gitea using basic authentication. The // Gitea account details are returned when the user is successfully authenticated. -func (c *Gitea) Login(w http.ResponseWriter, req *http.Request) (*model.User, error) { +func (c *Gitea) Login(ctx context.Context, w http.ResponseWriter, req *http.Request) (*model.User, error) { config := &oauth2.Config{ ClientID: c.ClientID, ClientSecret: c.ClientSecret, @@ -123,7 +123,7 @@ func (c *Gitea) Login(w http.ResponseWriter, req *http.Request) (*model.User, er return nil, err } - client, err := c.newClientToken(token.AccessToken) + client, err := c.newClientToken(ctx, token.AccessToken) if err != nil { return nil, err } @@ -144,8 +144,8 @@ func (c *Gitea) Login(w http.ResponseWriter, req *http.Request) (*model.User, er // Auth uses the Gitea oauth2 access token and refresh token to authenticate // a session and return the Gitea account login. -func (c *Gitea) Auth(token, _ string) (string, error) { - client, err := c.newClientToken(token) +func (c *Gitea) Auth(ctx context.Context, token, secret string) (string, error) { + client, err := c.newClientToken(ctx, token) if err != nil { return "", err } @@ -158,7 +158,7 @@ func (c *Gitea) Auth(token, _ string) (string, error) { // Refresh refreshes the Gitea oauth2 access token. If the token is // refreshed the user is updated and a true value is returned. -func (c *Gitea) Refresh(user *model.User) (bool, error) { +func (c *Gitea) Refresh(ctx context.Context, user *model.User) (bool, error) { config := &oauth2.Config{ ClientID: c.ClientID, ClientSecret: c.ClientSecret, @@ -167,7 +167,7 @@ func (c *Gitea) Refresh(user *model.User) (bool, error) { TokenURL: fmt.Sprintf(accessTokenURL, c.URL), }, } - source := config.TokenSource(context.TODO(), &oauth2.Token{RefreshToken: user.Secret}) + source := config.TokenSource(ctx, &oauth2.Token{RefreshToken: user.Secret}) token, err := source.Token() if err != nil || len(token.AccessToken) == 0 { @@ -181,8 +181,8 @@ func (c *Gitea) Refresh(user *model.User) (bool, error) { } // Teams is supported by the Gitea driver. -func (c *Gitea) Teams(u *model.User) ([]*model.Team, error) { - client, err := c.newClientToken(u.Token) +func (c *Gitea) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) { + client, err := c.newClientToken(ctx, u.Token) if err != nil { return nil, err } @@ -222,8 +222,8 @@ func (c *Gitea) TeamPerm(u *model.User, org string) (*model.Perm, error) { } // Repo returns the named Gitea repository. -func (c *Gitea) Repo(u *model.User, owner, name string) (*model.Repo, error) { - client, err := c.newClientToken(u.Token) +func (c *Gitea) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) { + client, err := c.newClientToken(ctx, u.Token) if err != nil { return nil, err } @@ -240,10 +240,10 @@ func (c *Gitea) Repo(u *model.User, owner, name string) (*model.Repo, error) { // Repos returns a list of all repositories for the Gitea account, including // organization repositories. -func (c *Gitea) Repos(u *model.User) ([]*model.Repo, error) { +func (c *Gitea) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) { repos := make([]*model.Repo, 0, perPage) - client, err := c.newClientToken(u.Token) + client, err := c.newClientToken(ctx, u.Token) if err != nil { return nil, err } @@ -278,8 +278,8 @@ func (c *Gitea) Repos(u *model.User) ([]*model.Repo, error) { } // Perm returns the user permissions for the named Gitea repository. -func (c *Gitea) Perm(u *model.User, owner, name string) (*model.Perm, error) { - client, err := c.newClientToken(u.Token) +func (c *Gitea) Perm(ctx context.Context, u *model.User, owner, name string) (*model.Perm, error) { + client, err := c.newClientToken(ctx, u.Token) if err != nil { return nil, err } @@ -292,8 +292,8 @@ func (c *Gitea) Perm(u *model.User, owner, name string) (*model.Perm, error) { } // File fetches the file from the Gitea repository and returns its contents. -func (c *Gitea) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { - client, err := c.newClientToken(u.Token) +func (c *Gitea) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { + client, err := c.newClientToken(ctx, u.Token) if err != nil { return nil, err } @@ -302,10 +302,10 @@ func (c *Gitea) File(u *model.User, r *model.Repo, b *model.Build, f string) ([] return cfg, err } -func (c *Gitea) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { +func (c *Gitea) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { var configs []*remote.FileMeta - client, err := c.newClientToken(u.Token) + client, err := c.newClientToken(ctx, u.Token) if err != nil { return nil, err } @@ -321,7 +321,7 @@ func (c *Gitea) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]* for _, e := range tree.Entries { // Filter path matching pattern and type file (blob) if m, _ := filepath.Match(f, e.Path); m && e.Type == "blob" { - data, err := c.File(u, r, b, e.Path) + data, err := c.File(ctx, u, r, b, e.Path) if err != nil { return nil, fmt.Errorf("multi-pipeline cannot get %s: %s", e.Path, err) } @@ -337,8 +337,8 @@ func (c *Gitea) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]* } // Status is supported by the Gitea driver. -func (c *Gitea) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { - client, err := c.newClientToken(u.Token) +func (c *Gitea) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { + client, err := c.newClientToken(ctx, u.Token) if err != nil { return err } @@ -381,7 +381,7 @@ func (c *Gitea) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { // Activate activates the repository by registering post-commit hooks with // the Gitea repository. -func (c *Gitea) Activate(u *model.User, r *model.Repo, link string) error { +func (c *Gitea) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error { config := map[string]string{ "url": link, "secret": r.Hash, @@ -394,7 +394,7 @@ func (c *Gitea) Activate(u *model.User, r *model.Repo, link string) error { Active: true, } - client, err := c.newClientToken(u.Token) + client, err := c.newClientToken(ctx, u.Token) if err != nil { return err } @@ -404,8 +404,8 @@ func (c *Gitea) Activate(u *model.User, r *model.Repo, link string) error { // Deactivate deactives the repository be removing repository push hooks from // the Gitea repository. -func (c *Gitea) Deactivate(u *model.User, r *model.Repo, link string) error { - client, err := c.newClientToken(u.Token) +func (c *Gitea) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + client, err := c.newClientToken(ctx, u.Token) if err != nil { return err } @@ -431,14 +431,14 @@ func (c *Gitea) Hook(r *http.Request) (*model.Repo, *model.Build, error) { } // helper function to return the Gitea client with Token -func (c *Gitea) newClientToken(token string) (*gitea.Client, error) { +func (c *Gitea) newClientToken(ctx context.Context, token string) (*gitea.Client, error) { httpClient := &http.Client{} if c.SkipVerify { httpClient.Transport = &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } } - return gitea.NewClient(c.URL, gitea.SetToken(token), gitea.SetHTTPClient(httpClient)) + return gitea.NewClient(c.URL, gitea.SetToken(token), gitea.SetHTTPClient(httpClient), gitea.SetContext(ctx)) } const ( diff --git a/server/remote/gitea/gitea_test.go b/server/remote/gitea/gitea_test.go index e24e42d48..f6e9ef063 100644 --- a/server/remote/gitea/gitea_test.go +++ b/server/remote/gitea/gitea_test.go @@ -15,6 +15,7 @@ package gitea import ( + "context" "net/http/httptest" "testing" @@ -33,6 +34,7 @@ func Test_gitea(t *testing.T) { SkipVerify: true, }) + ctx := context.Background() g := goblin.Goblin(t) g.Describe("Gitea", func() { @@ -89,7 +91,7 @@ func Test_gitea(t *testing.T) { g.Describe("Requesting a repository", func() { g.It("Should return the repository details", func() { - repo, err := c.Repo(fakeUser, fakeRepo.Owner, fakeRepo.Name) + repo, err := c.Repo(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name) g.Assert(err == nil).IsTrue() g.Assert(repo.Owner).Equal(fakeRepo.Owner) g.Assert(repo.Name).Equal(fakeRepo.Name) @@ -99,57 +101,57 @@ func Test_gitea(t *testing.T) { g.Assert(repo.Link).Equal("http://localhost/test_name/repo_name") }) g.It("Should handle a not found error", func() { - _, err := c.Repo(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) + _, err := c.Repo(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) g.Assert(err != nil).IsTrue() }) }) g.Describe("Requesting repository permissions", func() { g.It("Should return the permission details", func() { - perm, err := c.Perm(fakeUser, fakeRepo.Owner, fakeRepo.Name) + perm, err := c.Perm(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name) g.Assert(err == nil).IsTrue() g.Assert(perm.Admin).IsTrue() g.Assert(perm.Push).IsTrue() g.Assert(perm.Pull).IsTrue() }) g.It("Should handle a not found error", func() { - _, err := c.Perm(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) + _, err := c.Perm(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) g.Assert(err != nil).IsTrue() }) }) g.Describe("Requesting a repository list", func() { g.It("Should return the repository list", func() { - repos, err := c.Repos(fakeUser) + repos, err := c.Repos(ctx, fakeUser) g.Assert(err == nil).IsTrue() g.Assert(repos[0].Owner).Equal(fakeRepo.Owner) g.Assert(repos[0].Name).Equal(fakeRepo.Name) g.Assert(repos[0].FullName).Equal(fakeRepo.Owner + "/" + fakeRepo.Name) }) g.It("Should handle a not found error", func() { - _, err := c.Repos(fakeUserNoRepos) + _, err := c.Repos(ctx, fakeUserNoRepos) g.Assert(err != nil).IsTrue() }) }) g.It("Should register repository hooks", func() { - err := c.Activate(fakeUser, fakeRepo, "http://localhost") + err := c.Activate(ctx, fakeUser, fakeRepo, "http://localhost") g.Assert(err == nil).IsTrue() }) g.It("Should remove repository hooks", func() { - err := c.Deactivate(fakeUser, fakeRepo, "http://localhost") + err := c.Deactivate(ctx, fakeUser, fakeRepo, "http://localhost") g.Assert(err == nil).IsTrue() }) g.It("Should return a repository file", func() { - raw, err := c.File(fakeUser, fakeRepo, fakeBuild, ".drone.yml") + raw, err := c.File(ctx, fakeUser, fakeRepo, fakeBuild, ".drone.yml") g.Assert(err == nil).IsTrue() g.Assert(string(raw)).Equal("{ platform: linux/amd64 }") }) g.It("Should return nil from send build status", func() { - err := c.Status(fakeUser, fakeRepo, fakeBuild, "http://gitea.io", nil) + err := c.Status(ctx, fakeUser, fakeRepo, fakeBuild, "http://gitea.io", nil) g.Assert(err == nil).IsTrue() }) diff --git a/server/remote/github/github.go b/server/remote/github/github.go index ae5274198..d94c61baf 100644 --- a/server/remote/github/github.go +++ b/server/remote/github/github.go @@ -15,6 +15,7 @@ package github import ( + "context" "crypto/tls" "fmt" "net" @@ -28,8 +29,8 @@ import ( "github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/remote" + // TODO upgrade to v39 to pass context down "github.com/google/go-github/github" - "golang.org/x/net/context" "golang.org/x/oauth2" ) @@ -82,9 +83,6 @@ func New(opts Opts) (remote.Remote, error) { r.API = r.URL + "/api/v3/" } - // Hack to enable oauth2 access in older GHE - // TODO: dont use deprecated func - oauth2.RegisterBrokenAuthHeaderProvider(r.URL) return r, nil } @@ -104,7 +102,7 @@ type client struct { } // Login authenticates the session and returns the remote user details. -func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) { +func (c *client) Login(ctx context.Context, res http.ResponseWriter, req *http.Request) (*model.User, error) { config := c.newConfig(req) // get the OAuth errors @@ -126,12 +124,12 @@ func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User, return nil, nil } - token, err := config.Exchange(c.newContext(), code) + token, err := config.Exchange(c.newContext(ctx), code) if err != nil { return nil, err } - client := c.newClientToken(token.AccessToken) + client := c.newClientToken(ctx, token.AccessToken) user, _, err := client.Users.Get("") if err != nil { return nil, err @@ -155,8 +153,8 @@ func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User, } // Auth returns the GitHub user login for the given access token. -func (c *client) Auth(token, secret string) (string, error) { - client := c.newClientToken(token) +func (c *client) Auth(ctx context.Context, token, secret string) (string, error) { + client := c.newClientToken(ctx, token) user, _, err := client.Users.Get("") if err != nil { return "", err @@ -165,8 +163,8 @@ func (c *client) Auth(token, secret string) (string, error) { } // Teams returns a list of all team membership for the GitHub account. -func (c *client) Teams(u *model.User) ([]*model.Team, error) { - client := c.newClientToken(u.Token) +func (c *client) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) { + client := c.newClientToken(ctx, u.Token) opts := new(github.ListOptions) opts.Page = 1 @@ -184,8 +182,8 @@ func (c *client) Teams(u *model.User) ([]*model.Team, error) { } // Repo returns the named GitHub repository. -func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) { - client := c.newClientToken(u.Token) +func (c *client) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) { + client := c.newClientToken(ctx, u.Token) repo, _, err := client.Repositories.Get(owner, name) if err != nil { return nil, err @@ -195,8 +193,8 @@ func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) { // Repos returns a list of all repositories for GitHub account, including // organization repositories. -func (c *client) Repos(u *model.User) ([]*model.Repo, error) { - client := c.newClientToken(u.Token) +func (c *client) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) { + client := c.newClientToken(ctx, u.Token) opts := new(github.RepositoryListOptions) opts.PerPage = 100 @@ -215,8 +213,8 @@ func (c *client) Repos(u *model.User) ([]*model.Repo, error) { } // Perm returns the user permissions for the named GitHub repository. -func (c *client) Perm(u *model.User, owner, name string) (*model.Perm, error) { - client := c.newClientToken(u.Token) +func (c *client) Perm(ctx context.Context, u *model.User, owner, name string) (*model.Perm, error) { + client := c.newClientToken(ctx, u.Token) repo, _, err := client.Repositories.Get(owner, name) if err != nil { return nil, err @@ -225,8 +223,8 @@ func (c *client) Perm(u *model.User, owner, name string) (*model.Perm, error) { } // File fetches the file from the GitHub repository and returns its contents. -func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { - client := c.newClientToken(u.Token) +func (c *client) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { + client := c.newClientToken(ctx, u.Token) opts := new(github.RepositoryContentGetOptions) opts.Ref = b.Commit @@ -240,8 +238,8 @@ func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([ return data.Decode() } -func (c *client) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { - client := c.newClientToken(u.Token) +func (c *client) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { + client := c.newClientToken(ctx, u.Token) opts := new(github.RepositoryContentGetOptions) opts.Ref = b.Commit @@ -255,7 +253,7 @@ func (c *client) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([] for _, file := range data { go func(path string) { - content, err := c.File(u, r, b, path) + content, err := c.File(ctx, u, r, b, path) if err != nil { errc <- err } else { @@ -305,8 +303,8 @@ func (c *client) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { // Deactivate deactives the repository be removing registered push hooks from // the GitHub repository. -func (c *client) Deactivate(u *model.User, r *model.Repo, link string) error { - client := c.newClientToken(u.Token) +func (c *client) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + client := c.newClientToken(ctx, u.Token) hooks, _, err := client.Repositories.ListHooks(r.Owner, r.Name, nil) if err != nil { return err @@ -321,11 +319,11 @@ func (c *client) Deactivate(u *model.User, r *model.Repo, link string) error { // helper function to return the GitHub oauth2 context using an HTTPClient that // disables TLS verification if disabled in the remote settings. -func (c *client) newContext() context.Context { +func (c *client) newContext(ctx context.Context) context.Context { if !c.SkipVerify { - return oauth2.NoContext + return ctx } - return context.WithValue(nil, oauth2.HTTPClient, &http.Client{ + return context.WithValue(ctx, oauth2.HTTPClient, &http.Client{ Transport: &http.Transport{ Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{ @@ -359,11 +357,11 @@ func (c *client) newConfig(req *http.Request) *oauth2.Config { } // helper function to return the GitHub oauth2 client -func (c *client) newClientToken(token string) *github.Client { +func (c *client) newClientToken(ctx context.Context, token string) *github.Client { ts := oauth2.StaticTokenSource( &oauth2.Token{AccessToken: token}, ) - tc := oauth2.NewClient(oauth2.NoContext, ts) + tc := oauth2.NewClient(ctx, ts) if c.SkipVerify { tc.Transport.(*oauth2.Transport).Base = &http.Transport{ Proxy: http.ProxyFromEnvironment, @@ -427,8 +425,8 @@ func matchingHooks(hooks []github.Hook, rawurl string) *github.Hook { // Status sends the commit status to the remote system. // An example would be the GitHub pull request status. -func (c *client) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { - client := c.newClientToken(u.Token) +func (c *client) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { + client := c.newClientToken(ctx, u.Token) switch b.Event { case "deployment": return deploymentStatus(client, r, b, link) @@ -486,11 +484,11 @@ func deploymentStatus(client *github.Client, r *model.Repo, b *model.Build, link // Activate activates a repository by creating the post-commit hook and // adding the SSH deploy key, if applicable. -func (c *client) Activate(u *model.User, r *model.Repo, link string) error { - if err := c.Deactivate(u, r, link); err != nil { +func (c *client) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + if err := c.Deactivate(ctx, u, r, link); err != nil { return err } - client := c.newClientToken(u.Token) + client := c.newClientToken(ctx, u.Token) hook := &github.Hook{ Name: github.String("web"), Events: []string{ diff --git a/server/remote/github/github_test.go b/server/remote/github/github_test.go index e6edda99a..6ea3ee982 100644 --- a/server/remote/github/github_test.go +++ b/server/remote/github/github_test.go @@ -15,6 +15,7 @@ package github import ( + "context" "net/http/httptest" "testing" @@ -34,6 +35,7 @@ func Test_github(t *testing.T) { SkipVerify: true, }) + ctx := context.Background() g := goblin.Goblin(t) g.Describe("GitHub", func() { @@ -95,7 +97,7 @@ func Test_github(t *testing.T) { g.Describe("Requesting a repository", func() { g.It("Should return the repository details", func() { - repo, err := c.Repo(fakeUser, fakeRepo.Owner, fakeRepo.Name) + repo, err := c.Repo(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name) g.Assert(err == nil).IsTrue() g.Assert(repo.Owner).Equal(fakeRepo.Owner) g.Assert(repo.Name).Equal(fakeRepo.Name) @@ -105,21 +107,21 @@ func Test_github(t *testing.T) { g.Assert(repo.Link).Equal(fakeRepo.Link) }) g.It("Should handle a not found error", func() { - _, err := c.Repo(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) + _, err := c.Repo(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) g.Assert(err != nil).IsTrue() }) }) g.Describe("Requesting repository permissions", func() { g.It("Should return the permission details", func() { - perm, err := c.Perm(fakeUser, fakeRepo.Owner, fakeRepo.Name) + perm, err := c.Perm(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name) g.Assert(err == nil).IsTrue() g.Assert(perm.Admin).IsTrue() g.Assert(perm.Push).IsTrue() g.Assert(perm.Pull).IsTrue() }) g.It("Should handle a not found error", func() { - _, err := c.Perm(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) + _, err := c.Perm(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) g.Assert(err != nil).IsTrue() }) }) diff --git a/server/remote/gitlab/gitlab.go b/server/remote/gitlab/gitlab.go index 32737fcd6..d854d3506 100644 --- a/server/remote/gitlab/gitlab.go +++ b/server/remote/gitlab/gitlab.go @@ -15,6 +15,7 @@ package gitlab import ( + "context" "crypto/tls" "fmt" "io/ioutil" @@ -113,7 +114,7 @@ func Load(config string) *Gitlab { // Login authenticates the session and returns the // remote user details. -func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) { +func (g *Gitlab) Login(ctx context.Context, res http.ResponseWriter, req *http.Request) (*model.User, error) { var config = &oauth2.Config{ ClientId: g.Client, @@ -193,7 +194,7 @@ func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User, return user, nil } -func (g *Gitlab) Auth(token, secret string) (string, error) { +func (g *Gitlab) Auth(ctx context.Context, token, secret string) (string, error) { client := NewClient(g.URL, token, g.SkipVerify) login, err := client.CurrentUser() if err != nil { @@ -202,7 +203,7 @@ func (g *Gitlab) Auth(token, secret string) (string, error) { return login.Username, nil } -func (g *Gitlab) Teams(u *model.User) ([]*model.Team, error) { +func (g *Gitlab) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) { client := NewClient(g.URL, u.Token, g.SkipVerify) groups, err := client.AllGroups() if err != nil { @@ -218,7 +219,7 @@ func (g *Gitlab) Teams(u *model.User) ([]*model.Team, error) { } // Repo fetches the named repository from the remote system. -func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) { +func (g *Gitlab) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) { client := NewClient(g.URL, u.Token, g.SkipVerify) id, err := GetProjectId(g, client, owner, name) if err != nil { @@ -257,7 +258,7 @@ func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) { } // Repos fetches a list of repos from the remote system. -func (g *Gitlab) Repos(u *model.User) ([]*model.Repo, error) { +func (g *Gitlab) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) { client := NewClient(g.URL, u.Token, g.SkipVerify) var repos = []*model.Repo{} @@ -297,7 +298,7 @@ func (g *Gitlab) Repos(u *model.User) ([]*model.Repo, error) { } // Perm fetches the named repository from the remote system. -func (g *Gitlab) Perm(u *model.User, owner, name string) (*model.Perm, error) { +func (g *Gitlab) Perm(ctx context.Context, u *model.User, owner, name string) (*model.Perm, error) { client := NewClient(g.URL, u.Token, g.SkipVerify) id, err := GetProjectId(g, client, owner, name) @@ -324,35 +325,35 @@ func (g *Gitlab) Perm(u *model.User, owner, name string) (*model.Perm, error) { } // File fetches a file from the remote repository and returns in string format. -func (g *Gitlab) File(user *model.User, repo *model.Repo, build *model.Build, f string) ([]byte, error) { - var client = NewClient(g.URL, user.Token, g.SkipVerify) - id, err := GetProjectId(g, client, repo.Owner, repo.Name) +func (g *Gitlab) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { + var client = NewClient(g.URL, u.Token, g.SkipVerify) + id, err := GetProjectId(g, client, r.Owner, r.Name) if err != nil { return nil, err } - out, err := client.RepoRawFileRef(id, build.Commit, f) + out, err := client.RepoRawFileRef(id, b.Commit, f) if err != nil { return nil, err } return out, err } -func (c *Gitlab) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { +func (c *Gitlab) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { return nil, fmt.Errorf("Not implemented") } // NOTE Currently gitlab doesn't support status for commits and events, // also if we want get MR status in gitlab we need implement a special plugin for gitlab, // gitlab uses API to fetch build status on client side. But for now we skip this. -func (g *Gitlab) Status(u *model.User, repo *model.Repo, b *model.Build, link string, proc *model.Proc) error { +func (g *Gitlab) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { client := NewClient(g.URL, u.Token, g.SkipVerify) status := getStatus(b.Status) desc := getDesc(b.Status) client.SetStatus( - ns(repo.Owner, repo.Name), + ns(r.Owner, r.Name), b.Commit, status, desc, @@ -407,9 +408,9 @@ func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { // Activate activates a repository by adding a Post-commit hook and // a Public Deploy key, if applicable. -func (g *Gitlab) Activate(user *model.User, repo *model.Repo, link string) error { - var client = NewClient(g.URL, user.Token, g.SkipVerify) - id, err := GetProjectId(g, client, repo.Owner, repo.Name) +func (g *Gitlab) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + var client = NewClient(g.URL, u.Token, g.SkipVerify) + id, err := GetProjectId(g, client, r.Owner, r.Name) if err != nil { return err } @@ -432,9 +433,9 @@ func (g *Gitlab) Activate(user *model.User, repo *model.Repo, link string) error // Deactivate removes a repository by removing all the post-commit hooks // which are equal to link and removing the SSH deploy key. -func (g *Gitlab) Deactivate(user *model.User, repo *model.Repo, link string) error { - var client = NewClient(g.URL, user.Token, g.SkipVerify) - id, err := GetProjectId(g, client, repo.Owner, repo.Name) +func (g *Gitlab) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + var client = NewClient(g.URL, u.Token, g.SkipVerify) + id, err := GetProjectId(g, client, r.Owner, r.Name) if err != nil { return err } diff --git a/server/remote/gitlab/gitlab_test.go b/server/remote/gitlab/gitlab_test.go index 8bcfb9a6e..341e4afe0 100644 --- a/server/remote/gitlab/gitlab_test.go +++ b/server/remote/gitlab/gitlab_test.go @@ -16,6 +16,7 @@ package gitlab import ( "bytes" + "context" "net/http" "testing" @@ -43,13 +44,14 @@ func Test_Gitlab(t *testing.T) { Owner: "diaspora", } + ctx := context.Background() g := goblin.Goblin(t) g.Describe("Gitlab Plugin", func() { // Test projects method g.Describe("AllProjects", func() { g.It("Should return only non-archived projects is hidden", func() { gitlab.HideArchives = true - _projects, err := gitlab.Repos(&user) + _projects, err := gitlab.Repos(ctx, &user) g.Assert(err == nil).IsTrue() g.Assert(len(_projects)).Equal(1) @@ -57,7 +59,7 @@ func Test_Gitlab(t *testing.T) { g.It("Should return all the projects", func() { gitlab.HideArchives = false - _projects, err := gitlab.Repos(&user) + _projects, err := gitlab.Repos(ctx, &user) g.Assert(err == nil).IsTrue() g.Assert(len(_projects)).Equal(2) @@ -67,7 +69,7 @@ func Test_Gitlab(t *testing.T) { // Test repository method g.Describe("Repo", func() { g.It("Should return valid repo", func() { - _repo, err := gitlab.Repo(&user, "diaspora", "diaspora-client") + _repo, err := gitlab.Repo(ctx, &user, "diaspora", "diaspora-client") g.Assert(err == nil).IsTrue() g.Assert(_repo.Name).Equal("diaspora-client") @@ -76,7 +78,7 @@ func Test_Gitlab(t *testing.T) { }) g.It("Should return error, when repo not exist", func() { - _, err := gitlab.Repo(&user, "not-existed", "not-existed") + _, err := gitlab.Repo(ctx, &user, "not-existed", "not-existed") g.Assert(err != nil).IsTrue() }) @@ -85,21 +87,21 @@ func Test_Gitlab(t *testing.T) { // Test permissions method g.Describe("Perm", func() { g.It("Should return repo permissions", func() { - perm, err := gitlab.Perm(&user, "diaspora", "diaspora-client") + perm, err := gitlab.Perm(ctx, &user, "diaspora", "diaspora-client") g.Assert(err == nil).IsTrue() g.Assert(perm.Admin).Equal(true) g.Assert(perm.Pull).Equal(true) g.Assert(perm.Push).Equal(true) }) g.It("Should return repo permissions when user is admin", func() { - perm, err := gitlab.Perm(&user, "brightbox", "puppet") + perm, err := gitlab.Perm(ctx, &user, "brightbox", "puppet") g.Assert(err == nil).IsTrue() g.Assert(perm.Admin).Equal(true) g.Assert(perm.Pull).Equal(true) g.Assert(perm.Push).Equal(true) }) g.It("Should return error, when repo is not exist", func() { - _, err := gitlab.Perm(&user, "not-existed", "not-existed") + _, err := gitlab.Perm(ctx, &user, "not-existed", "not-existed") g.Assert(err != nil).IsTrue() }) @@ -108,13 +110,13 @@ func Test_Gitlab(t *testing.T) { // Test activate method g.Describe("Activate", func() { g.It("Should be success", func() { - err := gitlab.Activate(&user, &repo, "http://example.com/api/hook/test/test?access_token=token") + err := gitlab.Activate(ctx, &user, &repo, "http://example.com/api/hook/test/test?access_token=token") g.Assert(err == nil).IsTrue() }) g.It("Should be failed, when token not given", func() { - err := gitlab.Activate(&user, &repo, "http://example.com/api/hook/test/test") + err := gitlab.Activate(ctx, &user, &repo, "http://example.com/api/hook/test/test") g.Assert(err != nil).IsTrue() }) @@ -123,7 +125,7 @@ func Test_Gitlab(t *testing.T) { // Test deactivate method g.Describe("Deactivate", func() { g.It("Should be success", func() { - err := gitlab.Deactivate(&user, &repo, "http://example.com/api/hook/test/test?access_token=token") + err := gitlab.Deactivate(ctx, &user, &repo, "http://example.com/api/hook/test/test?access_token=token") g.Assert(err == nil).IsTrue() }) diff --git a/server/remote/gitlab3/gitlab.go b/server/remote/gitlab3/gitlab.go index d747827a2..c97358804 100644 --- a/server/remote/gitlab3/gitlab.go +++ b/server/remote/gitlab3/gitlab.go @@ -15,6 +15,7 @@ package gitlab3 import ( + "context" "crypto/tls" "fmt" "io/ioutil" @@ -113,7 +114,7 @@ func Load(config string) *Gitlab { // Login authenticates the session and returns the // remote user details. -func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) { +func (g *Gitlab) Login(ctx context.Context, res http.ResponseWriter, req *http.Request) (*model.User, error) { var config = &oauth2.Config{ ClientId: g.Client, @@ -193,7 +194,7 @@ func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User, return user, nil } -func (g *Gitlab) Auth(token, secret string) (string, error) { +func (g *Gitlab) Auth(ctx context.Context, token, secret string) (string, error) { client := NewClient(g.URL, token, g.SkipVerify) login, err := client.CurrentUser() if err != nil { @@ -202,7 +203,7 @@ func (g *Gitlab) Auth(token, secret string) (string, error) { return login.Username, nil } -func (g *Gitlab) Teams(u *model.User) ([]*model.Team, error) { +func (g *Gitlab) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) { client := NewClient(g.URL, u.Token, g.SkipVerify) groups, err := client.AllGroups() if err != nil { @@ -218,7 +219,7 @@ func (g *Gitlab) Teams(u *model.User) ([]*model.Team, error) { } // Repo fetches the named repository from the remote system. -func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) { +func (g *Gitlab) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) { client := NewClient(g.URL, u.Token, g.SkipVerify) id, err := GetProjectId(g, client, owner, name) if err != nil { @@ -257,7 +258,7 @@ func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) { } // Repos fetches a list of repos from the remote system. -func (g *Gitlab) Repos(u *model.User) ([]*model.Repo, error) { +func (g *Gitlab) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) { client := NewClient(g.URL, u.Token, g.SkipVerify) var repos = []*model.Repo{} @@ -297,7 +298,7 @@ func (g *Gitlab) Repos(u *model.User) ([]*model.Repo, error) { } // Perm fetches the named repository from the remote system. -func (g *Gitlab) Perm(u *model.User, owner, name string) (*model.Perm, error) { +func (g *Gitlab) Perm(ctx context.Context, u *model.User, owner, name string) (*model.Perm, error) { client := NewClient(g.URL, u.Token, g.SkipVerify) id, err := GetProjectId(g, client, owner, name) @@ -324,7 +325,7 @@ func (g *Gitlab) Perm(u *model.User, owner, name string) (*model.Perm, error) { } // File fetches a file from the remote repository and returns in string format. -func (g *Gitlab) File(user *model.User, repo *model.Repo, build *model.Build, f string) ([]byte, error) { +func (g *Gitlab) File(ctx context.Context, user *model.User, repo *model.Repo, build *model.Build, f string) ([]byte, error) { var client = NewClient(g.URL, user.Token, g.SkipVerify) id, err := GetProjectId(g, client, repo.Owner, repo.Name) if err != nil { @@ -338,21 +339,21 @@ func (g *Gitlab) File(user *model.User, repo *model.Repo, build *model.Build, f return out, err } -func (c *Gitlab) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { +func (c *Gitlab) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { return nil, fmt.Errorf("Not implemented") } // NOTE Currently gitlab doesn't support status for commits and events, // also if we want get MR status in gitlab we need implement a special plugin for gitlab, // gitlab uses API to fetch build status on client side. But for now we skip this. -func (g *Gitlab) Status(u *model.User, repo *model.Repo, b *model.Build, link string, proc *model.Proc) error { +func (g *Gitlab) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { client := NewClient(g.URL, u.Token, g.SkipVerify) status := getStatus(b.Status) desc := getDesc(b.Status) client.SetStatus( - ns(repo.Owner, repo.Name), + ns(r.Owner, r.Name), b.Commit, status, desc, @@ -407,9 +408,9 @@ func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { // Activate activates a repository by adding a Post-commit hook and // a Public Deploy key, if applicable. -func (g *Gitlab) Activate(user *model.User, repo *model.Repo, link string) error { - var client = NewClient(g.URL, user.Token, g.SkipVerify) - id, err := GetProjectId(g, client, repo.Owner, repo.Name) +func (g *Gitlab) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + var client = NewClient(g.URL, u.Token, g.SkipVerify) + id, err := GetProjectId(g, client, r.Owner, r.Name) if err != nil { return err } @@ -432,9 +433,9 @@ func (g *Gitlab) Activate(user *model.User, repo *model.Repo, link string) error // Deactivate removes a repository by removing all the post-commit hooks // which are equal to link and removing the SSH deploy key. -func (g *Gitlab) Deactivate(user *model.User, repo *model.Repo, link string) error { - var client = NewClient(g.URL, user.Token, g.SkipVerify) - id, err := GetProjectId(g, client, repo.Owner, repo.Name) +func (g *Gitlab) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + var client = NewClient(g.URL, u.Token, g.SkipVerify) + id, err := GetProjectId(g, client, r.Owner, r.Name) if err != nil { return err } diff --git a/server/remote/gitlab3/gitlab_test.go b/server/remote/gitlab3/gitlab_test.go index a181f05e9..c45955762 100644 --- a/server/remote/gitlab3/gitlab_test.go +++ b/server/remote/gitlab3/gitlab_test.go @@ -49,7 +49,7 @@ func Test_Gitlab(t *testing.T) { g.Describe("AllProjects", func() { g.It("Should return only non-archived projects is hidden", func() { gitlab.HideArchives = true - _projects, err := gitlab.Repos(&user) + _projects, err := gitlab.Repos(nil, &user) g.Assert(err == nil).IsTrue() g.Assert(len(_projects)).Equal(1) @@ -57,7 +57,7 @@ func Test_Gitlab(t *testing.T) { g.It("Should return all the projects", func() { gitlab.HideArchives = false - _projects, err := gitlab.Repos(&user) + _projects, err := gitlab.Repos(nil, &user) g.Assert(err == nil).IsTrue() g.Assert(len(_projects)).Equal(2) @@ -67,7 +67,7 @@ func Test_Gitlab(t *testing.T) { // Test repository method g.Describe("Repo", func() { g.It("Should return valid repo", func() { - _repo, err := gitlab.Repo(&user, "diaspora", "diaspora-client") + _repo, err := gitlab.Repo(nil, &user, "diaspora", "diaspora-client") g.Assert(err == nil).IsTrue() g.Assert(_repo.Name).Equal("diaspora-client") @@ -76,7 +76,7 @@ func Test_Gitlab(t *testing.T) { }) g.It("Should return error, when repo not exist", func() { - _, err := gitlab.Repo(&user, "not-existed", "not-existed") + _, err := gitlab.Repo(nil, &user, "not-existed", "not-existed") g.Assert(err != nil).IsTrue() }) @@ -85,21 +85,21 @@ func Test_Gitlab(t *testing.T) { // Test permissions method g.Describe("Perm", func() { g.It("Should return repo permissions", func() { - perm, err := gitlab.Perm(&user, "diaspora", "diaspora-client") + perm, err := gitlab.Perm(nil, &user, "diaspora", "diaspora-client") g.Assert(err == nil).IsTrue() g.Assert(perm.Admin).Equal(true) g.Assert(perm.Pull).Equal(true) g.Assert(perm.Push).Equal(true) }) g.It("Should return repo permissions when user is admin", func() { - perm, err := gitlab.Perm(&user, "brightbox", "puppet") + perm, err := gitlab.Perm(nil, &user, "brightbox", "puppet") g.Assert(err == nil).IsTrue() g.Assert(perm.Admin).Equal(true) g.Assert(perm.Pull).Equal(true) g.Assert(perm.Push).Equal(true) }) g.It("Should return error, when repo is not exist", func() { - _, err := gitlab.Perm(&user, "not-existed", "not-existed") + _, err := gitlab.Perm(nil, &user, "not-existed", "not-existed") g.Assert(err != nil).IsTrue() }) @@ -108,13 +108,13 @@ func Test_Gitlab(t *testing.T) { // Test activate method g.Describe("Activate", func() { g.It("Should be success", func() { - err := gitlab.Activate(&user, &repo, "http://example.com/api/hook/test/test?access_token=token") + err := gitlab.Activate(nil, &user, &repo, "http://example.com/api/hook/test/test?access_token=token") g.Assert(err == nil).IsTrue() }) g.It("Should be failed, when token not given", func() { - err := gitlab.Activate(&user, &repo, "http://example.com/api/hook/test/test") + err := gitlab.Activate(nil, &user, &repo, "http://example.com/api/hook/test/test") g.Assert(err != nil).IsTrue() }) @@ -123,7 +123,7 @@ func Test_Gitlab(t *testing.T) { // Test deactivate method g.Describe("Deactivate", func() { g.It("Should be success", func() { - err := gitlab.Deactivate(&user, &repo, "http://example.com/api/hook/test/test?access_token=token") + err := gitlab.Deactivate(nil, &user, &repo, "http://example.com/api/hook/test/test?access_token=token") g.Assert(err == nil).IsTrue() }) diff --git a/server/remote/gogs/gogs.go b/server/remote/gogs/gogs.go index 2a96fb3fb..c772f9d3b 100644 --- a/server/remote/gogs/gogs.go +++ b/server/remote/gogs/gogs.go @@ -15,6 +15,7 @@ package gogs import ( + "context" "crypto/tls" "fmt" "net" @@ -68,7 +69,7 @@ func New(opts Opts) (remote.Remote, error) { // Login authenticates an account with Gogs using basic authentication. The // Gogs account details are returned when the user is successfully authenticated. -func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) { +func (c *client) Login(ctx context.Context, res http.ResponseWriter, req *http.Request) (*model.User, error) { var ( username = req.FormValue("username") password = req.FormValue("password") @@ -122,12 +123,12 @@ func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User, } // Auth is not supported by the Gogs driver. -func (c *client) Auth(token, secret string) (string, error) { +func (c *client) Auth(ctx context.Context, token, secret string) (string, error) { return "", fmt.Errorf("Not Implemented") } // Teams is not supported by the Gogs driver. -func (c *client) Teams(u *model.User) ([]*model.Team, error) { +func (c *client) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) { client := c.newClientToken(u.Token) orgs, err := client.ListMyOrgs() if err != nil { @@ -142,7 +143,7 @@ func (c *client) Teams(u *model.User) ([]*model.Team, error) { } // Repo returns the named Gogs repository. -func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) { +func (c *client) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) { client := c.newClientToken(u.Token) repo, err := client.GetRepo(owner, name) if err != nil { @@ -153,7 +154,7 @@ func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) { // Repos returns a list of all repositories for the Gogs account, including // organization repositories. -func (c *client) Repos(u *model.User) ([]*model.Repo, error) { +func (c *client) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) { repos := []*model.Repo{} client := c.newClientToken(u.Token) @@ -169,7 +170,7 @@ func (c *client) Repos(u *model.User) ([]*model.Repo, error) { } // Perm returns the user permissions for the named Gogs repository. -func (c *client) Perm(u *model.User, owner, name string) (*model.Perm, error) { +func (c *client) Perm(ctx context.Context, u *model.User, owner, name string) (*model.Perm, error) { client := c.newClientToken(u.Token) repo, err := client.GetRepo(owner, name) if err != nil { @@ -179,7 +180,7 @@ func (c *client) Perm(u *model.User, owner, name string) (*model.Perm, error) { } // File fetches the file from the Gogs repository and returns its contents. -func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { +func (c *client) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { client := c.newClientToken(u.Token) ref := b.Commit @@ -202,12 +203,12 @@ func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([ return cfg, err } -func (c *client) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { +func (c *client) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { return nil, fmt.Errorf("Not implemented") } // Status is not supported by the Gogs driver. -func (c *client) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { +func (c *client) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { return nil } @@ -231,7 +232,7 @@ func (c *client) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { // Activate activates the repository by registering post-commit hooks with // the Gogs repository. -func (c *client) Activate(u *model.User, r *model.Repo, link string) error { +func (c *client) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error { config := map[string]string{ "url": link, "secret": r.Hash, @@ -250,7 +251,7 @@ func (c *client) Activate(u *model.User, r *model.Repo, link string) error { } // Deactivate is not supported by the Gogs driver. -func (c *client) Deactivate(u *model.User, r *model.Repo, link string) error { +func (c *client) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error { return nil } diff --git a/server/remote/gogs/gogs_test.go b/server/remote/gogs/gogs_test.go index 4076b9d4d..298b466df 100644 --- a/server/remote/gogs/gogs_test.go +++ b/server/remote/gogs/gogs_test.go @@ -15,6 +15,7 @@ package gogs import ( + "context" "net/http/httptest" "testing" @@ -34,6 +35,7 @@ func Test_gogs(t *testing.T) { SkipVerify: true, }) + ctx := context.Background() g := goblin.Goblin(t) g.Describe("Gogs", func() { @@ -88,7 +90,7 @@ func Test_gogs(t *testing.T) { g.Describe("Requesting a repository", func() { g.It("Should return the repository details", func() { - repo, err := c.Repo(fakeUser, fakeRepo.Owner, fakeRepo.Name) + repo, err := c.Repo(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name) g.Assert(err == nil).IsTrue() g.Assert(repo.Owner).Equal(fakeRepo.Owner) g.Assert(repo.Name).Equal(fakeRepo.Name) @@ -98,52 +100,52 @@ func Test_gogs(t *testing.T) { g.Assert(repo.Link).Equal("http://localhost/test_name/repo_name") }) g.It("Should handle a not found error", func() { - _, err := c.Repo(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) + _, err := c.Repo(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) g.Assert(err != nil).IsTrue() }) }) g.Describe("Requesting repository permissions", func() { g.It("Should return the permission details", func() { - perm, err := c.Perm(fakeUser, fakeRepo.Owner, fakeRepo.Name) + perm, err := c.Perm(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name) g.Assert(err == nil).IsTrue() g.Assert(perm.Admin).IsTrue() g.Assert(perm.Push).IsTrue() g.Assert(perm.Pull).IsTrue() }) g.It("Should handle a not found error", func() { - _, err := c.Perm(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) + _, err := c.Perm(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) g.Assert(err != nil).IsTrue() }) }) g.Describe("Requesting a repository list", func() { g.It("Should return the repository list", func() { - repos, err := c.Repos(fakeUser) + repos, err := c.Repos(ctx, fakeUser) g.Assert(err == nil).IsTrue() g.Assert(repos[0].Owner).Equal(fakeRepo.Owner) g.Assert(repos[0].Name).Equal(fakeRepo.Name) g.Assert(repos[0].FullName).Equal(fakeRepo.Owner + "/" + fakeRepo.Name) }) g.It("Should handle a not found error", func() { - _, err := c.Repos(fakeUserNoRepos) + _, err := c.Repos(ctx, fakeUserNoRepos) g.Assert(err != nil).IsTrue() }) }) g.It("Should register repositroy hooks", func() { - err := c.Activate(fakeUser, fakeRepo, "http://localhost") + err := c.Activate(ctx, fakeUser, fakeRepo, "http://localhost") g.Assert(err == nil).IsTrue() }) g.It("Should return a repository file", func() { - raw, err := c.File(fakeUser, fakeRepo, fakeBuild, ".drone.yml") + raw, err := c.File(ctx, fakeUser, fakeRepo, fakeBuild, ".drone.yml") g.Assert(err == nil).IsTrue() g.Assert(string(raw)).Equal("{ platform: linux/amd64 }") }) g.It("Should return a repository file from a ref", func() { - raw, err := c.File(fakeUser, fakeRepo, fakeBuildWithRef, ".drone.yml") + raw, err := c.File(ctx, fakeUser, fakeRepo, fakeBuildWithRef, ".drone.yml") g.Assert(err == nil).IsTrue() g.Assert(string(raw)).Equal("{ platform: linux/amd64 }") }) @@ -162,9 +164,9 @@ func Test_gogs(t *testing.T) { }) g.It("Should return no-op for usupporeted features", func() { - _, err1 := c.Auth("octocat", "4vyW6b49Z") - err2 := c.Status(nil, nil, nil, "", nil) - err3 := c.Deactivate(nil, nil, "") + _, err1 := c.Auth(ctx, "octocat", "4vyW6b49Z") + err2 := c.Status(ctx, nil, nil, nil, "", nil) + err3 := c.Deactivate(ctx, nil, nil, "") g.Assert(err1 != nil).IsTrue() g.Assert(err2 == nil).IsTrue() g.Assert(err3 == nil).IsTrue() diff --git a/server/remote/mocks/Remote.go b/server/remote/mocks/Remote.go deleted file mode 100644 index bfcac213f..000000000 --- a/server/remote/mocks/Remote.go +++ /dev/null @@ -1,296 +0,0 @@ -// Code generated by mockery v0.0.0-dev. DO NOT EDIT. - -package mocks - -import ( - http "net/http" - - mock "github.com/stretchr/testify/mock" - model "github.com/woodpecker-ci/woodpecker/server/model" - - remote "github.com/woodpecker-ci/woodpecker/server/remote" -) - -// Remote is an autogenerated mock type for the Remote type -type Remote struct { - mock.Mock -} - -// Activate provides a mock function with given fields: u, r, link -func (_m *Remote) Activate(u *model.User, r *model.Repo, link string) error { - ret := _m.Called(u, r, link) - - var r0 error - if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, string) error); ok { - r0 = rf(u, r, link) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Auth provides a mock function with given fields: token, secret -func (_m *Remote) Auth(token string, secret string) (string, error) { - ret := _m.Called(token, secret) - - var r0 string - if rf, ok := ret.Get(0).(func(string, string) string); ok { - r0 = rf(token, secret) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(token, secret) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Deactivate provides a mock function with given fields: u, r, link -func (_m *Remote) Deactivate(u *model.User, r *model.Repo, link string) error { - ret := _m.Called(u, r, link) - - var r0 error - if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, string) error); ok { - r0 = rf(u, r, link) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Dir provides a mock function with given fields: u, r, b, f -func (_m *Remote) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { - ret := _m.Called(u, r, b, f) - - var r0 []*remote.FileMeta - if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, *model.Build, string) []*remote.FileMeta); ok { - r0 = rf(u, r, b, f) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*remote.FileMeta) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(*model.User, *model.Repo, *model.Build, string) error); ok { - r1 = rf(u, r, b, f) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// File provides a mock function with given fields: u, r, b, f -func (_m *Remote) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { - ret := _m.Called(u, r, b, f) - - var r0 []byte - if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, *model.Build, string) []byte); ok { - r0 = rf(u, r, b, f) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(*model.User, *model.Repo, *model.Build, string) error); ok { - r1 = rf(u, r, b, f) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Hook provides a mock function with given fields: r -func (_m *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) { - ret := _m.Called(r) - - var r0 *model.Repo - if rf, ok := ret.Get(0).(func(*http.Request) *model.Repo); ok { - r0 = rf(r) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.Repo) - } - } - - var r1 *model.Build - if rf, ok := ret.Get(1).(func(*http.Request) *model.Build); ok { - r1 = rf(r) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(*model.Build) - } - } - - var r2 error - if rf, ok := ret.Get(2).(func(*http.Request) error); ok { - r2 = rf(r) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// Login provides a mock function with given fields: w, r -func (_m *Remote) Login(w http.ResponseWriter, r *http.Request) (*model.User, error) { - ret := _m.Called(w, r) - - var r0 *model.User - if rf, ok := ret.Get(0).(func(http.ResponseWriter, *http.Request) *model.User); ok { - r0 = rf(w, r) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.User) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(http.ResponseWriter, *http.Request) error); ok { - r1 = rf(w, r) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Netrc provides a mock function with given fields: u, r -func (_m *Remote) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { - ret := _m.Called(u, r) - - var r0 *model.Netrc - if rf, ok := ret.Get(0).(func(*model.User, *model.Repo) *model.Netrc); ok { - r0 = rf(u, r) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.Netrc) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(*model.User, *model.Repo) error); ok { - r1 = rf(u, r) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Perm provides a mock function with given fields: u, owner, repo -func (_m *Remote) Perm(u *model.User, owner string, repo string) (*model.Perm, error) { - ret := _m.Called(u, owner, repo) - - var r0 *model.Perm - if rf, ok := ret.Get(0).(func(*model.User, string, string) *model.Perm); ok { - r0 = rf(u, owner, repo) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.Perm) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(*model.User, string, string) error); ok { - r1 = rf(u, owner, repo) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Repo provides a mock function with given fields: u, owner, repo -func (_m *Remote) Repo(u *model.User, owner string, repo string) (*model.Repo, error) { - ret := _m.Called(u, owner, repo) - - var r0 *model.Repo - if rf, ok := ret.Get(0).(func(*model.User, string, string) *model.Repo); ok { - r0 = rf(u, owner, repo) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.Repo) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(*model.User, string, string) error); ok { - r1 = rf(u, owner, repo) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Repos provides a mock function with given fields: u -func (_m *Remote) Repos(u *model.User) ([]*model.Repo, error) { - ret := _m.Called(u) - - var r0 []*model.Repo - if rf, ok := ret.Get(0).(func(*model.User) []*model.Repo); ok { - r0 = rf(u) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*model.Repo) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(*model.User) error); ok { - r1 = rf(u) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Status provides a mock function with given fields: u, r, b, link, proc -func (_m *Remote) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { - ret := _m.Called(u, r, b, link, proc) - - var r0 error - if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, *model.Build, string, *model.Proc) error); ok { - r0 = rf(u, r, b, link, proc) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Teams provides a mock function with given fields: u -func (_m *Remote) Teams(u *model.User) ([]*model.Team, error) { - ret := _m.Called(u) - - var r0 []*model.Team - if rf, ok := ret.Get(0).(func(*model.User) []*model.Team); ok { - r0 = rf(u) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*model.Team) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(*model.User) error); ok { - r1 = rf(u) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/server/remote/mocks/remote.go b/server/remote/mocks/remote.go new file mode 100644 index 000000000..feac2d7d7 --- /dev/null +++ b/server/remote/mocks/remote.go @@ -0,0 +1,298 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + http "net/http" + + mock "github.com/stretchr/testify/mock" + + model "github.com/woodpecker-ci/woodpecker/server/model" + + remote "github.com/woodpecker-ci/woodpecker/server/remote" +) + +// Remote is an autogenerated mock type for the Remote type +type Remote struct { + mock.Mock +} + +// Activate provides a mock function with given fields: ctx, u, r, link +func (_m *Remote) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + ret := _m.Called(ctx, u, r, link) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *model.User, *model.Repo, string) error); ok { + r0 = rf(ctx, u, r, link) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Auth provides a mock function with given fields: ctx, token, secret +func (_m *Remote) Auth(ctx context.Context, token string, secret string) (string, error) { + ret := _m.Called(ctx, token, secret) + + var r0 string + if rf, ok := ret.Get(0).(func(context.Context, string, string) string); ok { + r0 = rf(ctx, token, secret) + } else { + r0 = ret.Get(0).(string) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, token, secret) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Deactivate provides a mock function with given fields: ctx, u, r, link +func (_m *Remote) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error { + ret := _m.Called(ctx, u, r, link) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *model.User, *model.Repo, string) error); ok { + r0 = rf(ctx, u, r, link) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Dir provides a mock function with given fields: ctx, u, r, b, f +func (_m *Remote) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { + ret := _m.Called(ctx, u, r, b, f) + + var r0 []*remote.FileMeta + if rf, ok := ret.Get(0).(func(context.Context, *model.User, *model.Repo, *model.Build, string) []*remote.FileMeta); ok { + r0 = rf(ctx, u, r, b, f) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*remote.FileMeta) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *model.User, *model.Repo, *model.Build, string) error); ok { + r1 = rf(ctx, u, r, b, f) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// File provides a mock function with given fields: ctx, u, r, b, f +func (_m *Remote) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { + ret := _m.Called(ctx, u, r, b, f) + + var r0 []byte + if rf, ok := ret.Get(0).(func(context.Context, *model.User, *model.Repo, *model.Build, string) []byte); ok { + r0 = rf(ctx, u, r, b, f) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *model.User, *model.Repo, *model.Build, string) error); ok { + r1 = rf(ctx, u, r, b, f) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Hook provides a mock function with given fields: r +func (_m *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) { + ret := _m.Called(r) + + var r0 *model.Repo + if rf, ok := ret.Get(0).(func(*http.Request) *model.Repo); ok { + r0 = rf(r) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Repo) + } + } + + var r1 *model.Build + if rf, ok := ret.Get(1).(func(*http.Request) *model.Build); ok { + r1 = rf(r) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.Build) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(*http.Request) error); ok { + r2 = rf(r) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// Login provides a mock function with given fields: ctx, w, r +func (_m *Remote) Login(ctx context.Context, w http.ResponseWriter, r *http.Request) (*model.User, error) { + ret := _m.Called(ctx, w, r) + + var r0 *model.User + if rf, ok := ret.Get(0).(func(context.Context, http.ResponseWriter, *http.Request) *model.User); ok { + r0 = rf(ctx, w, r) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.User) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, http.ResponseWriter, *http.Request) error); ok { + r1 = rf(ctx, w, r) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Netrc provides a mock function with given fields: u, r +func (_m *Remote) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { + ret := _m.Called(u, r) + + var r0 *model.Netrc + if rf, ok := ret.Get(0).(func(*model.User, *model.Repo) *model.Netrc); ok { + r0 = rf(u, r) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Netrc) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(*model.User, *model.Repo) error); ok { + r1 = rf(u, r) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Perm provides a mock function with given fields: ctx, u, owner, repo +func (_m *Remote) Perm(ctx context.Context, u *model.User, owner string, repo string) (*model.Perm, error) { + ret := _m.Called(ctx, u, owner, repo) + + var r0 *model.Perm + if rf, ok := ret.Get(0).(func(context.Context, *model.User, string, string) *model.Perm); ok { + r0 = rf(ctx, u, owner, repo) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Perm) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *model.User, string, string) error); ok { + r1 = rf(ctx, u, owner, repo) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Repo provides a mock function with given fields: ctx, u, owner, name +func (_m *Remote) Repo(ctx context.Context, u *model.User, owner string, name string) (*model.Repo, error) { + ret := _m.Called(ctx, u, owner, name) + + var r0 *model.Repo + if rf, ok := ret.Get(0).(func(context.Context, *model.User, string, string) *model.Repo); ok { + r0 = rf(ctx, u, owner, name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Repo) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *model.User, string, string) error); ok { + r1 = rf(ctx, u, owner, name) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Repos provides a mock function with given fields: ctx, u +func (_m *Remote) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) { + ret := _m.Called(ctx, u) + + var r0 []*model.Repo + if rf, ok := ret.Get(0).(func(context.Context, *model.User) []*model.Repo); ok { + r0 = rf(ctx, u) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*model.Repo) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *model.User) error); ok { + r1 = rf(ctx, u) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Status provides a mock function with given fields: ctx, u, r, b, link, proc +func (_m *Remote) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { + ret := _m.Called(ctx, u, r, b, link, proc) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *model.User, *model.Repo, *model.Build, string, *model.Proc) error); ok { + r0 = rf(ctx, u, r, b, link, proc) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Teams provides a mock function with given fields: ctx, u +func (_m *Remote) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) { + ret := _m.Called(ctx, u) + + var r0 []*model.Team + if rf, ok := ret.Get(0).(func(context.Context, *model.User) []*model.Team); ok { + r0 = rf(ctx, u) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*model.Team) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *model.User) error); ok { + r1 = rf(ctx, u) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/server/remote/remote.go b/server/remote/remote.go index 64374857d..b3dc06b72 100644 --- a/server/remote/remote.go +++ b/server/remote/remote.go @@ -14,63 +14,61 @@ package remote -//go:generate mockery -name Remote -output mock -case=underscore +//go:generate mockery -name Remote -output mocks -case=underscore import ( + "context" "net/http" "github.com/woodpecker-ci/woodpecker/server/model" - - "golang.org/x/net/context" ) // TODO: use pagination -// TODO: use context // TODO: add Driver() who return source forge back type Remote interface { // Login authenticates the session and returns the // remote user details. - Login(w http.ResponseWriter, r *http.Request) (*model.User, error) + Login(ctx context.Context, w http.ResponseWriter, r *http.Request) (*model.User, error) // Auth authenticates the session and returns the remote user // login for the given token and secret - Auth(token, secret string) (string, error) + Auth(ctx context.Context, token, secret string) (string, error) // Teams fetches a list of team memberships from the remote system. - Teams(u *model.User) ([]*model.Team, error) + Teams(ctx context.Context, u *model.User) ([]*model.Team, error) // Repo fetches the named repository from the remote system. - Repo(u *model.User, owner, repo string) (*model.Repo, error) + Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) // Repos fetches a list of repos from the remote system. - Repos(u *model.User) ([]*model.Repo, error) + Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) // Perm fetches the named repository permissions from // the remote system for the specified user. - Perm(u *model.User, owner, repo string) (*model.Perm, error) + Perm(ctx context.Context, u *model.User, owner, repo string) (*model.Perm, error) // File fetches a file from the remote repository and returns in string // format. - File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) + File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) // Dir fetches a folder from the remote repository - Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*FileMeta, error) + Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*FileMeta, error) // Status sends the commit status to the remote system. // An example would be the GitHub pull request status. - Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error + Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error // Netrc returns a .netrc file that can be used to clone // private repositories from a remote system. Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) // Activate activates a repository by creating the post-commit hook. - Activate(u *model.User, r *model.Repo, link string) error + Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error // Deactivate deactivates a repository by removing all previously created // post-commit hooks matching the given link. - Deactivate(u *model.User, r *model.Repo, link string) error + Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error // Hook parses the post-commit hook from the Request body and returns the // required data in a standard format. @@ -93,46 +91,46 @@ func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } // returns true if the token was refreshed, false if the token was not refreshed, // and error if it failed to refersh. type Refresher interface { - Refresh(*model.User) (bool, error) + Refresh(context.Context, *model.User) (bool, error) } // Login authenticates the session and returns the // remote user details. func Login(c context.Context, w http.ResponseWriter, r *http.Request) (*model.User, error) { - return FromContext(c).Login(w, r) + return FromContext(c).Login(c, w, r) } // Auth authenticates the session and returns the remote user // login for the given token and secret func Auth(c context.Context, token, secret string) (string, error) { - return FromContext(c).Auth(token, secret) + return FromContext(c).Auth(c, token, secret) } // Teams fetches a list of team memberships from the remote system. func Teams(c context.Context, u *model.User) ([]*model.Team, error) { - return FromContext(c).Teams(u) + return FromContext(c).Teams(c, u) } // Repo fetches the named repository from the remote system. func Repo(c context.Context, u *model.User, owner, repo string) (*model.Repo, error) { - return FromContext(c).Repo(u, owner, repo) + return FromContext(c).Repo(c, u, owner, repo) } // Repos fetches a list of repos from the remote system. func Repos(c context.Context, u *model.User) ([]*model.Repo, error) { - return FromContext(c).Repos(u) + return FromContext(c).Repos(c, u) } // Perm fetches the named repository permissions from // the remote system for the specified user. func Perm(c context.Context, u *model.User, owner, repo string) (*model.Perm, error) { - return FromContext(c).Perm(u, owner, repo) + return FromContext(c).Perm(c, u, owner, repo) } // Status sends the commit status to the remote system. // An example would be the GitHub pull request status. func Status(c context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { - return FromContext(c).Status(u, r, b, link, proc) + return FromContext(c).Status(c, u, r, b, link, proc) } // Netrc returns a .netrc file that can be used to clone @@ -144,13 +142,13 @@ func Netrc(c context.Context, u *model.User, r *model.Repo) (*model.Netrc, error // Activate activates a repository by creating the post-commit hook and // adding the SSH deploy key, if applicable. func Activate(c context.Context, u *model.User, r *model.Repo, link string) error { - return FromContext(c).Activate(u, r, link) + return FromContext(c).Activate(c, u, r, link) } // Deactivate removes a repository by removing all the post-commit hooks // which are equal to link and removing the SSH deploy key. func Deactivate(c context.Context, u *model.User, r *model.Repo, link string) error { - return FromContext(c).Deactivate(u, r, link) + return FromContext(c).Deactivate(c, u, r, link) } // Hook parses the post-commit hook from the Request body @@ -168,5 +166,5 @@ func Refresh(c context.Context, u *model.User) (bool, error) { if !ok { return false, nil } - return refresher.Refresh(u) + return refresher.Refresh(c, u) } diff --git a/server/router/middleware/session/repo.go b/server/router/middleware/session/repo.go index de5224104..0f525e06d 100644 --- a/server/router/middleware/session/repo.go +++ b/server/router/middleware/session/repo.go @@ -95,7 +95,7 @@ func SetPerm() gin.HandlerFunc { user.Login, repo.FullName, err) } if time.Unix(perm.Synced, 0).Add(time.Hour).Before(time.Now()) { - perm, err = remote.FromContext(c).Perm(user, repo.Owner, repo.Name) + perm, err = remote.FromContext(c).Perm(c, user, repo.Owner, repo.Name) if err == nil { log.Debugf("Synced user permission for %s %s", user.Login, repo.FullName) perm.Repo = repo.FullName diff --git a/server/router/middleware/token/token.go b/server/router/middleware/token/token.go index 5961bbb2a..371c35823 100644 --- a/server/router/middleware/token/token.go +++ b/server/router/middleware/token/token.go @@ -52,7 +52,7 @@ func Refresh(c *gin.Context) { // attempts to refresh the access token. If the // token is refreshed, we must also persist to the // database. - ok, _ = refresher.Refresh(user) + ok, _ = refresher.Refresh(c, user) if ok { err := store.UpdateUser(c, user) if err != nil { diff --git a/server/shared/configFetcher.go b/server/shared/configFetcher.go index 8924066e4..d5c8a984f 100644 --- a/server/shared/configFetcher.go +++ b/server/shared/configFetcher.go @@ -1,6 +1,8 @@ package shared import ( + "context" + "errors" "fmt" "strings" "time" @@ -27,81 +29,90 @@ func NewConfigFetcher(remote remote.Remote, user *model.User, repo *model.Repo, } } -// Fetch -// TODO: dedupe code -func (cf *configFetcher) Fetch() (files []*remote.FileMeta, err error) { - var file []byte - config := strings.TrimSpace(cf.repo.Config) - +// Fetch pipeline config from source forge +func (cf *configFetcher) Fetch(ctx context.Context) (files []*remote.FileMeta, err error) { logrus.Tracef("Start Fetching config for '%s'", cf.repo.FullName) - for i := 0; i < 5; i++ { - select { - case <-time.After(time.Second * time.Duration(i)): - if len(config) > 0 { - // either a file - if !strings.HasSuffix(config, "/") { - file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config) - if err == nil && len(file) != 0 { - logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config) - return []*remote.FileMeta{{ - Name: config, - Data: file, - }}, nil - } - } - - // or a folder - files, err = cf.remote_.Dir(cf.user, cf.repo, cf.build, strings.TrimSuffix(config, "/")) - if err == nil { - logrus.Tracef("ConfigFetch[%s]: found %d files in '%s'", cf.repo.FullName, len(files), config) - return filterPipelineFiles(files), nil - } - } 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 - - // test .woodpecker/ folder - // if folder is not supported we will get a "Not implemented" error and continue - 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) - if err == nil && len(files) != 0 { - return files, nil - } - - config = ".woodpecker.yml" - file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config) - if err == nil && len(file) != 0 { - logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config) - return []*remote.FileMeta{{ - Name: ".woodpecker.yml", - Data: file, - }}, nil - } - - config = ".drone.yml" - file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config) - if err == nil && len(file) != 0 { - logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config) - return []*remote.FileMeta{{ - Name: ".drone.yml", - Data: file, - }}, nil - } - - if err == nil && len(files) == 0 { - return nil, fmt.Errorf("ConfigFetcher: Fallback did not found config") - } - } - - // TODO: retry loop is inactive and could maybe be fixed/deleted - return nil, err + // try to fetch 3 times, timeout is one second longer each time + for i := 0; i < 3; i++ { + files, err = cf.fetch(ctx, time.Second*time.Duration(i), strings.TrimSpace(cf.repo.Config)) + if errors.Is(err, context.DeadlineExceeded) { + continue } + return + } + return +} + +// fetch config by timeout +// TODO: dedupe code +func (cf *configFetcher) fetch(c context.Context, timeout time.Duration, config string) ([]*remote.FileMeta, error) { + ctx, cancel := context.WithTimeout(c, timeout) + defer cancel() + + if len(config) > 0 { + logrus.Tracef("ConfigFetch[%s]: use user config '%s'", cf.repo.FullName, config) + // either a file + if !strings.HasSuffix(config, "/") { + file, err := cf.remote_.File(ctx, cf.user, cf.repo, cf.build, config) + if err == nil && len(file) != 0 { + logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config) + return []*remote.FileMeta{{ + Name: config, + Data: file, + }}, nil + } + } + + // or a folder + files, err := cf.remote_.Dir(ctx, cf.user, cf.repo, cf.build, strings.TrimSuffix(config, "/")) + if err == nil && len(files) != 0 { + logrus.Tracef("ConfigFetch[%s]: found %d files in '%s'", cf.repo.FullName, len(files), config) + return filterPipelineFiles(files), nil + } + + return nil, fmt.Errorf("config '%s' not found: %s", config, err) } - return []*remote.FileMeta{}, nil + 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 + + // test .woodpecker/ folder + // if folder is not supported we will get a "Not implemented" error and continue + config = ".woodpecker" + files, err := cf.remote_.Dir(ctx, cf.user, cf.repo, cf.build, config) + files = filterPipelineFiles(files) + if err == nil && len(files) != 0 { + logrus.Tracef("ConfigFetch[%s]: found %d files in '%s'", cf.repo.FullName, len(files), config) + return files, nil + } + + config = ".woodpecker.yml" + file, err := cf.remote_.File(ctx, cf.user, cf.repo, cf.build, config) + if err == nil && len(file) != 0 { + logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config) + return []*remote.FileMeta{{ + Name: config, + Data: file, + }}, nil + } + + config = ".drone.yml" + file, err = cf.remote_.File(ctx, cf.user, cf.repo, cf.build, config) + if err == nil && len(file) != 0 { + logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config) + return []*remote.FileMeta{{ + Name: config, + Data: file, + }}, nil + } + + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + return []*remote.FileMeta{}, fmt.Errorf("ConfigFetcher: Fallback did not found config: %s", err) + } } func filterPipelineFiles(files []*remote.FileMeta) []*remote.FileMeta { diff --git a/server/shared/configFetcher_test.go b/server/shared/configFetcher_test.go index 26f3b5722..d93448fb9 100644 --- a/server/shared/configFetcher_test.go +++ b/server/shared/configFetcher_test.go @@ -1,6 +1,7 @@ package shared_test import ( + "context" "fmt" "path/filepath" "testing" @@ -216,7 +217,7 @@ func TestFetch(t *testing.T) { r := new(mocks.Remote) dirs := map[string][]*remote.FileMeta{} for _, file := range tt.files { - r.On("File", mock.Anything, mock.Anything, mock.Anything, file.name).Return(file.data, nil) + r.On("File", mock.Anything, mock.Anything, mock.Anything, mock.Anything, file.name).Return(file.data, nil) path := filepath.Dir(file.name) if path != "." { dirs[path] = append(dirs[path], &remote.FileMeta{ @@ -227,12 +228,12 @@ func TestFetch(t *testing.T) { } for path, files := range dirs { - r.On("Dir", mock.Anything, mock.Anything, mock.Anything, path).Return(files, nil) + r.On("Dir", mock.Anything, mock.Anything, mock.Anything, mock.Anything, path).Return(files, nil) } // if the previous mocks do not match return not found errors - r.On("File", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("File not found")) - r.On("Dir", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("Directory not found")) + r.On("File", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("File not found")) + r.On("Dir", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("Directory not found")) configFetcher := shared.NewConfigFetcher( r, @@ -240,7 +241,7 @@ func TestFetch(t *testing.T) { repo, &model.Build{Commit: "89ab7b2d6bfb347144ac7c557e638ab402848fee"}, ) - files, err := configFetcher.Fetch() + files, err := configFetcher.Fetch(context.Background()) if tt.expectedError && err == nil { t.Fatal("expected an error") } else if !tt.expectedError && err != nil { diff --git a/server/shared/userSyncer.go b/server/shared/userSyncer.go index d1ef807f0..fdfe6f8bb 100644 --- a/server/shared/userSyncer.go +++ b/server/shared/userSyncer.go @@ -15,6 +15,7 @@ package shared import ( + "context" "time" "github.com/woodpecker-ci/woodpecker/server/model" @@ -24,7 +25,7 @@ import ( // UserSyncer syncs the user repository and permissions. type UserSyncer interface { - Sync(user *model.User) error + Sync(ctx context.Context, user *model.User) error } type Syncer struct { @@ -62,9 +63,9 @@ func (s *Syncer) SetFilter(fn FilterFunc) { s.Match = fn } -func (s *Syncer) Sync(user *model.User) error { +func (s *Syncer) Sync(ctx context.Context, user *model.User) error { unix := time.Now().Unix() - (3601) // force immediate expiration. note 1 hour expiration is hard coded at the moment - repos, err := s.Remote.Repos(user) + repos, err := s.Remote.Repos(ctx, user) if err != nil { return err }