Skip to content

Commit

Permalink
feat(eventsources/bitbucketserver): add OneEventPerChange config opti…
Browse files Browse the repository at this point in the history
…on for webhook event handling (#3135)

Signed-off-by: Ryan Currah <[email protected]>
  • Loading branch information
ryancurrah authored Sep 26, 2024
1 parent a92adbd commit 0d31a3e
Show file tree
Hide file tree
Showing 17 changed files with 15,145 additions and 1,273 deletions.
6,688 changes: 6,688 additions & 0 deletions api/event-source.html

Large diffs are not rendered by default.

6,830 changes: 6,830 additions & 0 deletions api/event-source.md

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion api/jsonschema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@
"type": "object"
},
"io.argoproj.events.v1alpha1.BitbucketBasicAuth": {
"description": "BasicAuth holds the information required to authenticate user via basic auth mechanism",
"description": "BitbucketBasicAuth holds the information required to authenticate user via basic auth mechanism",
"properties": {
"password": {
"$ref": "#/definitions/io.k8s.api.core.v1.SecretKeySelector",
Expand Down Expand Up @@ -685,6 +685,10 @@
"description": "Metadata holds the user defined metadata which will passed along the event payload.",
"type": "object"
},
"oneEventPerChange": {
"description": "OneEventPerChange controls whether to process each change in a repo:refs_changed webhook event as a separate event. This setting is useful when multiple tags are pushed simultaneously for the same commit, and each tag needs to independently trigger an action, such as a distinct workflow in Argo Workflows. When enabled, the BitbucketServerEventSource publishes an individual BitbucketServerEventData for each change, ensuring independent processing of each tag or reference update in a single webhook event.",
"type": "boolean"
},
"projectKey": {
"description": "DeprecatedProjectKey is the key of project for which integration needs to set up. Deprecated: use Repositories instead. Will be unsupported in v1.8.",
"type": "string"
Expand Down
6 changes: 5 additions & 1 deletion api/openapi-spec/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 30 additions & 2 deletions docs/APIs.md
Original file line number Diff line number Diff line change
Expand Up @@ -2845,8 +2845,8 @@ BitbucketBasicAuth

<p>

BasicAuth holds the information required to authenticate user via basic
auth mechanism
BitbucketBasicAuth holds the information required to authenticate user
via basic auth mechanism
</p>

</p>
Expand Down Expand Up @@ -3437,13 +3437,40 @@ under review.

<td>

<code>oneEventPerChange</code></br> <em> bool </em>
</td>

<td>

<em>(Optional)</em>
<p>

OneEventPerChange controls whether to process each change in a
repo:refs_changed webhook event as a separate event. This setting is
useful when multiple tags are pushed simultaneously for the same commit,
and each tag needs to independently trigger an action, such as a
distinct workflow in Argo Workflows. When enabled, the
BitbucketServerEventSource publishes an individual
BitbucketServerEventData for each change, ensuring independent
processing of each tag or reference update in a single webhook event.
</p>

</td>

</tr>

<tr>

<td>

<code>accessToken</code></br> <em>
<a href="https://v1-18.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#secretkeyselector-v1-core">
Kubernetes core/v1.SecretKeySelector </a> </em>
</td>

<td>

<em>(Optional)</em>
<p>

AccessToken is reference to K8s secret which holds the bitbucket api
Expand All @@ -3465,6 +3492,7 @@ Kubernetes core/v1.SecretKeySelector </a> </em>

<td>

<em>(Optional)</em>
<p>

WebhookSecret is reference to K8s secret which holds the bitbucket
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ require (
go.uber.org/ratelimit v0.3.1
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.25.0
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
google.golang.org/api v0.181.0
google.golang.org/grpc v1.63.2
Expand Down Expand Up @@ -303,6 +302,7 @@ require (
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
Expand Down
31 changes: 21 additions & 10 deletions pkg/apis/events/v1alpha1/eventsource_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -979,7 +979,7 @@ type BitbucketAuth struct {
OAuthToken *corev1.SecretKeySelector `json:"oauthToken,omitempty" protobuf:"bytes,2,opt,name=oauthToken"`
}

// BasicAuth holds the information required to authenticate user via basic auth mechanism
// BitbucketBasicAuth holds the information required to authenticate user via basic auth mechanism
type BitbucketBasicAuth struct {
// Username refers to the K8s secret that holds the username.
Username *corev1.SecretKeySelector `json:"username" protobuf:"bytes,1,name=username"`
Expand Down Expand Up @@ -1013,27 +1013,35 @@ type BitbucketServerEventSource struct {
// This helps in optimizing the event handling process by avoiding unnecessary triggers for branch reference changes that are already part of a pull request under review.
// +optional
SkipBranchRefsChangedOnOpenPR bool `json:"skipBranchRefsChangedOnOpenPR,omitempty" protobuf:"varint,7,opt,name=skipBranchRefsChangedOnOpenPR"`
// OneEventPerChange controls whether to process each change in a repo:refs_changed webhook event as a separate event. This setting is useful when multiple tags are
// pushed simultaneously for the same commit, and each tag needs to independently trigger an action, such as a distinct workflow in Argo Workflows. When enabled, the
// BitbucketServerEventSource publishes an individual BitbucketServerEventData for each change, ensuring independent processing of each tag or reference update in a
// single webhook event.
// +optional
OneEventPerChange bool `json:"oneEventPerChange,omitempty" protobuf:"varint,8,opt,name=oneEventPerChange"`
// AccessToken is reference to K8s secret which holds the bitbucket api access information.
AccessToken *corev1.SecretKeySelector `json:"accessToken,omitempty" protobuf:"bytes,8,opt,name=accessToken"`
// +optional
AccessToken *corev1.SecretKeySelector `json:"accessToken,omitempty" protobuf:"bytes,9,opt,name=accessToken"`
// WebhookSecret is reference to K8s secret which holds the bitbucket webhook secret (for HMAC validation).
WebhookSecret *corev1.SecretKeySelector `json:"webhookSecret,omitempty" protobuf:"bytes,9,opt,name=webhookSecret"`
// +optional
WebhookSecret *corev1.SecretKeySelector `json:"webhookSecret,omitempty" protobuf:"bytes,10,opt,name=webhookSecret"`
// BitbucketServerBaseURL is the base URL for API requests to a custom endpoint.
BitbucketServerBaseURL string `json:"bitbucketserverBaseURL" protobuf:"bytes,10,opt,name=bitbucketserverBaseURL"`
BitbucketServerBaseURL string `json:"bitbucketserverBaseURL" protobuf:"bytes,11,opt,name=bitbucketserverBaseURL"`
// DeleteHookOnFinish determines whether to delete the Bitbucket Server hook for the project once the event source is stopped.
// +optional
DeleteHookOnFinish bool `json:"deleteHookOnFinish,omitempty" protobuf:"varint,11,opt,name=deleteHookOnFinish"`
DeleteHookOnFinish bool `json:"deleteHookOnFinish,omitempty" protobuf:"varint,12,opt,name=deleteHookOnFinish"`
// Metadata holds the user defined metadata which will passed along the event payload.
// +optional
Metadata map[string]string `json:"metadata,omitempty" protobuf:"bytes,12,rep,name=metadata"`
Metadata map[string]string `json:"metadata,omitempty" protobuf:"bytes,13,rep,name=metadata"`
// Filter
// +optional
Filter *EventSourceFilter `json:"filter,omitempty" protobuf:"bytes,13,opt,name=filter"`
Filter *EventSourceFilter `json:"filter,omitempty" protobuf:"bytes,14,opt,name=filter"`
// TLS configuration for the bitbucketserver client.
// +optional
TLS *TLSConfig `json:"tls,omitempty" protobuf:"bytes,14,opt,name=tls"`
TLS *TLSConfig `json:"tls,omitempty" protobuf:"bytes,15,opt,name=tls"`
// CheckInterval is a duration in which to wait before checking that the webhooks exist, e.g. 1s, 30m, 2h... (defaults to 1m)
// +optional
CheckInterval string `json:"checkInterval" protobuf:"bytes,15,opt,name=checkInterval"`
CheckInterval string `json:"checkInterval" protobuf:"bytes,16,opt,name=checkInterval"`
}

type BitbucketServerRepository struct {
Expand All @@ -1044,7 +1052,10 @@ type BitbucketServerRepository struct {
}

func (b BitbucketServerEventSource) ShouldCreateWebhooks() bool {
return b.AccessToken != nil && b.Webhook != nil && b.Webhook.URL != ""
return b.AccessToken != nil &&
b.Webhook != nil &&
b.Webhook.URL != "" &&
(len(b.GetBitbucketServerRepositories()) > 0 || len(b.Projects) > 0)
}

func (b BitbucketServerEventSource) GetBitbucketServerRepositories() []BitbucketServerRepository {
Expand Down
91 changes: 91 additions & 0 deletions pkg/apis/events/v1alpha1/eventsource_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
)

func TestGetReplicas(t *testing.T) {
Expand Down Expand Up @@ -41,3 +42,93 @@ func TestGithubEventSourceGetRepositories(t *testing.T) {
}
assert.Equal(t, len(es.GetOwnedRepositories()), 2)
}

// TestShouldCreateWebhooks tests the ShouldCreateWebhooks function on the BitbucketServerEventSource type.
func TestShouldCreateWebhooks(t *testing.T) {
// Create a dummy SecretKeySelector for testing
dummySecretKeySelector := &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "dummy-secret",
},
Key: "token",
}

tests := []struct {
name string
b BitbucketServerEventSource
want bool
}{
{
name: "Valid Webhooks - Both Projects and Repositories",
b: BitbucketServerEventSource{
AccessToken: dummySecretKeySelector,
Webhook: &WebhookContext{URL: "http://example.com"},
Projects: []string{"Project1"},
Repositories: []BitbucketServerRepository{{ProjectKey: "Proj1", RepositorySlug: "Repo1"}},
},
want: true,
},
{
name: "No AccessToken",
b: BitbucketServerEventSource{
Webhook: &WebhookContext{URL: "http://example.com"},
Projects: []string{"Project1"},
Repositories: []BitbucketServerRepository{{ProjectKey: "Proj1", RepositorySlug: "Repo1"}},
},
want: false,
},
{
name: "No Webhook",
b: BitbucketServerEventSource{
AccessToken: dummySecretKeySelector,
Projects: []string{"Project1"},
Repositories: []BitbucketServerRepository{{ProjectKey: "Proj1", RepositorySlug: "Repo1"}},
},
want: false,
},
{
name: "No URL",
b: BitbucketServerEventSource{
AccessToken: dummySecretKeySelector,
Webhook: &WebhookContext{},
Projects: []string{"Project1"},
Repositories: []BitbucketServerRepository{{ProjectKey: "Proj1", RepositorySlug: "Repo1"}},
},
want: false,
},
{
name: "No Projects or Repositories",
b: BitbucketServerEventSource{
AccessToken: dummySecretKeySelector,
Webhook: &WebhookContext{URL: "http://example.com"},
},
want: false,
},
{
name: "Valid with Only Repositories",
b: BitbucketServerEventSource{
AccessToken: dummySecretKeySelector,
Webhook: &WebhookContext{URL: "http://example.com"},
Repositories: []BitbucketServerRepository{{ProjectKey: "Proj1", RepositorySlug: "Repo1"}},
},
want: true,
},
{
name: "Valid with Only Projects",
b: BitbucketServerEventSource{
AccessToken: dummySecretKeySelector,
Webhook: &WebhookContext{URL: "http://example.com"},
Projects: []string{"Project1"},
},
want: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.b.ShouldCreateWebhooks(); got != tt.want {
t.Errorf("%s: ShouldCreateWebhooks() = %v, want %v", tt.name, got, tt.want)
}
})
}
}
Loading

0 comments on commit 0d31a3e

Please sign in to comment.