mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 01:28:45 +01:00
feat: Add Support for AzureDevops Webhooks (#14969)
* feat: Add Support for AzureDevops Webhooks Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com> * document azure devops webhook configuration Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com> --------- Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
This commit is contained in:
committed by
GitHub
parent
5c76d8f027
commit
19de408dbc
@@ -19,9 +19,9 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
argosettings "github.com/argoproj/argo-cd/v2/util/settings"
|
||||
|
||||
"github.com/go-playground/webhooks/v6/github"
|
||||
"github.com/go-playground/webhooks/v6/gitlab"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/go-playground/webhooks.v5/github"
|
||||
"gopkg.in/go-playground/webhooks.v5/gitlab"
|
||||
)
|
||||
|
||||
type WebhookHandler struct {
|
||||
|
||||
BIN
docs/assets/azure-devops-webhook-config.png
Normal file
BIN
docs/assets/azure-devops-webhook-config.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
@@ -4,7 +4,7 @@
|
||||
|
||||
Argo CD polls Git repositories every three minutes to detect changes to the manifests. To eliminate
|
||||
this delay from polling, the API server can be configured to receive webhook events. Argo CD supports
|
||||
Git webhook notifications from GitHub, GitLab, Bitbucket, Bitbucket Server and Gogs. The following explains how to configure
|
||||
Git webhook notifications from GitHub, GitLab, Bitbucket, Bitbucket Server, Azure DevOps and Gogs. The following explains how to configure
|
||||
a Git webhook for GitHub, but the same process should be applicable to other providers.
|
||||
|
||||
!!! note
|
||||
@@ -12,19 +12,28 @@ a Git webhook for GitHub, but the same process should be applicable to other pro
|
||||
the same. A hook event for a push to branch `x` will trigger a refresh for an app pointing at the same repo with
|
||||
`targetRevision: refs/tags/x`.
|
||||
|
||||
### 1. Create The WebHook In The Git Provider
|
||||
## 1. Create The WebHook In The Git Provider
|
||||
|
||||
In your Git provider, navigate to the settings page where webhooks can be configured. The payload
|
||||
URL configured in the Git provider should use the `/api/webhook` endpoint of your Argo CD instance
|
||||
(e.g. `https://argocd.example.com/api/webhook`). If you wish to use a shared secret, input an
|
||||
arbitrary value in the secret. This value will be used when configuring the webhook in the next step.
|
||||
|
||||
## Github
|
||||
|
||||

|
||||
|
||||
!!! note
|
||||
When creating the webhook in GitHub, the "Content type" needs to be set to "application/json". The default value "application/x-www-form-urlencoded" is not supported by the library used to handle the hooks
|
||||
|
||||
### 2. Configure Argo CD With The WebHook Secret (Optional)
|
||||
## Azure DevOps
|
||||
|
||||

|
||||
|
||||
Azure DevOps optionally supports securing the webhook using basic authentication. To use it, specify the username and password in the webhook configuration and configure the same username/password in `argocd-secret` Kubernetes secret in
|
||||
`webhook.azuredevops.username` and `webhook.azuredevops.password` keys.
|
||||
|
||||
## 2. Configure Argo CD With The WebHook Secret (Optional)
|
||||
|
||||
Configuring a webhook shared secret is optional, since Argo CD will still refresh applications
|
||||
related to the Git repository, even with unauthenticated webhook events. This is safe to do since
|
||||
@@ -36,12 +45,14 @@ In the `argocd-secret` kubernetes secret, configure one of the following keys wi
|
||||
provider's webhook secret configured in step 1.
|
||||
|
||||
| Provider | K8s Secret Key |
|
||||
|-----------------| ---------------------------------|
|
||||
|-----------------|----------------------------------|
|
||||
| GitHub | `webhook.github.secret` |
|
||||
| GitLab | `webhook.gitlab.secret` |
|
||||
| BitBucket | `webhook.bitbucket.uuid` |
|
||||
| BitBucketServer | `webhook.bitbucketserver.secret` |
|
||||
| Gogs | `webhook.gogs.secret` |
|
||||
| Azure DevOps | `webhook.azuredevops.username` |
|
||||
| | `webhook.azuredevops.password` |
|
||||
|
||||
Edit the Argo CD kubernetes secret:
|
||||
|
||||
@@ -79,6 +90,10 @@ stringData:
|
||||
|
||||
# gogs server webhook secret
|
||||
webhook.gogs.secret: shhhh! it's a gogs server secret
|
||||
|
||||
# azuredevops username and password
|
||||
webhook.azuredevops.username: admin
|
||||
webhook.azuredevops.password: secret-password
|
||||
```
|
||||
|
||||
After saving, the changes should take effect automatically.
|
||||
|
||||
4
go.mod
4
go.mod
@@ -29,9 +29,10 @@ require (
|
||||
github.com/go-logr/logr v1.2.4
|
||||
github.com/go-openapi/loads v0.21.2
|
||||
github.com/go-openapi/runtime v0.26.0
|
||||
github.com/go-playground/webhooks/v6 v6.2.1-0.20230808162451-10570b0a59e8
|
||||
github.com/go-redis/cache/v9 v9.0.0
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogits/go-gogs-client v0.0.0-20190616193657-5a05380e4bc2
|
||||
github.com/gogits/go-gogs-client v0.0.0-20200905025246-8bb8a50cb355
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang/protobuf v1.5.3
|
||||
@@ -85,7 +86,6 @@ require (
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc
|
||||
google.golang.org/grpc v1.56.2
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gopkg.in/go-playground/webhooks.v5 v5.17.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
k8s.io/api v0.24.2
|
||||
|
||||
8
go.sum
8
go.sum
@@ -1048,6 +1048,8 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-playground/webhooks/v6 v6.2.1-0.20230808162451-10570b0a59e8 h1:QDFjrpOZagU8KEpSCF0WvBKOGq2GYuVZ4ZDg/gelrEE=
|
||||
github.com/go-playground/webhooks/v6 v6.2.1-0.20230808162451-10570b0a59e8/go.mod h1:GCocmfMtpJdkEOM1uG9p2nXzg1kY5X/LtvQgtPHUaaA=
|
||||
github.com/go-redis/cache/v9 v9.0.0 h1:0thdtFo0xJi0/WXbRVu8B066z8OvVymXTJGaXrVWnN0=
|
||||
github.com/go-redis/cache/v9 v9.0.0/go.mod h1:cMwi1N8ASBOufbIvk7cdXe2PbPjK/WMRL95FFHWsSgI=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
@@ -1094,8 +1096,8 @@ github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogits/go-gogs-client v0.0.0-20190616193657-5a05380e4bc2 h1:BbwX8wsMRDZRdNYxAna+4ls3wvMKJyn4PT6Zk1CPxP4=
|
||||
github.com/gogits/go-gogs-client v0.0.0-20190616193657-5a05380e4bc2/go.mod h1:cY2AIrMgHm6oOHmR7jY+9TtjzSjQ3iG7tURJG3Y6XH0=
|
||||
github.com/gogits/go-gogs-client v0.0.0-20200905025246-8bb8a50cb355 h1:HTVNOdTWO/gHYeFnr/HwpYwY6tgMcYd+Rgf1XrHnORY=
|
||||
github.com/gogits/go-gogs-client v0.0.0-20200905025246-8bb8a50cb355/go.mod h1:cY2AIrMgHm6oOHmR7jY+9TtjzSjQ3iG7tURJG3Y6XH0=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
@@ -2791,8 +2793,6 @@ gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/go-playground/webhooks.v5 v5.17.0 h1:truBced5ZmkiNKK47cM8bMe86wUSjNks7SFMuNKwzlc=
|
||||
gopkg.in/go-playground/webhooks.v5 v5.17.0/go.mod h1:LZbya/qLVdbqDR1aKrGuWV6qbia2zCYSR5dpom2SInQ=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
|
||||
@@ -71,6 +71,10 @@ type ArgoCDSettings struct {
|
||||
WebhookBitbucketServerSecret string `json:"webhookBitbucketServerSecret,omitempty"`
|
||||
// WebhookGogsSecret holds the shared secret for authenticating Gogs webhook events
|
||||
WebhookGogsSecret string `json:"webhookGogsSecret,omitempty"`
|
||||
// WebhookAzureDevOpsUsername holds the username for authenticating Azure DevOps webhook events
|
||||
WebhookAzureDevOpsUsername string `json:"webhookAzureDevOpsUsername,omitempty"`
|
||||
// WebhookAzureDevOpsPassword holds the password for authenticating Azure DevOps webhook events
|
||||
WebhookAzureDevOpsPassword string `json:"webhookAzureDevOpsPassword,omitempty"`
|
||||
// Secrets holds all secrets in argocd-secret as a map[string]string
|
||||
Secrets map[string]string `json:"secrets,omitempty"`
|
||||
// KustomizeBuildOptions is a string of kustomize build parameters
|
||||
@@ -411,6 +415,10 @@ const (
|
||||
settingsWebhookBitbucketServerSecretKey = "webhook.bitbucketserver.secret"
|
||||
// settingsWebhookGogsSecret is the key for Gogs webhook secret
|
||||
settingsWebhookGogsSecretKey = "webhook.gogs.secret"
|
||||
// settingsWebhookAzureDevOpsUsernameKey is the key for Azure DevOps webhook username
|
||||
settingsWebhookAzureDevOpsUsernameKey = "webhook.azuredevops.username"
|
||||
// settingsWebhookAzureDevOpsPasswordKey is the key for Azure DevOps webhook password
|
||||
settingsWebhookAzureDevOpsPasswordKey = "webhook.azuredevops.password"
|
||||
// settingsApplicationInstanceLabelKey is the key to configure injected app instance label key
|
||||
settingsApplicationInstanceLabelKey = "application.instanceLabelKey"
|
||||
// settingsResourceTrackingMethodKey is the key to configure tracking method for application resources
|
||||
@@ -1457,6 +1465,12 @@ func (mgr *SettingsManager) updateSettingsFromSecret(settings *ArgoCDSettings, a
|
||||
if gogsWebhookSecret := argoCDSecret.Data[settingsWebhookGogsSecretKey]; len(gogsWebhookSecret) > 0 {
|
||||
settings.WebhookGogsSecret = string(gogsWebhookSecret)
|
||||
}
|
||||
if azureDevOpsUsername := argoCDSecret.Data[settingsWebhookAzureDevOpsUsernameKey]; len(azureDevOpsUsername) > 0 {
|
||||
settings.WebhookAzureDevOpsUsername = string(azureDevOpsUsername)
|
||||
}
|
||||
if azureDevOpsPassword := argoCDSecret.Data[settingsWebhookAzureDevOpsPasswordKey]; len(azureDevOpsPassword) > 0 {
|
||||
settings.WebhookAzureDevOpsPassword = string(azureDevOpsPassword)
|
||||
}
|
||||
|
||||
// The TLS certificate may be externally managed. We try to load it from an
|
||||
// external secret first. If the external secret doesn't exist, we either
|
||||
@@ -1576,6 +1590,12 @@ func (mgr *SettingsManager) SaveSettings(settings *ArgoCDSettings) error {
|
||||
if settings.WebhookGogsSecret != "" {
|
||||
argoCDSecret.Data[settingsWebhookGogsSecretKey] = []byte(settings.WebhookGogsSecret)
|
||||
}
|
||||
if settings.WebhookAzureDevOpsUsername != "" {
|
||||
argoCDSecret.Data[settingsWebhookAzureDevOpsUsernameKey] = []byte(settings.WebhookAzureDevOpsUsername)
|
||||
}
|
||||
if settings.WebhookAzureDevOpsPassword != "" {
|
||||
argoCDSecret.Data[settingsWebhookAzureDevOpsPasswordKey] = []byte(settings.WebhookAzureDevOpsPassword)
|
||||
}
|
||||
// we only write the certificate to the secret if it's not externally
|
||||
// managed.
|
||||
if settings.Certificate != nil && !settings.CertificateIsExternal {
|
||||
|
||||
107
util/webhook/testdata/azuredevops-git-push-event.json
vendored
Normal file
107
util/webhook/testdata/azuredevops-git-push-event.json
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"subscriptionId": "8fd412f1-9873-4b45-8854-655b1b8a2eff",
|
||||
"notificationId": 2,
|
||||
"id": "09b0b950-47fa-4f45-8b65-5a22686314f8",
|
||||
"eventType": "git.push",
|
||||
"publisherId": "tfs",
|
||||
"message": {
|
||||
"text": "Alexander Matyushentsev pushed updates to alex-test:master\r\n(https://dev.azure.com/alexander0053/alex-test/_git/alex-test/#version=GBmaster)",
|
||||
"html": "Alexander Matyushentsev pushed updates to <a href=\"https://dev.azure.com/alexander0053/alex-test/_git/alex-test/\">alex-test</a>:<a href=\"https://dev.azure.com/alexander0053/alex-test/_git/alex-test/#version=GBmaster\">master</a>",
|
||||
"markdown": "Alexander Matyushentsev pushed updates to [alex-test](https://dev.azure.com/alexander0053/alex-test/_git/alex-test/):[master](https://dev.azure.com/alexander0053/alex-test/_git/alex-test/#version=GBmaster)"
|
||||
},
|
||||
"detailedMessage": {
|
||||
"text": "Alexander Matyushentsev pushed a commit to alex-test:master\r\n - draft 298a79aa (https://dev.azure.com/alexander0053/alex-test/_git/alex-test/commit/298a79aa1552799a70718a0ee914d153d5a1a76b)",
|
||||
"html": "Alexander Matyushentsev pushed a commit to <a href=\"https://dev.azure.com/alexander0053/alex-test/_git/alex-test/\">alex-test</a>:<a href=\"https://dev.azure.com/alexander0053/alex-test/_git/alex-test/#version=GBmaster\">master</a>\r\n<ul>\r\n<li>draft <a href=\"https://dev.azure.com/alexander0053/alex-test/_git/alex-test/commit/298a79aa1552799a70718a0ee914d153d5a1a76b\">298a79aa</a></li>\r\n</ul>",
|
||||
"markdown": "Alexander Matyushentsev pushed a commit to [alex-test](https://dev.azure.com/alexander0053/alex-test/_git/alex-test/):[master](https://dev.azure.com/alexander0053/alex-test/_git/alex-test/#version=GBmaster)\r\n* draft [298a79aa](https://dev.azure.com/alexander0053/alex-test/_git/alex-test/commit/298a79aa1552799a70718a0ee914d153d5a1a76b)"
|
||||
},
|
||||
"resource": {
|
||||
"commits": [
|
||||
{
|
||||
"commitId": "298a79aa1552799a70718a0ee914d153d5a1a76b",
|
||||
"author": {
|
||||
"name": "Alexander Matyushentsev",
|
||||
"email": "AMatyushentsev@gmail.com",
|
||||
"date": "2023-08-09T00:45:39Z"
|
||||
},
|
||||
"committer": {
|
||||
"name": "Alexander Matyushentsev",
|
||||
"email": "AMatyushentsev@gmail.com",
|
||||
"date": "2023-08-09T00:45:39Z"
|
||||
},
|
||||
"comment": "draft\n\nSigned-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>",
|
||||
"url": "https://dev.azure.com/alexander0053/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6/commits/298a79aa1552799a70718a0ee914d153d5a1a76b"
|
||||
}
|
||||
],
|
||||
"refUpdates": [
|
||||
{
|
||||
"name": "refs/heads/master",
|
||||
"oldObjectId": "fa51eeb1e50b98293ce281e6d5492b9decae613b",
|
||||
"newObjectId": "298a79aa1552799a70718a0ee914d153d5a1a76b"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"id": "ba2967cc-02c2-414c-8d10-1b99197cbaa6",
|
||||
"name": "alex-test",
|
||||
"url": "https://dev.azure.com/alexander0053/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6",
|
||||
"project": {
|
||||
"id": "ab1c194f-94fa-4d1a-87ff-e9458637d060",
|
||||
"name": "alex-test",
|
||||
"url": "https://dev.azure.com/alexander0053/_apis/projects/ab1c194f-94fa-4d1a-87ff-e9458637d060",
|
||||
"state": "wellFormed",
|
||||
"visibility": "unchanged",
|
||||
"lastUpdateTime": "0001-01-01T00:00:00"
|
||||
},
|
||||
"defaultBranch": "refs/heads/master",
|
||||
"remoteUrl": "https://dev.azure.com/alexander0053/alex-test/_git/alex-test"
|
||||
},
|
||||
"pushedBy": {
|
||||
"displayName": "Alexander Matyushentsev",
|
||||
"url": "https://spsprodcus4.vssps.visualstudio.com/A7a73fd0c-d080-434d-a8b4-0b4c0217e290/_apis/Identities/07220d5e-521c-683d-982c-726e80086d08",
|
||||
"_links": {
|
||||
"avatar": {
|
||||
"href": "https://dev.azure.com/alexander0053/_apis/GraphProfile/MemberAvatars/aad.MDcyMjBkNWUtNTIxYy03ODNkLTk4MmMtNzI2ZTgwMDg2ZDA4"
|
||||
}
|
||||
},
|
||||
"id": "07220d5e-521c-683d-982c-726e80086d08",
|
||||
"uniqueName": "alexander@akuity.onmicrosoft.com",
|
||||
"imageUrl": "https://dev.azure.com/alexander0053/_api/_common/identityImage?id=07220d5e-521c-683d-982c-726e80086d08",
|
||||
"descriptor": "aad.MDcyMjBkNWUtNTIxYy03ODNkLTk4MmMtNzI2ZTgwMDg2ZDA4"
|
||||
},
|
||||
"pushId": 4,
|
||||
"date": "2023-08-09T00:45:42.8315767Z",
|
||||
"url": "https://dev.azure.com/alexander0053/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6/pushes/4",
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "https://dev.azure.com/alexander0053/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6/pushes/4"
|
||||
},
|
||||
"repository": {
|
||||
"href": "https://dev.azure.com/alexander0053/ab1c194f-94fa-4d1a-87ff-e9458637d060/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6"
|
||||
},
|
||||
"commits": {
|
||||
"href": "https://dev.azure.com/alexander0053/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6/pushes/4/commits"
|
||||
},
|
||||
"pusher": {
|
||||
"href": "https://spsprodcus4.vssps.visualstudio.com/A7a73fd0c-d080-434d-a8b4-0b4c0217e290/_apis/Identities/07220d5e-521c-683d-982c-726e80086d08"
|
||||
},
|
||||
"refs": {
|
||||
"href": "https://dev.azure.com/alexander0053/ab1c194f-94fa-4d1a-87ff-e9458637d060/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6/refs/heads/master"
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourceVersion": "1.0",
|
||||
"resourceContainers": {
|
||||
"collection": {
|
||||
"id": "d54a3f95-82a0-47c4-8444-00da7391d976",
|
||||
"baseUrl": "https://dev.azure.com/alexander0053/"
|
||||
},
|
||||
"account": {
|
||||
"id": "7a73fd0c-d080-434d-a8b4-0b4c0217e290",
|
||||
"baseUrl": "https://dev.azure.com/alexander0053/"
|
||||
},
|
||||
"project": {
|
||||
"id": "ab1c194f-94fa-4d1a-87ff-e9458637d060",
|
||||
"baseUrl": "https://dev.azure.com/alexander0053/"
|
||||
}
|
||||
},
|
||||
"createdDate": "2023-08-09T00:45:49.3448928Z"
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/argoproj/argo-cd/v2/util/glob"
|
||||
"html"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -12,13 +11,14 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-playground/webhooks/v6/azuredevops"
|
||||
"github.com/go-playground/webhooks/v6/bitbucket"
|
||||
bitbucketserver "github.com/go-playground/webhooks/v6/bitbucket-server"
|
||||
"github.com/go-playground/webhooks/v6/github"
|
||||
"github.com/go-playground/webhooks/v6/gitlab"
|
||||
"github.com/go-playground/webhooks/v6/gogs"
|
||||
gogsclient "github.com/gogits/go-gogs-client"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/go-playground/webhooks.v5/bitbucket"
|
||||
bitbucketserver "gopkg.in/go-playground/webhooks.v5/bitbucket-server"
|
||||
"gopkg.in/go-playground/webhooks.v5/github"
|
||||
"gopkg.in/go-playground/webhooks.v5/gitlab"
|
||||
"gopkg.in/go-playground/webhooks.v5/gogs"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
servercache "github.com/argoproj/argo-cd/v2/server/cache"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/glob"
|
||||
"github.com/argoproj/argo-cd/v2/util/security"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
@@ -41,21 +42,26 @@ type settingsSource interface {
|
||||
// https://github.com/shadow-maint/shadow/blob/master/libmisc/chkname.c#L36
|
||||
const usernameRegex = `[a-zA-Z0-9_\.][a-zA-Z0-9_\.-]{0,30}[a-zA-Z0-9_\.\$-]?`
|
||||
|
||||
var _ settingsSource = &settings.SettingsManager{}
|
||||
var (
|
||||
_ settingsSource = &settings.SettingsManager{}
|
||||
errBasicAuthVerificationFailed = errors.New("basic auth verification failed")
|
||||
)
|
||||
|
||||
type ArgoCDWebhookHandler struct {
|
||||
repoCache *cache.Cache
|
||||
serverCache *servercache.Cache
|
||||
db db.ArgoDB
|
||||
ns string
|
||||
appNs []string
|
||||
appClientset appclientset.Interface
|
||||
github *github.Webhook
|
||||
gitlab *gitlab.Webhook
|
||||
bitbucket *bitbucket.Webhook
|
||||
bitbucketserver *bitbucketserver.Webhook
|
||||
gogs *gogs.Webhook
|
||||
settingsSrc settingsSource
|
||||
repoCache *cache.Cache
|
||||
serverCache *servercache.Cache
|
||||
db db.ArgoDB
|
||||
ns string
|
||||
appNs []string
|
||||
appClientset appclientset.Interface
|
||||
github *github.Webhook
|
||||
gitlab *gitlab.Webhook
|
||||
bitbucket *bitbucket.Webhook
|
||||
bitbucketserver *bitbucketserver.Webhook
|
||||
azuredevops *azuredevops.Webhook
|
||||
azuredevopsAuthHandler func(r *http.Request) error
|
||||
gogs *gogs.Webhook
|
||||
settingsSrc settingsSource
|
||||
}
|
||||
|
||||
func NewHandler(namespace string, applicationNamespaces []string, appClientset appclientset.Interface, set *settings.ArgoCDSettings, settingsSrc settingsSource, repoCache *cache.Cache, serverCache *servercache.Cache, argoDB db.ArgoDB) *ArgoCDWebhookHandler {
|
||||
@@ -79,20 +85,35 @@ func NewHandler(namespace string, applicationNamespaces []string, appClientset a
|
||||
if err != nil {
|
||||
log.Warnf("Unable to init the Gogs webhook")
|
||||
}
|
||||
azuredevopsWebhook, err := azuredevops.New()
|
||||
if err != nil {
|
||||
log.Warnf("Unable to init the Azure DevOps webhook")
|
||||
}
|
||||
azuredevopsAuthHandler := func(r *http.Request) error {
|
||||
if set.WebhookAzureDevOpsUsername != "" && set.WebhookAzureDevOpsPassword != "" {
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok || username != set.WebhookAzureDevOpsUsername || password != set.WebhookAzureDevOpsPassword {
|
||||
return errBasicAuthVerificationFailed
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
acdWebhook := ArgoCDWebhookHandler{
|
||||
ns: namespace,
|
||||
appNs: applicationNamespaces,
|
||||
appClientset: appClientset,
|
||||
github: githubWebhook,
|
||||
gitlab: gitlabWebhook,
|
||||
bitbucket: bitbucketWebhook,
|
||||
bitbucketserver: bitbucketserverWebhook,
|
||||
gogs: gogsWebhook,
|
||||
settingsSrc: settingsSrc,
|
||||
repoCache: repoCache,
|
||||
serverCache: serverCache,
|
||||
db: argoDB,
|
||||
ns: namespace,
|
||||
appNs: applicationNamespaces,
|
||||
appClientset: appClientset,
|
||||
github: githubWebhook,
|
||||
gitlab: gitlabWebhook,
|
||||
bitbucket: bitbucketWebhook,
|
||||
bitbucketserver: bitbucketserverWebhook,
|
||||
azuredevops: azuredevopsWebhook,
|
||||
azuredevopsAuthHandler: azuredevopsAuthHandler,
|
||||
gogs: gogsWebhook,
|
||||
settingsSrc: settingsSrc,
|
||||
repoCache: repoCache,
|
||||
serverCache: serverCache,
|
||||
db: argoDB,
|
||||
}
|
||||
|
||||
return &acdWebhook
|
||||
@@ -107,6 +128,14 @@ func parseRevision(ref string) string {
|
||||
// the revision, and whether or not this affected origin/HEAD (the default branch of the repository)
|
||||
func affectedRevisionInfo(payloadIf interface{}) (webURLs []string, revision string, change changeInfo, touchedHead bool, changedFiles []string) {
|
||||
switch payload := payloadIf.(type) {
|
||||
case azuredevops.GitPushEvent:
|
||||
// See: https://learn.microsoft.com/en-us/azure/devops/service-hooks/events?view=azure-devops#git.push
|
||||
webURLs = append(webURLs, payload.Resource.Repository.RemoteURL)
|
||||
revision = parseRevision(payload.Resource.RefUpdates[0].Name)
|
||||
change.shaAfter = parseRevision(payload.Resource.RefUpdates[0].NewObjectID)
|
||||
change.shaBefore = parseRevision(payload.Resource.RefUpdates[0].OldObjectID)
|
||||
touchedHead = payload.Resource.RefUpdates[0].Name == payload.Resource.Repository.DefaultBranch
|
||||
// unfortunately, Azure DevOps doesn't provide a list of changed files
|
||||
case github.PushPayload:
|
||||
// See: https://developer.github.com/v3/activity/events/types/#pushevent
|
||||
webURLs = append(webURLs, payload.Repository.HTMLURL)
|
||||
@@ -430,6 +459,14 @@ func (a *ArgoCDWebhookHandler) Handler(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case r.Header.Get("X-Vss-Activityid") != "":
|
||||
if err = a.azuredevopsAuthHandler(r); err != nil {
|
||||
if errors.Is(err, errBasicAuthVerificationFailed) {
|
||||
log.WithField(common.SecurityField, common.SecurityHigh).Infof("Azure DevOps webhook basic auth verification failed")
|
||||
}
|
||||
} else {
|
||||
payload, err = a.azuredevops.Parse(r, azuredevops.GitPushEventType)
|
||||
}
|
||||
//Gogs needs to be checked before GitHub since it carries both Gogs and (incompatible) GitHub headers
|
||||
case r.Header.Get("X-Gogs-Event") != "":
|
||||
payload, err = a.gogs.Parse(r, gogs.PushEvent)
|
||||
|
||||
@@ -5,18 +5,19 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/go-playground/webhooks/v6/bitbucket"
|
||||
bitbucketserver "github.com/go-playground/webhooks/v6/bitbucket-server"
|
||||
"github.com/go-playground/webhooks/v6/github"
|
||||
"github.com/go-playground/webhooks/v6/gitlab"
|
||||
gogsclient "github.com/gogits/go-gogs-client"
|
||||
"gopkg.in/go-playground/webhooks.v5/bitbucket"
|
||||
bitbucketserver "gopkg.in/go-playground/webhooks.v5/bitbucket-server"
|
||||
"gopkg.in/go-playground/webhooks.v5/github"
|
||||
"gopkg.in/go-playground/webhooks.v5/gitlab"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kubetesting "k8s.io/client-go/testing"
|
||||
|
||||
@@ -89,6 +90,22 @@ func TestGitHubCommitEvent(t *testing.T) {
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
func TestAzureDevOpsCommitEvent(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
h := NewMockHandler(nil, []string{})
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
|
||||
req.Header.Set("X-Vss-Activityid", "abc")
|
||||
eventJSON, err := os.ReadFile("testdata/azuredevops-git-push-event.json")
|
||||
assert.NoError(t, err)
|
||||
req.Body = io.NopCloser(bytes.NewReader(eventJSON))
|
||||
w := httptest.NewRecorder()
|
||||
h.Handler(w, req)
|
||||
assert.Equal(t, w.Code, http.StatusOK)
|
||||
expectedLogResult := "Received push event repo: https://dev.azure.com/alexander0053/alex-test/_git/alex-test, revision: master, touchedHead: true"
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
// TestGitHubCommitEvent_MultiSource_Refresh makes sure that a webhook will refresh a multi-source app when at least
|
||||
// one source matches.
|
||||
func TestGitHubCommitEvent_MultiSource_Refresh(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user