Skip to content

Commit e2d18b0

Browse files
committed
feat(audit-trail): add support for audit trail to terraform provider
Signed-off-by: Michal Wasilewski <[email protected]>
1 parent f997f2a commit e2d18b0

File tree

7 files changed

+243
-0
lines changed

7 files changed

+243
-0
lines changed

docs/resources/audit_trail_webhook.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "spacelift_audit_trail_webhook Resource - terraform-provider-spacelift"
4+
subcategory: ""
5+
description: |-
6+
spacelift_audit_trail_webhook represents a webhook endpoint to which Spacelift sends POST requests about audit events.
7+
---
8+
9+
# spacelift_audit_trail_webhook (Resource)
10+
11+
`spacelift_audit_trail_webhook` represents a webhook endpoint to which Spacelift sends POST requests about audit events.
12+
13+
## Example Usage
14+
15+
```terraform
16+
resource "spacelift_audit_trail_webhook" "example" {
17+
endpoint = "https://example.com"
18+
enabled = true
19+
secret = "mysecretkey"
20+
}
21+
```
22+
23+
<!-- schema generated by tfplugindocs -->
24+
## Schema
25+
26+
### Required
27+
28+
- `enabled` (Boolean) `enabled` determines whether the webhook is enabled. If it is not, Spacelift will not send any requests to the endpoint.
29+
- `endpoint` (String) `endpoint` is the URL to which Spacelift will send POST requests about audit events.
30+
- `secret` (String, Sensitive) `secret` is a secret that Spacelift will send with the request
31+
32+
### Optional
33+
34+
- `include_runs` (Boolean) `include_runs` determines whether the webhook should include information about the run that triggered the event.
35+
36+
### Read-Only
37+
38+
- `id` (String) The ID of this resource.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
resource "spacelift_audit_trail_webhook" "example" {
2+
endpoint = "https://example.com"
3+
enabled = true
4+
secret = "mysecretkey"
5+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package structs
2+
3+
type AuditTrailWebhook struct {
4+
Enabled bool `graphql:"enabled"`
5+
Endpoint string `graphql:"endpoint"`
6+
IncludeRuns bool `graphql:"includeRuns"`
7+
Secret string `graphql:"secret"`
8+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package structs
2+
3+
import "github.com/shurcooL/graphql"
4+
5+
type AuditTrailWebhookInput struct {
6+
Enabled graphql.Boolean `json:"enabled"`
7+
Endpoint graphql.String `json:"endpoint"`
8+
IncludeRuns graphql.Boolean `json:"includeRuns"`
9+
Secret graphql.String `json:"secret"`
10+
}

spacelift/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ func Provider(commit, version string) plugin.ProviderFunc {
9898
"spacelift_worker_pools": dataWorkerPools(),
9999
},
100100
ResourcesMap: map[string]*schema.Resource{
101+
"spacelift_audit_trail_webhook": resourceAuditTrailWebhook(),
101102
"spacelift_aws_role": resourceAWSRole(),
102103
"spacelift_aws_integration": resourceAWSIntegration(),
103104
"spacelift_aws_integration_attachment": resourceAWSIntegrationAttachment(),
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package spacelift
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
10+
"github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal"
11+
"github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/structs"
12+
)
13+
14+
func resourceAuditTrailWebhook() *schema.Resource {
15+
return &schema.Resource{
16+
Description: "" +
17+
"`spacelift_audit_trail_webhook` represents a webhook endpoint to which Spacelift " +
18+
"sends POST requests about audit events.",
19+
CreateContext: resourceAuditTrailWebhookCreate,
20+
ReadContext: resourceAuditTrailWebhookRead,
21+
UpdateContext: resourceAuditTrailWebhookUpdate,
22+
DeleteContext: resourceAuditTrailWebhookDelete,
23+
Importer: &schema.ResourceImporter{
24+
StateContext: schema.ImportStatePassthroughContext,
25+
},
26+
27+
Schema: map[string]*schema.Schema{
28+
"enabled": {
29+
Type: schema.TypeBool,
30+
Required: true,
31+
Description: "`enabled` determines whether the webhook is enabled. If it is not, " +
32+
"Spacelift will not send any requests to the endpoint.",
33+
},
34+
"endpoint": {
35+
Type: schema.TypeString,
36+
Required: true,
37+
Description: "`endpoint` is the URL to which Spacelift will send POST requests " +
38+
"about audit events.",
39+
},
40+
"include_runs": {
41+
Type: schema.TypeBool,
42+
Optional: true,
43+
Description: "`include_runs` determines whether the webhook should include " +
44+
"information about the run that triggered the event.",
45+
},
46+
"secret": {
47+
Type: schema.TypeString,
48+
Required: true,
49+
Sensitive: true,
50+
Description: "`secret` is a secret that Spacelift will send with the request",
51+
},
52+
},
53+
}
54+
}
55+
56+
func resourceAuditTrailWebhookCreate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics {
57+
var mutation struct {
58+
AuditTrailWebhook *structs.AuditTrailWebhook `graphql:"auditTrailSetWebhook(input: $input)"`
59+
}
60+
variables := map[string]interface{}{
61+
"input": structs.AuditTrailWebhookInput{
62+
Enabled: toBool(data.Get("enabled")),
63+
Endpoint: toString(data.Get("endpoint")),
64+
IncludeRuns: toBool(data.Get("include_runs")),
65+
Secret: toString(data.Get("secret")),
66+
},
67+
}
68+
if err := i.(*internal.Client).Mutate(ctx, "AuditTrailWebhookCreate", &mutation, variables); err != nil {
69+
return diag.Errorf("could not create audit trail webhook: %v", internal.FromSpaceliftError(err))
70+
}
71+
72+
data.SetId(time.Now().String())
73+
74+
return resourceAuditTrailWebhookRead(ctx, data, i)
75+
}
76+
77+
func resourceAuditTrailWebhookRead(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics {
78+
var query struct {
79+
AuditTrailWebhook *structs.AuditTrailWebhook `graphql:"auditTrailWebhook"`
80+
}
81+
if err := i.(*internal.Client).Query(ctx, "AuditTrailWebhookRead", &query, nil); err != nil {
82+
return diag.Errorf("could not query for audit trail webhook: %v", internal.FromSpaceliftError(err))
83+
}
84+
85+
if query.AuditTrailWebhook == nil {
86+
data.SetId("")
87+
return nil
88+
}
89+
90+
data.Set("enabled", query.AuditTrailWebhook.Enabled)
91+
data.Set("endpoint", query.AuditTrailWebhook.Endpoint)
92+
data.Set("include_runs", query.AuditTrailWebhook.IncludeRuns)
93+
data.Set("secret", query.AuditTrailWebhook.Secret)
94+
95+
return nil
96+
}
97+
98+
func resourceAuditTrailWebhookUpdate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics {
99+
var mutation struct {
100+
AuditTrailWebhook *structs.AuditTrailWebhook `graphql:"auditTrailSetWebhook(input: $input)"`
101+
}
102+
variables := map[string]interface{}{
103+
"input": structs.AuditTrailWebhookInput{
104+
Enabled: toBool(data.Get("enabled")),
105+
Endpoint: toString(data.Get("endpoint")),
106+
IncludeRuns: toBool(data.Get("include_runs")),
107+
Secret: toString(data.Get("secret")),
108+
},
109+
}
110+
if err := i.(*internal.Client).Mutate(ctx, "AuditTrailWebhookUpdate", &mutation, variables); err != nil {
111+
return diag.Errorf("could not update audit trail webhook: %v", internal.FromSpaceliftError(err))
112+
}
113+
114+
return resourceAuditTrailWebhookRead(ctx, data, i)
115+
}
116+
117+
func resourceAuditTrailWebhookDelete(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics {
118+
var mutation struct {
119+
AuditTrailWebhook *structs.AuditTrailWebhook `graphql:"auditTrailDeleteWebhook"`
120+
}
121+
if err := i.(*internal.Client).Mutate(ctx, "AuditTrailWebhookDelete", &mutation, nil); err != nil {
122+
return diag.Errorf("could not delete audit trail webhook: %v", internal.FromSpaceliftError(err))
123+
}
124+
125+
data.SetId("")
126+
127+
return nil
128+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package spacelift
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"testing"
7+
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
9+
10+
. "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/testhelpers"
11+
)
12+
13+
var auditTrailWebhookSimple = `
14+
resource "spacelift_audit_trail_webhook" "test" {
15+
enabled = true
16+
endpoint = "%s"
17+
include_runs = true
18+
secret = "secret"
19+
}
20+
`
21+
22+
func Test_resourceAuditTrailWebhook(t *testing.T) {
23+
const resourceName = "spacelift_audit_trail_webhook.test"
24+
25+
t.Run("creates an audit trail webhook without an error", func(t *testing.T) {
26+
testSteps(t, []resource.TestStep{
27+
{
28+
Config: fmt.Sprintf(auditTrailWebhookSimple, "https://example.com"),
29+
Check: Resource(
30+
resourceName,
31+
Attribute("enabled", Equals("true")),
32+
Attribute("endpoint", Equals("https://example.com")),
33+
Attribute("include_runs", Equals("true")),
34+
Attribute("secret", Equals("secret")),
35+
),
36+
},
37+
{
38+
ResourceName: resourceName,
39+
ImportState: true,
40+
ImportStateVerify: true,
41+
},
42+
})
43+
})
44+
45+
t.Run("endpoint has to exist", func(t *testing.T) {
46+
testSteps(t, []resource.TestStep{
47+
{
48+
Config: fmt.Sprintf(auditTrailWebhookSimple, "https://invalidendpoint.com/"),
49+
ExpectError: regexp.MustCompile(`could not send webhook to given endpoint`),
50+
},
51+
})
52+
})
53+
}

0 commit comments

Comments
 (0)