Pass down context.Context (#371)

* pass context down to remote clients

* make tests work

* add ctx to Refresh() and use it

* bitbucketserver

* code format

* plugin interface: add todo context

* solve todo

* RM TODO by using context.WithTimeout

* refactor & fix

* Apply suggestions from code review

Co-authored-by: Anbraten <anton@ju60.de>

* go fmt

* Update server/remote/coding/coding.go

Co-authored-by: Anbraten <anton@ju60.de>

Co-authored-by: Anbraten <anton@ju60.de>
This commit is contained in:
6543 2021-09-28 12:56:59 +02:00 committed by GitHub
parent c0888de86b
commit e3499f610d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 815 additions and 791 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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