-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Piotr Pawluk <[email protected]>
- Loading branch information
1 parent
a19a973
commit 9bd4c5d
Showing
14 changed files
with
576 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
151 changes: 151 additions & 0 deletions
151
runatlantis.io/docs/sending-notifications-via-webhooks.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
# Sending notifications via webhooks | ||
|
||
It is possible to send notifications to external systems whenever an apply is being done. | ||
|
||
You can make requests to any HTTP endpoint or send messages directly to your Slack channel. | ||
|
||
::: tip NOTE | ||
Currently only `apply` events are supported. | ||
::: | ||
|
||
## Configuration | ||
|
||
Webhooks are configured in Atlantis [server-side configuration](server-configuration.md). | ||
There can be many webhooks: sending notifications to different destinations or for different | ||
workspaces/branches. Here is example configuration to send Slack messages for every apply: | ||
|
||
```yaml | ||
webhooks: | ||
- event: apply | ||
kind: slack | ||
channel: my-channel-id | ||
``` | ||
If you are deploying Atlantis as a Helm chart, this can be implemented via the `config` parameter available for [chart customizations](https://github.com/runatlantis/helm-charts#customization): | ||
|
||
```yaml | ||
## Use Server Side Config, | ||
## ref: https://www.runatlantis.io/docs/server-configuration.html | ||
config: | | ||
--- | ||
webhooks: | ||
- event: apply | ||
kind: slack | ||
channel: my-channel-id | ||
``` | ||
|
||
### Filter on workspace/branch | ||
|
||
To limit notifications to particular workspaces or branches, use `workspace-regex` or `branch-regex` parameters. | ||
If the workspace **and** branch matches respective regex, an event will be sent. Note that empty regular expression | ||
(a result of unset parameter) matches every string. | ||
|
||
## Using HTTP webhooks | ||
|
||
You can send POST requests with JSON payload to any HTTP/HTTPS server. | ||
|
||
### Configuring Atlantis | ||
|
||
In your Atlantis [server-side configuration](server-configuration.md) you can add the following: | ||
|
||
```yaml | ||
webhooks: | ||
- event: apply | ||
kind: http | ||
url: https://example.com/hooks | ||
``` | ||
|
||
The `apply` event information will be POSTed to `https://example.com/hooks`. | ||
|
||
You can supply any additional headers with `--webhook-http-headers` parameter (or environment variable), | ||
for example for authentication purposes. See [webhook-http-headers](server-configuration.md#webhook-http-headers) for details. | ||
|
||
### JSON payload | ||
|
||
The payload is a JSON-marshalled [ApplyResult](https://pkg.go.dev/github.com/runatlantis/atlantis/server/events/webhooks#ApplyResult) struct. | ||
|
||
Example payload: | ||
|
||
```json | ||
{ | ||
"Workspace": "default", | ||
"Repo": { | ||
"FullName": "octocat/Hello-World", | ||
"Owner": "octocat", | ||
"Name": "Hello-World", | ||
"CloneURL": "https://:@github.com/octocat/Hello-World.git", | ||
"SanitizedCloneURL": "https://:<redacted>@github.com/octocat/Hello-World.git", | ||
"VCSHost": { | ||
"Hostname": "github.com", | ||
"Type": 0 | ||
} | ||
}, | ||
"Pull": { | ||
"Num": 2137, | ||
"HeadCommit": "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", | ||
"URL": "https://github.com/octocat/Hello-World/pull/2137", | ||
"HeadBranch": "feature/some-branch", | ||
"BaseBranch": "main", | ||
"Author": "octocat", | ||
"State": 0, | ||
"BaseRepo": { | ||
"FullName": "octocat/Hello-World", | ||
"Owner": "octocat", | ||
"Name": "Hello-World", | ||
"CloneURL": "https://:@github.com/octocat/Hello-World.git", | ||
"SanitizedCloneURL": "https://:<redacted>@github.com/octocat/Hello-World.git", | ||
"VCSHost": { | ||
"Hostname": "github.com", | ||
"Type": 0 | ||
} | ||
} | ||
}, | ||
"User": { | ||
"Username": "octocat", | ||
"Teams": null | ||
}, | ||
"Success": true, | ||
"Directory": "terraform/example", | ||
"ProjectName": "example-project" | ||
} | ||
``` | ||
|
||
## Using Slack hooks | ||
|
||
For this you'll need to: | ||
|
||
* Create a Bot user in Slack | ||
* Configure Atlantis to send notifications to Slack. | ||
|
||
### Configuring Slack for Atlantis | ||
|
||
* Go to [Slack: Apps](https://api.slack.com/apps) | ||
* Click the `Create New App` button | ||
* Select `From scratch` in the dialog that opens | ||
* Give it a name, e.g. `atlantis-bot`. | ||
* Select your Slack workspace | ||
* Click `Create App` | ||
* On the left go to `oAuth & Permissions` | ||
* Scroll down to Scopes | Bot Token Scopes and add the following OAuth scopes: | ||
* `channels:read` | ||
* `chat:write` | ||
* `groups:read` | ||
* `incoming-webhook` | ||
* `mpim:read` | ||
* Install the app onto your Slack workspace | ||
* Copy the `Bot User OAuth Token` and provide it to Atlantis by using `--slack-token=xoxb-xxxxxxxxxxx` or via the environment `ATLANTIS_SLACK_TOKEN=xoxb-xxxxxxxxxxx`. | ||
* Create a channel in your Slack workspace (e.g. `my-channel`) or use existing | ||
* Add the app to Created channel or existing channel ( click channel name then tab integrations, there Click "Add apps" | ||
|
||
### Configuring Atlantis | ||
|
||
After following the above steps it is time to configure Atlantis. Assuming you have already provided the `slack-token` (via parameter or environment variable) you can now instruct Atlantis to send `apply` events to Slack. | ||
|
||
In your Atlantis [server-side configuration](server-configuration.md) you can now add the following: | ||
|
||
```yaml | ||
webhooks: | ||
- event: apply | ||
kind: slack | ||
channel: my-channel-id | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package webhooks | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"regexp" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/runatlantis/atlantis/server/logging" | ||
) | ||
|
||
// HttpWebhook sends webhooks to any HTTP destination. | ||
type HttpWebhook struct { | ||
Client *http.Client | ||
WorkspaceRegex *regexp.Regexp | ||
BranchRegex *regexp.Regexp | ||
URL string | ||
} | ||
|
||
// Send sends the webhook to URL if workspace and branch matches their respective regex. | ||
func (h *HttpWebhook) Send(log logging.SimpleLogging, applyResult ApplyResult) error { | ||
if !h.WorkspaceRegex.MatchString(applyResult.Workspace) || !h.BranchRegex.MatchString(applyResult.Pull.BaseBranch) { | ||
return nil | ||
} | ||
if err := h.doSend(log, applyResult); err != nil { | ||
return errors.Wrap(err, fmt.Sprintf("sending webhook to %q", h.URL)) | ||
} | ||
return nil | ||
} | ||
|
||
func (h *HttpWebhook) doSend(_ logging.SimpleLogging, applyResult ApplyResult) error { | ||
body, err := json.Marshal(applyResult) | ||
if err != nil { | ||
return err | ||
} | ||
req, err := http.NewRequest("POST", h.URL, bytes.NewBuffer(body)) | ||
if err != nil { | ||
return err | ||
} | ||
req.Header.Set("Content-Type", "application/json") | ||
resp, err := h.Client.Do(req) | ||
if err != nil { | ||
return err | ||
} | ||
defer resp.Body.Close() | ||
if resp.StatusCode != http.StatusOK { | ||
respBody, _ := io.ReadAll(resp.Body) | ||
return fmt.Errorf("returned status code %d with response %q", resp.StatusCode, respBody) | ||
} | ||
return nil | ||
} | ||
|
||
// NewHttpClient creates a new HTTP client that will add arbitrary headers to every request. | ||
func NewHttpClient(headers map[string][]string) *http.Client { | ||
return &http.Client{ | ||
Transport: &AuthedTransport{ | ||
Base: http.DefaultTransport, | ||
Headers: headers, | ||
}, | ||
} | ||
} | ||
|
||
// AuthedTransport is a http.RoundTripper which wraps Base | ||
// adding arbitrary Headers to each request. | ||
type AuthedTransport struct { | ||
Base http.RoundTripper | ||
Headers map[string][]string | ||
} | ||
|
||
// RoundTrip handles each http request. | ||
func (t *AuthedTransport) RoundTrip(req *http.Request) (*http.Response, error) { | ||
for header, values := range t.Headers { | ||
for _, value := range values { | ||
req.Header.Add(header, value) | ||
} | ||
} | ||
return t.Base.RoundTrip(req) | ||
} |
Oops, something went wrong.