Skip to content

Commit

Permalink
Merge pull request PagerDuty#861 from imjaroiswebdev/issue-848-suppor…
Browse files Browse the repository at this point in the history
…t-iw-permissions

Add support for Incident Workflow triggers team restrictions
  • Loading branch information
imjaroiswebdev authored May 3, 2024
2 parents 109d59e + fb99d19 commit 5211b7e
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 21 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/hashicorp/terraform-plugin-mux v0.13.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0
github.com/hashicorp/terraform-plugin-testing v1.6.0
github.com/heimweh/go-pagerduty v0.0.0-20240403153232-5876af2ce24a
github.com/heimweh/go-pagerduty v0.0.0-20240503143637-3459408ac715
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S
github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/heimweh/go-pagerduty v0.0.0-20240403153232-5876af2ce24a h1:upvfy2kYdl/poYpITYmq6ZqJb5mu9zHm4V0YeXlyNOM=
github.com/heimweh/go-pagerduty v0.0.0-20240403153232-5876af2ce24a/go.mod h1:r59w5iyN01Qvi734yA5hZldbSeJJmsJzee/1kQ/MK7s=
github.com/heimweh/go-pagerduty v0.0.0-20240503143637-3459408ac715 h1:DbdS2LIPkhsqgRcQzOAux0RpTJSH8VYOrN4rZZgznak=
github.com/heimweh/go-pagerduty v0.0.0-20240503143637-3459408ac715/go.mod h1:r59w5iyN01Qvi734yA5hZldbSeJJmsJzee/1kQ/MK7s=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
Expand Down
70 changes: 70 additions & 0 deletions pagerduty/resource_pagerduty_incident_workflow_trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,25 @@ func resourcePagerDutyIncidentWorkflowTrigger() *schema.Resource {
Type: schema.TypeString,
Optional: true,
},
"permissions": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"restricted": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
},
"team_id": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
},
}
}
Expand Down Expand Up @@ -138,6 +157,16 @@ func validateIncidentWorkflowTrigger(_ context.Context, d *schema.ResourceDiff,
return fmt.Errorf("when trigger type conditional is used, condition must be specified")
}

// pagerduty_incident_workflow_trigger.permissions input validation
permissionRestricted := d.Get("permissions.0.restricted").(bool)
permissionTeamID := d.Get("permissions.0.team_id").(string)
if triggerType != "manual" && permissionRestricted {
return fmt.Errorf("restricted can only be true when trigger type is manual")
}
if !permissionRestricted && permissionTeamID != "" {
return fmt.Errorf("team_id not allowed when restricted is false")
}

s, hadServices := d.GetOk("services")
all := d.Get("subscribed_to_all_services").(bool)
if all && hadServices && len(s.([]interface{})) > 0 {
Expand Down Expand Up @@ -186,6 +215,14 @@ func flattenIncidentWorkflowTrigger(d *schema.ResourceData, t *pagerduty.Inciden
if t.Condition != nil {
d.Set("condition", t.Condition)
}
if t.Permissions != nil {
d.Set("permissions", []map[string]interface{}{
{
"restricted": t.Permissions.Restricted,
"team_id": t.Permissions.TeamID,
},
})
}

return nil
}
Expand Down Expand Up @@ -219,6 +256,14 @@ func buildIncidentWorkflowTriggerStruct(d *schema.ResourceData, forUpdate bool)
iwt.Condition = &condStr
}

if permissions, ok := d.GetOk("permissions"); ok {
p, err := expandIncidentWorkflowTriggerPermissions(permissions)
if err != nil {
return nil, err
}
iwt.Permissions = p
}

return &iwt, nil
}

Expand All @@ -232,3 +277,28 @@ func buildIncidentWorkflowTriggerServices(s interface{}) []*pagerduty.ServiceRef
}
return newServices
}

func expandIncidentWorkflowTriggerPermissions(v interface{}) (*pagerduty.IncidentWorkflowTriggerPermissions, error) {
var permissions *pagerduty.IncidentWorkflowTriggerPermissions

permissionsData, ok := v.([]interface{})
if ok && len(permissionsData) > 0 {
p := permissionsData[0].(map[string]interface{})

// Unfortunately this validatation can't be made during diff checking, since
// Diff Customization doesn't support computed/"known after apply" values
// like team_id in this case. Based on
// https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/customizing-differences
// because of this, it will only be returned during the apply phase.
if p["restricted"].(bool) && p["team_id"].(string) == "" {
return nil, fmt.Errorf("team_id must be specified when restricted is true")
}

permissions = &pagerduty.IncidentWorkflowTriggerPermissions{
Restricted: p["restricted"].(bool),
TeamID: p["team_id"].(string),
}
}

return permissions, nil
}
144 changes: 144 additions & 0 deletions pagerduty/resource_pagerduty_incident_workflow_trigger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,150 @@ func TestAccPagerDutyIncidentWorkflowTrigger_BasicConditionalAllServices(t *test
})
}

func TestAccPagerDutyIncidentWorkflowTrigger_ManualWithTeamPermissions(t *testing.T) {
username := fmt.Sprintf("tf-%s", acctest.RandString(5))
email := fmt.Sprintf("%[email protected]", username)
escalationPolicy := fmt.Sprintf("tf-%s", acctest.RandString(5))
service := fmt.Sprintf("tf-%s", acctest.RandString(5))
workflow := fmt.Sprintf("tf-%s", acctest.RandString(5))
teamName := fmt.Sprintf("tf-%s", acctest.RandString(5))
teamIDTFRef := "pagerduty_team.foo.id"
emptyCondition := ""
dummyCondition := "event.summary matches 'foo'"

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccPreCheckIncidentWorkflows(t)
},
ProviderFactories: testAccProviderFactories,
CheckDestroy: testAccCheckPagerDutyIncidentWorkflowTriggerDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckPagerDutyIncidentWorkflowTriggerConfigManualWithPermissions(username, email, escalationPolicy, service, teamName, workflow),
Check: resource.ComposeTestCheckFunc(
testAccCheckPagerDutyIncidentWorkflowTriggerExists("pagerduty_incident_workflow_trigger.test"),
resource.TestCheckResourceAttr(
"pagerduty_incident_workflow_trigger.test", "type", "manual"),
resource.TestCheckResourceAttr(
"pagerduty_incident_workflow_trigger.test", "permissions.0.restricted", "false"),
),
},
{
Config: testAccCheckPagerDutyIncidentWorkflowTriggerConfigManualWithPermissionsUpdated(username, email, escalationPolicy, service, teamName, workflow, "manual", emptyCondition, "true", teamIDTFRef),
Check: resource.ComposeTestCheckFunc(
testAccCheckPagerDutyIncidentWorkflowTriggerExists("pagerduty_incident_workflow_trigger.test"),
resource.TestCheckResourceAttr(
"pagerduty_incident_workflow_trigger.test", "type", "manual"),
resource.TestCheckResourceAttr(
"pagerduty_incident_workflow_trigger.test", "permissions.0.restricted", "true"),
testAccCheckPagerDutyIncidentWorkflowTriggerCheckPermissionsTeamId("pagerduty_incident_workflow_trigger.test", "pagerduty_team.foo"),
),
},
// Check input validation conditions for permissions configuration
{
Config: testAccCheckPagerDutyIncidentWorkflowTriggerConfigManualWithPermissionsUpdated(username, email, escalationPolicy, service, teamName, workflow, "conditional", dummyCondition, "true", teamIDTFRef),
Check: resource.ComposeTestCheckFunc(
testAccCheckPagerDutyIncidentWorkflowTriggerExists("pagerduty_incident_workflow_trigger.test"),
),
PlanOnly: true,
ExpectError: regexp.MustCompile("restricted can only be true when trigger type is manual"),
},
{
Config: testAccCheckPagerDutyIncidentWorkflowTriggerConfigManualWithPermissionsUpdated(username, email, escalationPolicy, service, teamName, workflow, "manual", emptyCondition, "false", teamIDTFRef),
Check: resource.ComposeTestCheckFunc(
testAccCheckPagerDutyIncidentWorkflowTriggerExists("pagerduty_incident_workflow_trigger.test"),
),
PlanOnly: true,
ExpectError: regexp.MustCompile("team_id not allowed when restricted is false"),
},
{
Config: testAccCheckPagerDutyIncidentWorkflowTriggerConfigManualWithPermissionsUpdated(username, email, escalationPolicy, service, teamName, workflow, "manual", emptyCondition, "true", `""`),
Check: resource.ComposeTestCheckFunc(
testAccCheckPagerDutyIncidentWorkflowTriggerExists("pagerduty_incident_workflow_trigger.test"),
),
ExpectError: regexp.MustCompile("team_id must be specified when restricted is true"),
},
},
})
}

func testAccCheckPagerDutyIncidentWorkflowTriggerConfigManualWithPermissions(username, email, escalationPolicy, service, workflow, team string) string {
return fmt.Sprintf(`
%s
%s
resource "pagerduty_team" "foo" {
name = %q
}
resource "pagerduty_incident_workflow_trigger" "test" {
type = "manual"
workflow = pagerduty_incident_workflow.test.id
services = [pagerduty_service.foo.id]
subscribed_to_all_services = false
}
`, testAccCheckPagerDutyServiceConfig(username, email, escalationPolicy, service), testAccCheckPagerDutyIncidentWorkflowConfig(workflow), team)
}

func testAccCheckPagerDutyIncidentWorkflowTriggerConfigManualWithPermissionsUpdated(username, email, escalationPolicy, service, workflow, team, triggerType, condition, isRestricted, teamId string) string {
return fmt.Sprintf(`
%s
%s
resource "pagerduty_team" "foo" {
name = "%s"
}
resource "pagerduty_incident_workflow_trigger" "test" {
type = "%s"
condition = "%s"
workflow = pagerduty_incident_workflow.test.id
services = [pagerduty_service.foo.id]
subscribed_to_all_services = false
permissions {
restricted = %s
team_id = %s
}
}
`, testAccCheckPagerDutyServiceConfig(username, email, escalationPolicy, service), testAccCheckPagerDutyIncidentWorkflowConfig(workflow), team, triggerType, condition, isRestricted, teamId)
}

func testAccCheckPagerDutyIncidentWorkflowTriggerCheckPermissionsTeamId(iwtName, teamName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rsIWT, ok := s.RootModule().Resources[iwtName]
if !ok {
return fmt.Errorf("not found: %s", iwtName)
}
if rsIWT.Primary.ID == "" {
return fmt.Errorf("no incident workflow trigger ID is set")
}

rsTeam, ok := s.RootModule().Resources[teamName]
if !ok {
return fmt.Errorf("not found: %s", teamName)
}
if rsTeam.Primary.ID == "" {
return fmt.Errorf("no team ID is set")
}

client, _ := testAccProvider.Meta().(*Config).Client()

found, _, err := client.IncidentWorkflowTriggers.Get(rsIWT.Primary.ID)
if err != nil {
return err
}

if found.Permissions.TeamID != rsTeam.Primary.ID {
return fmt.Errorf("incident workflow trigger team restriction wanted %q, but got %q", rsTeam.Primary.ID, found.Permissions.TeamID)
}

return nil
}
}

func TestAccPagerDutyIncidentWorkflowTrigger_ChangeTypeCausesReplace(t *testing.T) {
workflow := fmt.Sprintf("tf-%s", acctest.RandString(5))

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ github.com/hashicorp/terraform-svchost
# github.com/hashicorp/yamux v0.1.1
## explicit; go 1.15
github.com/hashicorp/yamux
# github.com/heimweh/go-pagerduty v0.0.0-20240403153232-5876af2ce24a
# github.com/heimweh/go-pagerduty v0.0.0-20240503143637-3459408ac715
## explicit; go 1.17
github.com/heimweh/go-pagerduty/pagerduty
github.com/heimweh/go-pagerduty/persistentconfig
Expand Down
3 changes: 3 additions & 0 deletions website/docs/r/incident_workflow_trigger.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ The following arguments are supported:
* `workflow` - (Required) The workflow ID for the workflow to trigger.
* `services` - (Optional) A list of service IDs. Incidents in any of the listed services are eligible to fire this trigger.
* `subscribed_to_all_services` - (Required) Set to `true` if the trigger should be eligible for firing on all services. Only allowed to be `true` if the services list is not defined or empty.
* `permissions` - (Optional) Indicates who can start this Trigger. Applicable only to `manual`-type triggers.
* `restricted` - (Optional) If `true`, indicates that the Trigger can only be started by authorized Users. If `false` (default), any user can start this Trigger. Applicable only to `manual`-type triggers.
* `team_id` - (Optional) The ID of the Team whose members can manually start this Trigger. Required and allowed only if `restricted` is `true`.
* `condition` - (Required for `conditional`-type triggers) A [PCL](https://developer.pagerduty.com/docs/ZG9jOjM1NTE0MDc0-pcl-overview) condition string which must be satisfied for the trigger to fire.

## Attributes Reference
Expand Down

0 comments on commit 5211b7e

Please sign in to comment.