Skip to content

Commit

Permalink
feat(audit-trail): add support for audit trail to terraform provider
Browse files Browse the repository at this point in the history
Signed-off-by: Michal Wasilewski <[email protected]>
  • Loading branch information
mwasilew2 committed Jan 25, 2024
1 parent f997f2a commit e2d18b0
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 0 deletions.
38 changes: 38 additions & 0 deletions docs/resources/audit_trail_webhook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "spacelift_audit_trail_webhook Resource - terraform-provider-spacelift"
subcategory: ""
description: |-
spacelift_audit_trail_webhook represents a webhook endpoint to which Spacelift sends POST requests about audit events.
---

# spacelift_audit_trail_webhook (Resource)

`spacelift_audit_trail_webhook` represents a webhook endpoint to which Spacelift sends POST requests about audit events.

## Example Usage

```terraform
resource "spacelift_audit_trail_webhook" "example" {
endpoint = "https://example.com"
enabled = true
secret = "mysecretkey"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `enabled` (Boolean) `enabled` determines whether the webhook is enabled. If it is not, Spacelift will not send any requests to the endpoint.
- `endpoint` (String) `endpoint` is the URL to which Spacelift will send POST requests about audit events.
- `secret` (String, Sensitive) `secret` is a secret that Spacelift will send with the request

### Optional

- `include_runs` (Boolean) `include_runs` determines whether the webhook should include information about the run that triggered the event.

### Read-Only

- `id` (String) The ID of this resource.
5 changes: 5 additions & 0 deletions examples/resources/spacelift_audit_trail_webhook/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
resource "spacelift_audit_trail_webhook" "example" {
endpoint = "https://example.com"
enabled = true
secret = "mysecretkey"
}
8 changes: 8 additions & 0 deletions spacelift/internal/structs/audit_trail_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package structs

type AuditTrailWebhook struct {
Enabled bool `graphql:"enabled"`
Endpoint string `graphql:"endpoint"`
IncludeRuns bool `graphql:"includeRuns"`
Secret string `graphql:"secret"`
}
10 changes: 10 additions & 0 deletions spacelift/internal/structs/audit_trail_webhook_input.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package structs

import "github.com/shurcooL/graphql"

type AuditTrailWebhookInput struct {
Enabled graphql.Boolean `json:"enabled"`
Endpoint graphql.String `json:"endpoint"`
IncludeRuns graphql.Boolean `json:"includeRuns"`
Secret graphql.String `json:"secret"`
}
1 change: 1 addition & 0 deletions spacelift/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ func Provider(commit, version string) plugin.ProviderFunc {
"spacelift_worker_pools": dataWorkerPools(),
},
ResourcesMap: map[string]*schema.Resource{
"spacelift_audit_trail_webhook": resourceAuditTrailWebhook(),
"spacelift_aws_role": resourceAWSRole(),
"spacelift_aws_integration": resourceAWSIntegration(),
"spacelift_aws_integration_attachment": resourceAWSIntegrationAttachment(),
Expand Down
128 changes: 128 additions & 0 deletions spacelift/resource_audit_trail_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package spacelift

import (
"context"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal"
"github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/structs"
)

func resourceAuditTrailWebhook() *schema.Resource {
return &schema.Resource{
Description: "" +
"`spacelift_audit_trail_webhook` represents a webhook endpoint to which Spacelift " +
"sends POST requests about audit events.",
CreateContext: resourceAuditTrailWebhookCreate,
ReadContext: resourceAuditTrailWebhookRead,
UpdateContext: resourceAuditTrailWebhookUpdate,
DeleteContext: resourceAuditTrailWebhookDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"enabled": {
Type: schema.TypeBool,
Required: true,
Description: "`enabled` determines whether the webhook is enabled. If it is not, " +
"Spacelift will not send any requests to the endpoint.",
},
"endpoint": {
Type: schema.TypeString,
Required: true,
Description: "`endpoint` is the URL to which Spacelift will send POST requests " +
"about audit events.",
},
"include_runs": {
Type: schema.TypeBool,
Optional: true,
Description: "`include_runs` determines whether the webhook should include " +
"information about the run that triggered the event.",
},
"secret": {
Type: schema.TypeString,
Required: true,
Sensitive: true,
Description: "`secret` is a secret that Spacelift will send with the request",
},
},
}
}

func resourceAuditTrailWebhookCreate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics {
var mutation struct {
AuditTrailWebhook *structs.AuditTrailWebhook `graphql:"auditTrailSetWebhook(input: $input)"`
}
variables := map[string]interface{}{
"input": structs.AuditTrailWebhookInput{
Enabled: toBool(data.Get("enabled")),
Endpoint: toString(data.Get("endpoint")),
IncludeRuns: toBool(data.Get("include_runs")),
Secret: toString(data.Get("secret")),
},
}
if err := i.(*internal.Client).Mutate(ctx, "AuditTrailWebhookCreate", &mutation, variables); err != nil {
return diag.Errorf("could not create audit trail webhook: %v", internal.FromSpaceliftError(err))
}

data.SetId(time.Now().String())

return resourceAuditTrailWebhookRead(ctx, data, i)
}

func resourceAuditTrailWebhookRead(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics {
var query struct {
AuditTrailWebhook *structs.AuditTrailWebhook `graphql:"auditTrailWebhook"`
}
if err := i.(*internal.Client).Query(ctx, "AuditTrailWebhookRead", &query, nil); err != nil {
return diag.Errorf("could not query for audit trail webhook: %v", internal.FromSpaceliftError(err))
}

if query.AuditTrailWebhook == nil {
data.SetId("")
return nil
}

data.Set("enabled", query.AuditTrailWebhook.Enabled)
data.Set("endpoint", query.AuditTrailWebhook.Endpoint)
data.Set("include_runs", query.AuditTrailWebhook.IncludeRuns)
data.Set("secret", query.AuditTrailWebhook.Secret)

return nil
}

func resourceAuditTrailWebhookUpdate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics {
var mutation struct {
AuditTrailWebhook *structs.AuditTrailWebhook `graphql:"auditTrailSetWebhook(input: $input)"`
}
variables := map[string]interface{}{
"input": structs.AuditTrailWebhookInput{
Enabled: toBool(data.Get("enabled")),
Endpoint: toString(data.Get("endpoint")),
IncludeRuns: toBool(data.Get("include_runs")),
Secret: toString(data.Get("secret")),
},
}
if err := i.(*internal.Client).Mutate(ctx, "AuditTrailWebhookUpdate", &mutation, variables); err != nil {
return diag.Errorf("could not update audit trail webhook: %v", internal.FromSpaceliftError(err))
}

return resourceAuditTrailWebhookRead(ctx, data, i)
}

func resourceAuditTrailWebhookDelete(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics {
var mutation struct {
AuditTrailWebhook *structs.AuditTrailWebhook `graphql:"auditTrailDeleteWebhook"`
}
if err := i.(*internal.Client).Mutate(ctx, "AuditTrailWebhookDelete", &mutation, nil); err != nil {
return diag.Errorf("could not delete audit trail webhook: %v", internal.FromSpaceliftError(err))
}

data.SetId("")

return nil
}
53 changes: 53 additions & 0 deletions spacelift/resource_audit_trail_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package spacelift

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"

. "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/testhelpers"
)

var auditTrailWebhookSimple = `
resource "spacelift_audit_trail_webhook" "test" {
enabled = true
endpoint = "%s"
include_runs = true
secret = "secret"
}
`

func Test_resourceAuditTrailWebhook(t *testing.T) {
const resourceName = "spacelift_audit_trail_webhook.test"

t.Run("creates an audit trail webhook without an error", func(t *testing.T) {
testSteps(t, []resource.TestStep{
{
Config: fmt.Sprintf(auditTrailWebhookSimple, "https://example.com"),
Check: Resource(
resourceName,
Attribute("enabled", Equals("true")),
Attribute("endpoint", Equals("https://example.com")),
Attribute("include_runs", Equals("true")),
Attribute("secret", Equals("secret")),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
})
})

t.Run("endpoint has to exist", func(t *testing.T) {
testSteps(t, []resource.TestStep{
{
Config: fmt.Sprintf(auditTrailWebhookSimple, "https://invalidendpoint.com/"),
ExpectError: regexp.MustCompile(`could not send webhook to given endpoint`),
},
})
})
}

0 comments on commit e2d18b0

Please sign in to comment.