Skip to content

Commit

Permalink
Merge branch 'main' into chore/v0.15.0-rc1
Browse files Browse the repository at this point in the history
  • Loading branch information
wass3r authored Sep 27, 2022
2 parents c7aa210 + fa289f3 commit 8bc6894
Show file tree
Hide file tree
Showing 8 changed files with 398 additions and 0 deletions.
49 changes: 49 additions & 0 deletions api/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,55 @@ func CreateSecret(c *gin.Context) {
}
}

if strings.EqualFold(t, constants.SecretOrg) {
// retrieve org name from SCM
//
// SCM can be case insensitive, causing access retrieval to work
// but Org/Repo != org/repo in Vela. So this check ensures that
// what a user inputs matches the casing we expect in Vela since
// the SCM will have the source of truth for casing.
org, err := scm.FromContext(c).GetOrgName(u, o)
if err != nil {
retErr := fmt.Errorf("unable to retrieve organization %s", o)

util.HandleError(c, http.StatusNotFound, retErr)

return
}

// check if casing is accurate
if org != o {
retErr := fmt.Errorf("unable to retrieve organization %s. Did you mean %s?", o, org)

util.HandleError(c, http.StatusNotFound, retErr)

return
}
}

if strings.EqualFold(t, constants.SecretRepo) {
// retrieve repo name from SCM
//
// same story as org secret. SCM has accurate casing.
scmRepo, err := scm.FromContext(c).GetRepoName(u, o, n)
if err != nil {
retErr := fmt.Errorf("unable to retrieve repository %s/%s", o, n)

util.HandleError(c, http.StatusNotFound, retErr)

return
}

// check if casing is accurate
if scmRepo != n {
retErr := fmt.Errorf("unable to retrieve repository %s. Did you mean %s?", n, scmRepo)

util.HandleError(c, http.StatusNotFound, retErr)

return
}
}

// update engine logger with API metadata
//
// https://pkg.go.dev/github.com/sirupsen/logrus?tab=doc#Entry.WithFields
Expand Down
22 changes: 22 additions & 0 deletions api/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,17 @@ func PostWebhook(c *gin.Context) {

h, r, b := webhook.Hook, webhook.Repo, webhook.Build

logrus.Debugf("hook generated from SCM: %v", h)
logrus.Debugf("repo generated from SCM: %v", r)

if b != nil {
logrus.Debugf(`build author: %s,
build branch: %s,
build commit: %s,
build ref: %s`,
b.GetAuthor(), b.GetBranch(), b.GetCommit(), b.GetRef())
}

// check if build was parsed from webhook.
// build will be nil on repository events, but
// for renaming, we want to continue.
Expand Down Expand Up @@ -273,6 +284,8 @@ func PostWebhook(c *gin.Context) {
}

// send API call to capture repo owner
logrus.Debugf("capturing owner of repository %s", r.GetFullName())

u, err := database.FromContext(c).GetUser(r.GetUserID())
if err != nil {
retErr := fmt.Errorf("%s: failed to get owner for %s: %w", baseErr, r.GetFullName(), err)
Expand Down Expand Up @@ -301,6 +314,8 @@ func PostWebhook(c *gin.Context) {
return
}

logrus.Debugf("currently %d builds running on repo %s", builds, r.GetFullName())

// check if the number of pending and running builds exceeds the limit for the repo
if builds >= r.GetBuildLimit() {
retErr := fmt.Errorf("%s: repo %s has exceeded the concurrent build limit of %d", baseErr, r.GetFullName(), r.GetBuildLimit())
Expand All @@ -313,8 +328,13 @@ func PostWebhook(c *gin.Context) {
}

// update fields in build object
logrus.Debugf("updating build number to %d", r.GetCounter())
b.SetNumber(r.GetCounter())

logrus.Debugf("updating parent number to %d", b.GetNumber())
b.SetParent(b.GetNumber())

logrus.Debug("updating status to pending")
b.SetStatus(constants.StatusPending)

// if this is a comment on a pull_request event
Expand Down Expand Up @@ -388,6 +408,7 @@ func PostWebhook(c *gin.Context) {
// failing to successfully process the request. This logic ensures we attempt our
// best efforts to handle these cases gracefully.
for i := 0; i < retryLimit; i++ {
logrus.Debugf("compilation loop - attempt %d", i+1)
// check if we're on the first iteration of the loop
if i > 0 {
// incrementally sleep in between retries
Expand Down Expand Up @@ -726,6 +747,7 @@ func publishToQueue(queue queue.Service, db database.Service, p *pipeline.Build,
// that repo to its new name in order to preserve it. It also updates the secrets
// associated with that repo.
func renameRepository(h *library.Hook, r *library.Repo, c *gin.Context, m *types.Metadata) error {
logrus.Debugf("renaming repository from %s to %s", r.GetPreviousName(), r.GetName())
// get the old name of the repo
previousName := r.GetPreviousName()
// get the repo from the database that matches the old name
Expand Down
43 changes: 43 additions & 0 deletions scm/github/org.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2022 Target Brands, Inc. All rights reserved.
//
// Use of this source code is governed by the LICENSE file in this repository.

package github

import (
"net/http"

"github.com/sirupsen/logrus"

"github.com/go-vela/types/library"
)

// GetOrgName gets org name from Github.
func (c *client) GetOrgName(u *library.User, o string) (string, error) {
c.Logger.WithFields(logrus.Fields{
"org": o,
"user": u.GetName(),
}).Tracef("retrieving org information for %s", o)

// create GitHub OAuth client with user's token
client := c.newClientToken(u.GetToken())

// send an API call to get the org info
orgInfo, resp, err := client.Organizations.Get(ctx, o)

orgName := orgInfo.GetLogin()

// if org is not found, return the personal org
if resp.StatusCode == http.StatusNotFound {
user, _, err := client.Users.Get(ctx, "")
if err != nil {
return "", err
}

orgName = user.GetLogin()
} else if err != nil {
return "", err
}

return orgName, nil
}
131 changes: 131 additions & 0 deletions scm/github/org_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright (c) 2022 Target Brands, Inc. All rights reserved.
//
// Use of this source code is governed by the LICENSE file in this repository.

package github

import (
"net/http"
"net/http/httptest"
"reflect"
"testing"

"github.com/gin-gonic/gin"

"github.com/go-vela/types/library"
)

func TestGithub_GetOrgName(t *testing.T) {
// setup context
gin.SetMode(gin.TestMode)

resp := httptest.NewRecorder()
_, engine := gin.CreateTestContext(resp)

// setup mock server
engine.GET("/api/v3/orgs/:org", func(c *gin.Context) {
c.Header("Content-Type", "application/json")
c.Status(http.StatusOK)
c.File("testdata/get_org.json")
})

s := httptest.NewServer(engine)
defer s.Close()

// setup types
u := new(library.User)
u.SetName("foo")
u.SetToken("bar")

want := "github"

client, _ := NewTest(s.URL)

// run test
got, err := client.GetOrgName(u, "github")

if resp.Code != http.StatusOK {
t.Errorf("GetOrgName returned %v, want %v", resp.Code, http.StatusOK)
}

if err != nil {
t.Errorf("GetOrgName returned err: %v", err)
}

if !reflect.DeepEqual(got, want) {
t.Errorf("GetOrgName is %v, want %v", got, want)
}
}

func TestGithub_GetOrgName_Personal(t *testing.T) {
// setup context
gin.SetMode(gin.TestMode)

resp := httptest.NewRecorder()
_, engine := gin.CreateTestContext(resp)

// setup mock server
engine.GET("/api/v3/user", func(c *gin.Context) {
c.Header("Content-Type", "application/json")
c.Status(http.StatusOK)
c.File("testdata/user.json")
})

s := httptest.NewServer(engine)
defer s.Close()

// setup types
u := new(library.User)
u.SetName("foo")
u.SetToken("bar")

want := "octocat"

client, _ := NewTest(s.URL)

// run test
got, err := client.GetOrgName(u, "octocat")

if resp.Code != http.StatusOK {
t.Errorf("GetOrgName returned %v, want %v", resp.Code, http.StatusOK)
}

if err != nil {
t.Errorf("GetOrgName returned err: %v", err)
}

if !reflect.DeepEqual(got, want) {
t.Errorf("GetOrgName is %v, want %v", got, want)
}
}

func TestGithub_GetOrgName_Fail(t *testing.T) {
// setup context
gin.SetMode(gin.TestMode)

resp := httptest.NewRecorder()
_, engine := gin.CreateTestContext(resp)

// setup mock server
engine.GET("/api/v3/orgs/:org", func(c *gin.Context) {
c.Header("Content-Type", "application/json")
c.Status(http.StatusNotFound)
})

s := httptest.NewServer(engine)
defer s.Close()

// setup types
u := new(library.User)
u.SetName("foo")
u.SetToken("bar")

client, _ := NewTest(s.URL)

// run test
_, err := client.GetOrgName(u, "octocat")

if err == nil {
t.Error("GetOrgName should return error")
}
}
21 changes: 21 additions & 0 deletions scm/github/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func (c *client) ConfigBackoff(u *library.User, r *library.Repo, ref string) (da
retryLimit := 5

for i := 0; i < retryLimit; i++ {
logrus.Debugf("Fetching config file - Attempt %d", i+1)
// attempt to fetch the config
data, err = c.Config(u, r, ref)

Expand Down Expand Up @@ -309,6 +310,26 @@ func (c *client) GetRepo(u *library.User, r *library.Repo) (*library.Repo, error
return toLibraryRepo(*repo), nil
}

// GetRepoName returns the name of the repository in the SCM.
func (c *client) GetRepoName(u *library.User, o string, r string) (string, error) {
c.Logger.WithFields(logrus.Fields{
"org": o,
"repo": r,
"user": u.GetName(),
}).Tracef("retrieving repository information for %s/%s", o, r)

// create GitHub OAuth client with user's token
client := c.newClientToken(u.GetToken())

// send an API call to get the repo info
repo, _, err := client.Repositories.Get(ctx, o, r)
if err != nil {
return "", err
}

return repo.GetName(), nil
}

// ListUserRepos returns a list of all repos the user has access to.
func (c *client) ListUserRepos(u *library.User) ([]*library.Repo, error) {
c.Logger.WithFields(logrus.Fields{
Expand Down
Loading

0 comments on commit 8bc6894

Please sign in to comment.