Skip to content

Commit

Permalink
Merge pull request #30 from pavel-snyk/feat/add-pull-request-integrat…
Browse files Browse the repository at this point in the history
…ion-settings-to-integration

feat: add pull request integration settings
  • Loading branch information
pavel-snyk authored Sep 18, 2022
2 parents da3de66 + 2dd736d commit b651ce1
Show file tree
Hide file tree
Showing 11 changed files with 344 additions and 20 deletions.
31 changes: 30 additions & 1 deletion docs/resources/integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,32 @@ The integration resource allows you to manage Snyk integration.

## Example Usage

### Default

```terraform
resource "snyk_integration" "github" {
resource "snyk_integration" "gitlab" {
organization_id = snyk_organization.frontend.id
type = "gitlab"
token = "gitlab-secret-token"
}
```

### Using Pull Request Testing

```terraform
resource "snyk_integration" "github" {
organization_id = snyk_organization.backend.id
type = "github"
token = "rotated-github-secret-token"
pull_request_testing = {
enabled = true
fail_on_any_issue = false
fail_only_on_issues_with_fix = true
}
}
```

Expand All @@ -32,6 +52,7 @@ resource "snyk_integration" "github" {
### Optional

- `password` (String, Sensitive) The password used by the integration.
- `pull_request_testing` (Attributes) Pull request tests settings applied whenever a new PR is opened. (see [below for nested schema](#nestedatt--pull_request_testing))
- `region` (String) The region used by the integration.
- `registry_url` (String) The URL for container registries used by the integration (e.g. for ECR).
- `role_arn` (String) The role ARN used by the integration (ECR only).
Expand All @@ -43,4 +64,12 @@ resource "snyk_integration" "github" {

- `id` (String) The ID of the integration.

<a id="nestedatt--pull_request_testing"></a>
### Nested Schema for `pull_request_testing`

Optional:

- `enabled` (Boolean) Denotes the pull request testing feature should be enabled for this integration.
- `fail_on_any_issue` (Boolean) Fails an opened pull request if any vulnerable dependencies have been detected, otherwise the pull request should only fail when a dependency with issues is added.
- `fail_only_for_high_and_critical_severity` (Boolean) Fails an opened pull request if any dependencies are marked as being of high or critical severity.
- `fail_only_on_issues_with_fix` (Boolean) Fails an opened pull request only when issues found have a fix available.
6 changes: 0 additions & 6 deletions examples/resources/snyk_integration/resource.tf

This file was deleted.

6 changes: 6 additions & 0 deletions examples/resources/snyk_integration/resource_default.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
resource "snyk_integration" "gitlab" {
organization_id = snyk_organization.frontend.id

type = "gitlab"
token = "gitlab-secret-token"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
resource "snyk_integration" "github" {
organization_id = snyk_organization.backend.id

type = "github"
token = "rotated-github-secret-token"

pull_request_testing = {
enabled = true

fail_on_any_issue = false
fail_only_on_issues_with_fix = true
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/hashicorp/terraform-plugin-go v0.14.0
github.com/hashicorp/terraform-plugin-log v0.7.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.23.0
github.com/pavel-snyk/snyk-sdk-go v0.1.0
github.com/pavel-snyk/snyk-sdk-go v0.3.1
github.com/stretchr/testify v1.8.0
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pavel-snyk/snyk-sdk-go v0.1.0 h1:k615zmCpVCLMcEW+RyV5EqFaBmOEUM4Ny5QTcSKotGA=
github.com/pavel-snyk/snyk-sdk-go v0.1.0/go.mod h1:LRL1TRuuM925gnyGp54WtS9p8S4yJMd0oS4JpLg+n7Y=
github.com/pavel-snyk/snyk-sdk-go v0.3.1 h1:RechIZ/uzShWUDkEzgt5Bu3CCLeg7q/I09AWBL9uhHQ=
github.com/pavel-snyk/snyk-sdk-go v0.3.1/go.mod h1:LRL1TRuuM925gnyGp54WtS9p8S4yJMd0oS4JpLg+n7Y=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
135 changes: 125 additions & 10 deletions internal/provider/resource_integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,51 @@ func (r integrationResourceType) GetSchema(_ context.Context) (tfsdk.Schema, dia
},
Type: types.StringType,
},
"pull_request_testing": {
Description: "Pull request tests settings applied whenever a new PR is opened.",
Optional: true,
PlanModifiers: tfsdk.AttributePlanModifiers{
resource.UseStateForUnknown(),
},
Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
"enabled": {
Description: "Denotes the pull request testing feature should be enabled for this integration.",
Computed: true,
Optional: true,
PlanModifiers: tfsdk.AttributePlanModifiers{
resource.UseStateForUnknown(),
},
Type: types.BoolType,
},
"fail_on_any_issue": {
Description: "Fails an opened pull request if any vulnerable dependencies have been detected, otherwise the pull request should only fail when a dependency with issues is added.",
Computed: true,
Optional: true,
PlanModifiers: tfsdk.AttributePlanModifiers{
resource.UseStateForUnknown(),
},
Type: types.BoolType,
},
"fail_only_for_high_and_critical_severity": {
Description: "Fails an opened pull request if any dependencies are marked as being of high or critical severity.",
Computed: true,
Optional: true,
PlanModifiers: tfsdk.AttributePlanModifiers{
resource.UseStateForUnknown(),
},
Type: types.BoolType,
},
"fail_only_on_issues_with_fix": {
Description: "Fails an opened pull request only when issues found have a fix available.",
Computed: true,
Optional: true,
PlanModifiers: tfsdk.AttributePlanModifiers{
resource.UseStateForUnknown(),
},
Type: types.BoolType,
},
}),
},
"region": {
Description: "The region used by the integration.",
Optional: true,
Expand Down Expand Up @@ -117,16 +162,24 @@ type integrationResource struct {
}

type integrationData struct {
ID types.String `tfsdk:"id"`
OrganizationID types.String `tfsdk:"organization_id"`
Password types.String `tfsdk:"password"`
Region types.String `tfsdk:"region"`
RegistryURL types.String `tfsdk:"registry_url"`
RoleARN types.String `tfsdk:"role_arn"`
Token types.String `tfsdk:"token"`
Type types.String `tfsdk:"type"`
URL types.String `tfsdk:"url"`
Username types.String `tfsdk:"username"`
ID types.String `tfsdk:"id"`
OrganizationID types.String `tfsdk:"organization_id"`
Password types.String `tfsdk:"password"`
PullRequestTesting *pullRequestTesting `tfsdk:"pull_request_testing"`
Region types.String `tfsdk:"region"`
RegistryURL types.String `tfsdk:"registry_url"`
RoleARN types.String `tfsdk:"role_arn"`
Token types.String `tfsdk:"token"`
Type types.String `tfsdk:"type"`
URL types.String `tfsdk:"url"`
Username types.String `tfsdk:"username"`
}

type pullRequestTesting struct {
Enabled types.Bool `tfsdk:"enabled"`
FailOnAnyIssue types.Bool `tfsdk:"fail_on_any_issue"`
FailOnlyForHighAndCriticalSeverity types.Bool `tfsdk:"fail_only_for_high_and_critical_severity"`
FailOnlyOnIssuesWithFix types.Bool `tfsdk:"fail_only_on_issues_with_fix"`
}

func (r integrationResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) {
Expand Down Expand Up @@ -212,6 +265,29 @@ func (r integrationResource) Create(ctx context.Context, request resource.Create
result.ID = types.String{Value: integration.ID}
}

if plan.PullRequestTesting != nil {
updateRequest := &snyk.IntegrationSettingsUpdateRequest{
IntegrationSettings: &snyk.IntegrationSettings{
PullRequestTestEnabled: toBoolPtr(plan.PullRequestTesting.Enabled),
PullRequestFailOnAnyVulnerability: toBoolPtr(plan.PullRequestTesting.FailOnAnyIssue),
PullRequestFailOnlyForHighAndCriticalSeverity: toBoolPtr(plan.PullRequestTesting.FailOnlyForHighAndCriticalSeverity),
PullRequestFailOnlyForIssuesWithFix: toBoolPtr(plan.PullRequestTesting.FailOnlyOnIssuesWithFix),
},
}

settings, _, err := r.p.client.Integrations.UpdateSettings(ctx, orgID, result.ID.Value, updateRequest)
if err != nil {
response.Diagnostics.AddError("Error updating pull request settings", err.Error())
return
}
result.PullRequestTesting = &pullRequestTesting{
Enabled: fromBoolPtr(settings.PullRequestTestEnabled),
FailOnAnyIssue: fromBoolPtr(settings.PullRequestFailOnAnyVulnerability),
FailOnlyForHighAndCriticalSeverity: fromBoolPtr(settings.PullRequestFailOnlyForHighAndCriticalSeverity),
FailOnlyOnIssuesWithFix: fromBoolPtr(settings.PullRequestFailOnlyForIssuesWithFix),
}
}

diags = response.State.Set(ctx, result)
response.Diagnostics.Append(diags...)
if response.Diagnostics.HasError() {
Expand All @@ -235,6 +311,22 @@ func (r integrationResource) Read(ctx context.Context, request resource.ReadRequ
return
}

if state.PullRequestTesting != nil {
settings, _, err := r.p.client.Integrations.GetSettings(ctx, organizationID, integration.ID)
if err != nil {
response.Diagnostics.AddError("Error reading integration settings", err.Error())
return
}

pullRequestTesting := &pullRequestTesting{
Enabled: fromBoolPtr(settings.PullRequestTestEnabled),
FailOnAnyIssue: fromBoolPtr(settings.PullRequestFailOnAnyVulnerability),
FailOnlyForHighAndCriticalSeverity: fromBoolPtr(settings.PullRequestFailOnlyForHighAndCriticalSeverity),
FailOnlyOnIssuesWithFix: fromBoolPtr(settings.PullRequestFailOnlyForIssuesWithFix),
}
state.PullRequestTesting = pullRequestTesting
}

state.ID = types.String{Value: integration.ID}

diags = response.State.Set(ctx, &state)
Expand Down Expand Up @@ -275,6 +367,29 @@ func (r integrationResource) Update(ctx context.Context, request resource.Update
return
}

if plan.PullRequestTesting != nil {
updateRequest := &snyk.IntegrationSettingsUpdateRequest{
IntegrationSettings: &snyk.IntegrationSettings{
PullRequestTestEnabled: toBoolPtr(plan.PullRequestTesting.Enabled),
PullRequestFailOnAnyVulnerability: toBoolPtr(plan.PullRequestTesting.FailOnAnyIssue),
PullRequestFailOnlyForHighAndCriticalSeverity: toBoolPtr(plan.PullRequestTesting.FailOnlyForHighAndCriticalSeverity),
PullRequestFailOnlyForIssuesWithFix: toBoolPtr(plan.PullRequestTesting.FailOnlyOnIssuesWithFix),
},
}

settings, _, err := r.p.client.Integrations.UpdateSettings(ctx, organizationID, integrationID, updateRequest)
if err != nil {
response.Diagnostics.AddError("Error updating pull request settings", err.Error())
return
}
plan.PullRequestTesting = &pullRequestTesting{
Enabled: fromBoolPtr(settings.PullRequestTestEnabled),
FailOnAnyIssue: fromBoolPtr(settings.PullRequestFailOnAnyVulnerability),
FailOnlyForHighAndCriticalSeverity: fromBoolPtr(settings.PullRequestFailOnlyForHighAndCriticalSeverity),
FailOnlyOnIssuesWithFix: fromBoolPtr(settings.PullRequestFailOnlyForIssuesWithFix),
}
}

plan.ID = types.String{Value: integration.ID}

diags = response.State.Set(ctx, &plan)
Expand Down
49 changes: 49 additions & 0 deletions internal/provider/resource_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,32 @@ func TestAccResourceIntegration_basic(t *testing.T) {
testAccCheckResourceIntegrationExists("snyk_integration.test", organizationName, &integration),
resource.TestCheckResourceAttrSet("snyk_integration.test", "id"),
resource.TestCheckResourceAttr("snyk_integration.test", "type", "gitlab"),
resource.TestCheckNoResourceAttr("snyk_integration.test", "pull_request_testing.enabled"),
),
},
},
})
}

func TestAccResourceIntegration_pullRequestTesting(t *testing.T) {
t.Parallel()

var integration snyk.Integration
organizationName := fmt.Sprintf("tf-test-acc_%s", acctest.RandString(10))
groupID := os.Getenv("SNYK_GROUP_ID")
token := acctest.RandString(20)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testAccProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccResourceIntegrationConfigWithPullRequestTesting(organizationName, groupID, token),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckResourceIntegrationExists("snyk_integration.test", organizationName, &integration),
resource.TestCheckResourceAttrSet("snyk_integration.test", "pull_request_testing.enabled"),
resource.TestCheckResourceAttr("snyk_integration.test", "pull_request_testing.fail_on_any_issue", "true"),
resource.TestCheckResourceAttr("snyk_integration.test", "pull_request_testing.fail_only_on_issues_with_fix", "false"),
),
},
},
Expand Down Expand Up @@ -101,3 +127,26 @@ resource "snyk_integration" "test" {
}
`, organizationName, groupID, token)
}

func testAccResourceIntegrationConfigWithPullRequestTesting(organizationName, groupID, token string) string {
return fmt.Sprintf(`
resource "snyk_organization" "test" {
name = "%s"
group_id = "%s"
}
resource "snyk_integration" "test" {
organization_id = snyk_organization.test.id
type = "gitlab"
url = "https://testing.gitlab.local"
token = "%s"
pull_request_testing = {
enabled = true
fail_on_any_issue = true
fail_only_on_issues_with_fix = false
}
}
`, organizationName, groupID, token)
}
17 changes: 17 additions & 0 deletions internal/provider/types_conversion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package provider

import "github.com/hashicorp/terraform-plugin-framework/types"

func fromBoolPtr(v *bool) types.Bool {
if v == nil {
return types.Bool{Null: true}
}
return types.Bool{Value: *v}
}

func toBoolPtr(v types.Bool) *bool {
if v.Null || v.Unknown {
return nil
}
return &v.Value
}
Loading

0 comments on commit b651ce1

Please sign in to comment.