Merge pull request #747 from gdasson/fix_742
Add support for Bitbucket Context path
This commit is contained in:
commit
678f374bc3
|
|
@ -1539,6 +1539,8 @@ kubectl create secret generic bb-server-token --from-literal=token=<token>
|
|||
The HTTP access token must have `Repositories (Read/Write)` permission for
|
||||
the repository specified in `.spec.address`.
|
||||
|
||||
**NOTE:** Please provide HTTPS clone URL in the `address` field of this provider. SSH URLs are not supported by this provider type.
|
||||
|
||||
#### Azure DevOps
|
||||
|
||||
When `.spec.type` is set to `azuredevops`, the referenced secret must contain a key called `token` with the value set to a
|
||||
|
|
|
|||
|
|
@ -1567,6 +1567,8 @@ kubectl create secret generic bb-server-token --from-literal=token=<token>
|
|||
The HTTP access token must have `Repositories (Read/Write)` permission for
|
||||
the repository specified in `.spec.address`.
|
||||
|
||||
**NOTE:** Please provide HTTPS clone URL in the `address` field of this provider. SSH URLs are not supported by this provider type.
|
||||
|
||||
#### Azure DevOps
|
||||
|
||||
When `.spec.type` is set to `azuredevops`, the referenced secret must contain a key called `token` with the value set to a
|
||||
|
|
|
|||
|
|
@ -37,11 +37,9 @@ import (
|
|||
|
||||
// BitbucketServer is a notifier for BitBucket Server and Data Center.
|
||||
type BitbucketServer struct {
|
||||
ProjectKey string
|
||||
RepositorySlug string
|
||||
ProviderUID string
|
||||
Url *url.URL
|
||||
ProviderAddress string
|
||||
Host string
|
||||
Username string
|
||||
Password string
|
||||
Token string
|
||||
|
|
@ -49,8 +47,10 @@ type BitbucketServer struct {
|
|||
}
|
||||
|
||||
const (
|
||||
bbServerEndPointTmpl = "/rest/api/latest/projects/%[1]s/repos/%[2]s/commits/%[3]s/builds"
|
||||
bbServerEndPointCommitsTmpl = "%[1]s/rest/api/latest/projects/%[2]s/repos/%[3]s/commits"
|
||||
bbServerEndPointBuildsTmpl = "%[1]s/builds"
|
||||
bbServerGetBuildStatusQueryString = "key"
|
||||
bbServerSourceCodeMgmtString = "/scm/"
|
||||
)
|
||||
|
||||
type bbServerBuildStatus struct {
|
||||
|
|
@ -82,18 +82,11 @@ type bbServerBuildStatusSetRequest struct {
|
|||
|
||||
// NewBitbucketServer creates and returns a new BitbucketServer notifier.
|
||||
func NewBitbucketServer(providerUID string, addr string, token string, certPool *x509.CertPool, username string, password string) (*BitbucketServer, error) {
|
||||
hst, id, err := parseBitbucketServerGitAddress(addr)
|
||||
url, err := parseBitbucketServerGitAddress(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
comp := strings.Split(id, "/")
|
||||
if len(comp) != 2 {
|
||||
return nil, fmt.Errorf("invalid repository id %q", id)
|
||||
}
|
||||
projectkey := comp[0]
|
||||
reposlug := comp[1]
|
||||
|
||||
httpClient := retryablehttp.NewClient()
|
||||
if certPool != nil {
|
||||
httpClient.HTTPClient.Transport = &http.Transport{
|
||||
|
|
@ -114,10 +107,8 @@ func NewBitbucketServer(providerUID string, addr string, token string, certPool
|
|||
}
|
||||
|
||||
return &BitbucketServer{
|
||||
ProjectKey: projectkey,
|
||||
RepositorySlug: reposlug,
|
||||
ProviderUID: providerUID,
|
||||
Host: hst,
|
||||
Url: url,
|
||||
ProviderAddress: addr,
|
||||
Token: token,
|
||||
Username: username,
|
||||
|
|
@ -151,14 +142,14 @@ func (b BitbucketServer) Post(ctx context.Context, event eventv1.Event) error {
|
|||
// key has a limitation of 40 characters in bitbucket api
|
||||
key := sha1String(id)
|
||||
|
||||
u := b.Host + b.createApiPath(rev)
|
||||
dupe, err := b.duplicateBitbucketServerStatus(ctx, rev, state, name, desc, id, key, u)
|
||||
u := b.Url.JoinPath(b.createBuildPath(rev)).String()
|
||||
dupe, err := b.duplicateBitbucketServerStatus(ctx, state, name, desc, key, u)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get existing commit status: %w", err)
|
||||
}
|
||||
|
||||
if !dupe {
|
||||
_, err = b.postBuildStatus(ctx, rev, state, name, desc, id, key, u)
|
||||
_, err = b.postBuildStatus(ctx, state, name, desc, key, u)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not post build status: %w", err)
|
||||
}
|
||||
|
|
@ -178,9 +169,9 @@ func (b BitbucketServer) state(severity string) (string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (b BitbucketServer) duplicateBitbucketServerStatus(ctx context.Context, rev, state, name, desc, id, key, u string) (bool, error) {
|
||||
func (b BitbucketServer) duplicateBitbucketServerStatus(ctx context.Context, state, name, desc, key, u string) (bool, error) {
|
||||
// Prepare request object
|
||||
req, err := b.prepareCommonRequest(ctx, u, nil, http.MethodGet, key, rev)
|
||||
req, err := b.prepareCommonRequest(ctx, u, nil, http.MethodGet)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("could not check duplicate commit status: %w", err)
|
||||
}
|
||||
|
|
@ -219,7 +210,7 @@ func (b BitbucketServer) duplicateBitbucketServerStatus(ctx context.Context, rev
|
|||
return false, nil
|
||||
}
|
||||
|
||||
func (b BitbucketServer) postBuildStatus(ctx context.Context, rev, state, name, desc, id, key, url string) (*http.Response, error) {
|
||||
func (b BitbucketServer) postBuildStatus(ctx context.Context, state, name, desc, key, url string) (*http.Response, error) {
|
||||
//Prepare json body
|
||||
j := &bbServerBuildStatusSetRequest{
|
||||
Key: key,
|
||||
|
|
@ -235,7 +226,7 @@ func (b BitbucketServer) postBuildStatus(ctx context.Context, rev, state, name,
|
|||
}
|
||||
|
||||
//Prepare request
|
||||
req, err := b.prepareCommonRequest(ctx, url, p, http.MethodPost, key, rev)
|
||||
req, err := b.prepareCommonRequest(ctx, url, p, http.MethodPost)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed preparing request for post build commit status: %w", err)
|
||||
}
|
||||
|
|
@ -257,21 +248,46 @@ func (b BitbucketServer) postBuildStatus(ctx context.Context, rev, state, name,
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
func (b BitbucketServer) createApiPath(rev string) string {
|
||||
return fmt.Sprintf(bbServerEndPointTmpl, b.ProjectKey, b.RepositorySlug, rev)
|
||||
func (b BitbucketServer) createBuildPath(rev string) string {
|
||||
return fmt.Sprintf(bbServerEndPointBuildsTmpl, rev)
|
||||
}
|
||||
|
||||
func parseBitbucketServerGitAddress(s string) (string, string, error) {
|
||||
host, id, err := parseGitAddress(s)
|
||||
func parseBitbucketServerGitAddress(s string) (*url.URL, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("could not parse git address: %w", err)
|
||||
return nil, fmt.Errorf("could not parse git address: %w", err)
|
||||
}
|
||||
//Remove "scm/" --> https://community.atlassian.com/t5/Bitbucket-questions/remote-url-in-Bitbucket-server-what-does-scm-represent-is-it/qaq-p/2060987
|
||||
id = strings.TrimPrefix(id, "scm/")
|
||||
return host, id, nil
|
||||
if u.Scheme != "http" && u.Scheme != "https" {
|
||||
return nil, fmt.Errorf("could not parse git address: unsupported scheme type in address: %s. Must be http or https", u.Scheme)
|
||||
}
|
||||
|
||||
idWithContext := strings.TrimSuffix(u.Path, ".git")
|
||||
|
||||
// /scm/ is always part of http/https clone urls : https://community.atlassian.com/t5/Bitbucket-questions/remote-url-in-Bitbucket-server-what-does-scm-represent-is-it/qaq-p/2060987
|
||||
lastIndex := strings.LastIndex(idWithContext, bbServerSourceCodeMgmtString)
|
||||
if lastIndex < 0 {
|
||||
return nil, fmt.Errorf("could not parse git address: supplied provider address is not http(s) git clone url")
|
||||
}
|
||||
|
||||
// Handle context scenarios --> https://confluence.atlassian.com/bitbucketserver/change-bitbucket-s-context-path-776640153.html
|
||||
cntxtPath := idWithContext[:lastIndex] // Context path is anything that comes before last /scm/
|
||||
|
||||
id := idWithContext[lastIndex+len(bbServerSourceCodeMgmtString):] // Remove last `/scm/` from id as it is not used in API calls
|
||||
|
||||
comp := strings.Split(id, "/")
|
||||
if len(comp) != 2 {
|
||||
return nil, fmt.Errorf("could not parse git address: invalid repository id %q", id)
|
||||
}
|
||||
projectkey := comp[0]
|
||||
reposlug := comp[1]
|
||||
|
||||
// Update the path till commits endpoint. The final builds endpoint would be added in Post function.
|
||||
u.Path = fmt.Sprintf(bbServerEndPointCommitsTmpl, cntxtPath, projectkey, reposlug)
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func (b BitbucketServer) prepareCommonRequest(ctx context.Context, path string, body io.Reader, method string, key, rev string) (*retryablehttp.Request, error) {
|
||||
func (b BitbucketServer) prepareCommonRequest(ctx context.Context, path string, body io.Reader, method string) (*retryablehttp.Request, error) {
|
||||
req, err := retryablehttp.NewRequestWithContext(ctx, method, path, body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not prepare request: %w", err)
|
||||
|
|
|
|||
|
|
@ -33,11 +33,69 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestNewBitbucketServerBasic(t *testing.T) {
|
||||
func TestNewBitbucketServerBasicNoContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, b.Username, "dummyuser")
|
||||
assert.Equal(t, b.Password, "testpassword")
|
||||
assert.Equal(t, b.Url.Scheme, "https")
|
||||
assert.Equal(t, b.Url.Host, "example.com:7990")
|
||||
}
|
||||
|
||||
func TestNewBitbucketServerBasicWithContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/context/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, b.Username, "dummyuser")
|
||||
assert.Equal(t, b.Password, "testpassword")
|
||||
assert.Equal(t, b.Url.Scheme, "https")
|
||||
assert.Equal(t, b.Url.Host, "example.com:7990")
|
||||
}
|
||||
|
||||
func TestBitbucketServerApiPathNoContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
u := b.Url.JoinPath(b.createBuildPath("00151b98e303e19610378e6f1c49e31e5e80cd3b")).String()
|
||||
assert.Equal(t, u, "https://example.com:7990/rest/api/latest/projects/projectfoo/repos/repobar/commits/00151b98e303e19610378e6f1c49e31e5e80cd3b/builds")
|
||||
}
|
||||
|
||||
func TestBitbucketServerApiPathOneWordContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/context1/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
u := b.Url.JoinPath(b.createBuildPath("00151b98e303e19610378e6f1c49e31e5e80cd3b")).String()
|
||||
assert.Equal(t, u, "https://example.com:7990/context1/rest/api/latest/projects/projectfoo/repos/repobar/commits/00151b98e303e19610378e6f1c49e31e5e80cd3b/builds")
|
||||
}
|
||||
|
||||
func TestBitbucketServerApiPathMultipleWordContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/context1/context2/context3/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
u := b.Url.JoinPath(b.createBuildPath("00151b98e303e19610378e6f1c49e31e5e80cd3b")).String()
|
||||
assert.Equal(t, u, "https://example.com:7990/context1/context2/context3/rest/api/latest/projects/projectfoo/repos/repobar/commits/00151b98e303e19610378e6f1c49e31e5e80cd3b/builds")
|
||||
}
|
||||
|
||||
func TestBitbucketServerApiPathOneWordScmInContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
u := b.Url.JoinPath(b.createBuildPath("00151b98e303e19610378e6f1c49e31e5e80cd3b")).String()
|
||||
assert.Equal(t, u, "https://example.com:7990/scm/rest/api/latest/projects/projectfoo/repos/repobar/commits/00151b98e303e19610378e6f1c49e31e5e80cd3b/builds")
|
||||
}
|
||||
|
||||
func TestBitbucketServerApiPathMultipleWordScmInContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/context2/scm/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
u := b.Url.JoinPath(b.createBuildPath("00151b98e303e19610378e6f1c49e31e5e80cd3b")).String()
|
||||
assert.Equal(t, u, "https://example.com:7990/scm/context2/scm/rest/api/latest/projects/projectfoo/repos/repobar/commits/00151b98e303e19610378e6f1c49e31e5e80cd3b/builds")
|
||||
}
|
||||
|
||||
func TestBitbucketServerApiPathScmAlreadyRemovedInInput(t *testing.T) {
|
||||
_, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/context1/context2/context3/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, err.Error(), "could not parse git address: supplied provider address is not http(s) git clone url")
|
||||
}
|
||||
|
||||
func TestBitbucketServerSshAddress(t *testing.T) {
|
||||
_, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "ssh://git@mybitbucket:2222/ap/fluxcd-sandbox.git", "", nil, "", "")
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, err.Error(), "could not parse git address: unsupported scheme type in address: ssh. Must be http or https")
|
||||
}
|
||||
|
||||
func TestNewBitbucketServerToken(t *testing.T) {
|
||||
|
|
@ -55,7 +113,7 @@ func TestNewBitbucketServerInvalidCreds(t *testing.T) {
|
|||
func TestNewBitbucketServerInvalidRepo(t *testing.T) {
|
||||
_, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/projectfoo/repobar/invalid.git", "BBDC-ODIxODYxMzIyNzUyOttorMjO059P2rYTb6EH7mP", nil, "", "")
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, err.Error(), "invalid repository id \"projectfoo/repobar/invalid\"")
|
||||
assert.Equal(t, err.Error(), "could not parse git address: invalid repository id \"projectfoo/repobar/invalid\"")
|
||||
}
|
||||
|
||||
func TestPostBitbucketServerMissingRevision(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue