Skip to content

Commit

Permalink
Merge pull request #73 from fluxcd/feature/bitbucket
Browse files Browse the repository at this point in the history
Add bitbucket notifier
  • Loading branch information
Philip Laine authored Nov 8, 2020
2 parents fbf1ea0 + ee473c8 commit 5a50b19
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 17 deletions.
17 changes: 9 additions & 8 deletions api/v1beta1/provider_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
// ProviderSpec defines the desired state of Provider
type ProviderSpec struct {
// Type of provider
// +kubebuilder:validation:Enum=slack;discord;msteams;rocket;generic;github;gitlab
// +kubebuilder:validation:Enum=slack;discord;msteams;rocket;generic;github;gitlab;bitbucket
// +required
Type string `json:"type"`

Expand Down Expand Up @@ -56,13 +56,14 @@ type ProviderSpec struct {
}

const (
GenericProvider string = "generic"
SlackProvider string = "slack"
DiscordProvider string = "discord"
MSTeamsProvider string = "msteams"
RocketProvider string = "rocket"
GitHubProvider string = "github"
GitLabProvider string = "gitlab"
GenericProvider string = "generic"
SlackProvider string = "slack"
DiscordProvider string = "discord"
MSTeamsProvider string = "msteams"
RocketProvider string = "rocket"
GitHubProvider string = "github"
GitLabProvider string = "gitlab"
BitbucketProvider string = "bitbucket"
)

// ProviderStatus defines the observed state of Provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ spec:
- generic
- github
- gitlab
- bitbucket
type: string
username:
description: Bot username for this provider
Expand Down
38 changes: 29 additions & 9 deletions docs/spec/v1beta1/provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Git commit status providers:

* GitHub
* GitLab
* Bitbucket

Status:

Expand Down Expand Up @@ -108,9 +109,9 @@ incoming [event](event.md) in JSON format to the webhook address.

### Git commit status

The GitHub/GitLab provider is a special kind of notification provider
that based on the state of a Kustomization resource,
will update the commit status for the currently reconciled commit id.
The GitHub, GitLab, and Bitbucket provider is a special kind of notification
provider that based on the state of a Kustomization resource,
will update the commit status for the reconciled commit id.

```yaml
apiVersion: notification.toolkit.fluxcd.io/v1beta1
Expand All @@ -123,13 +124,32 @@ spec:
type: github
address: https://github.com/stefanprodan/podinfo
secretRef:
name: git-api-token
name: api-token
```
The secret referenced in the provider is expected to contain a
personal access token to authenticate with the GitHub or GitLab API.
GitHub and GitLab use personal access tokens to authenticate with their API.
* [GitHub personal access token](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token)
* [GitLab personal access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html)
Both provider types require a secret in the same format, with the personal access token as the value for the token key.
```yaml
apiVersion: v1
kind: Secret
metadata:
name: api-token
namespace: default
data:
token: <personal-access-tokens>
```
```sh
kubectl create secret generic git-api-token \
--from-literal=token=YOUR-TOKEN
Bitbucket authenticates using an [app password](https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/). It requires
both the username and the password when authenticating. There for the token needs to be passed with the format `<username>:<app-password>`.
A token that is not in this format will cause the provider to fail.
```yaml
apiVersion: v1
kind: Secret
metadata:
name: api-token
namespace: default
data:
token: <username>:<app-password>
```
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/go-logr/logr v0.2.1
github.com/google/go-github/v32 v32.0.0
github.com/hashicorp/go-retryablehttp v0.6.7
github.com/ktrysmt/go-bitbucket v0.6.5
github.com/onsi/ginkgo v1.12.1
github.com/onsi/gomega v1.10.1
github.com/stretchr/testify v1.6.1
Expand Down
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down Expand Up @@ -252,6 +253,10 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/k0kubun/pp v2.3.0+incompatible h1:EKhKbi34VQDWJtq+zpsKSEhkHHs9w2P8Izbq8IhLVSo=
github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
Expand All @@ -265,19 +270,26 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/ktrysmt/go-bitbucket v0.6.5 h1:+M4oFRZbzKH+Jt/eY3rKDPwyA5uvjY0KSJQwePGvvzQ=
github.com/ktrysmt/go-bitbucket v0.6.5/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
Expand Down Expand Up @@ -416,6 +428,7 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand All @@ -442,6 +455,7 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dD
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand All @@ -456,6 +470,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -532,6 +547,7 @@ google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
Expand Down
115 changes: 115 additions & 0 deletions internal/notifier/bitbucket.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package notifier

import (
"errors"
"strings"

"github.com/fluxcd/pkg/recorder"
"github.com/ktrysmt/go-bitbucket"
)

type Bitbucket struct {
Owner string
Repo string
Client *bitbucket.Client
}

func NewBitbucket(addr string, token string) (*Bitbucket, error) {
if len(token) == 0 {
return nil, errors.New("Bitbucket token cannot be empty")
}

_, id, err := parseGitAddress(addr)
if err != nil {
return nil, err
}

comp := strings.Split(token, ":")
if len(comp) != 2 {
return nil, errors.New("Invalid token format, expected to be <user>:<password>")
}
username := comp[0]
password := comp[1]

comp = strings.Split(id, "/")
if len(comp) != 2 {
return nil, errors.New("Invalid bitbucket repository id")
}
owner := comp[0]
repo := comp[1]

return &Bitbucket{
Owner: owner,
Repo: repo,
Client: bitbucket.NewBasicAuth(username, password),
}, nil
}

// Post Bitbucket commit status
func (b Bitbucket) Post(event recorder.Event) error {
// Skip progressing events
if event.Reason == "Progressing" {
return nil
}

revString, ok := event.Metadata["revision"]
if !ok {
return errors.New("Missing revision metadata")
}
rev, err := parseRevision(revString)
if err != nil {
return err
}
state, err := toBitbucketState(event.Severity)
if err != nil {
return err
}

name, desc := formatNameAndDescription(event)

cmo := &bitbucket.CommitsOptions{
Owner: b.Owner,
RepoSlug: b.Repo,
Revision: rev,
}
cso := &bitbucket.CommitStatusOptions{
State: state,
Key: name,
Name: name,
Description: desc,
Url: "https://bitbucket.org",
}
_, err = b.Client.Repositories.Commits.CreateCommitStatus(cmo, cso)
if err != nil {
return err
}

return nil
}

func toBitbucketState(severity string) (string, error) {
switch severity {
case recorder.EventSeverityInfo:
return "SUCCESSFUL", nil
case recorder.EventSeverityError:
return "FAILED", nil
default:
return "", errors.New("Can't convert to GitHub state")
}
}
41 changes: 41 additions & 0 deletions internal/notifier/bitbucket_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package notifier

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestNewBitbucketBasic(t *testing.T) {
b, err := NewBitbucket("https://bitbucket.org/foo/bar", "foo:bar")

assert.Nil(t, err)
assert.Equal(t, b.Owner, "foo")
assert.Equal(t, b.Repo, "bar")
}

func TestNewBitbucketInvalidUrl(t *testing.T) {
_, err := NewBitbucket("https://bitbucket.org/foo/bar/baz", "foo:bar")
assert.NotNil(t, err)
}

func TestNewBitbucketInvalidToken(t *testing.T) {
_, err := NewBitbucket("https://bitbucket.org/foo/bar", "bar")
assert.NotNil(t, err)
}
2 changes: 2 additions & 0 deletions internal/notifier/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ func (f Factory) Notifier(provider string) (Interface, error) {
n, err = NewGitHub(f.URL, f.Token)
case v1beta1.GitLabProvider:
n, err = NewGitLab(f.URL, f.Token)
case v1beta1.BitbucketProvider:
n, err = NewBitbucket(f.URL, f.Token)
default:
err = fmt.Errorf("provider %s not supported", provider)
}
Expand Down

0 comments on commit 5a50b19

Please sign in to comment.