Skip to content

Commit

Permalink
Merge pull request #885 from alenapan/event-orchestration-dynamic-rou…
Browse files Browse the repository at this point in the history
…ting-and-esc-policy-support

Event Orchestration: add support for Dynamic Routing and Dynamic Escalation Policy Assignment
  • Loading branch information
imjaroiswebdev authored Jul 22, 2024
2 parents 4741e41 + 9539e47 commit b473252
Show file tree
Hide file tree
Showing 15 changed files with 487 additions and 29 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-20240503143637-3459408ac715
github.com/heimweh/go-pagerduty v0.0.0-20240722154207-95f2261a009a
)

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-20240503143637-3459408ac715 h1:DbdS2LIPkhsqgRcQzOAux0RpTJSH8VYOrN4rZZgznak=
github.com/heimweh/go-pagerduty v0.0.0-20240503143637-3459408ac715/go.mod h1:r59w5iyN01Qvi734yA5hZldbSeJJmsJzee/1kQ/MK7s=
github.com/heimweh/go-pagerduty v0.0.0-20240722154207-95f2261a009a h1:s7Z3DLwXMmfoeLEpgF8tfIxXGs8o734zcEVICTvAkAQ=
github.com/heimweh/go-pagerduty v0.0.0-20240722154207-95f2261a009a/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
4 changes: 4 additions & 0 deletions pagerduty/event_orchestration_path_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,3 +427,7 @@ func emptyOrchestrationPathStructBuilder(pathType string) *pagerduty.EventOrches

return commonEmptyOrchestrationPath()
}

func isNonEmptyList(arg interface{}) bool {
return !isNilFunc(arg) && len(arg.([]interface{})) > 0
}
34 changes: 34 additions & 0 deletions pagerduty/import_pagerduty_event_orchestration_path_router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/heimweh/go-pagerduty/pagerduty"
)

func TestAccPagerDutyEventOrchestrationPathRouter_import(t *testing.T) {
Expand Down Expand Up @@ -37,6 +38,39 @@ func TestAccPagerDutyEventOrchestrationPathRouter_import(t *testing.T) {
})
}

func TestAccPagerDutyEventOrchestrationPathRouterDynamicRouteTo_import(t *testing.T) {
team := fmt.Sprintf("tf-name-%s", acctest.RandString(5))
escalationPolicy := fmt.Sprintf("tf-%s", acctest.RandString(5))
service := fmt.Sprintf("tf-%s", acctest.RandString(5))
orchestration := fmt.Sprintf("tf-orchestration-%s", acctest.RandString(5))
dynamicRouteToByNameInput := &pagerduty.EventOrchestrationPathDynamicRouteTo{
LookupBy: "service_name",
Regex: ".*",
Source: "event.custom_details.pd_service_name",
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPagerDutyEventOrchestrationRouterDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckPagerDutyEventOrchestrationRouterDynamicRouteToConfig(team, escalationPolicy, service, orchestration, dynamicRouteToByNameInput),
},
{
ResourceName: "pagerduty_event_orchestration_router.router",
ImportStateIdFunc: testAccCheckPagerDutyEventOrchestrationPathRouterID,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"set.0.rule.0.id",
"set.0.rule.1.id",
},
},
},
})
}

func testAccCheckPagerDutyEventOrchestrationPathRouterID(s *terraform.State) (string, error) {
return s.RootModule().Resources["pagerduty_event_orchestration.orch"].Primary.ID, nil
}
22 changes: 14 additions & 8 deletions pagerduty/resource_pagerduty_event_orchestration_path_global.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ var eventOrchestrationPathGlobalCatchAllActionsSchema = map[string]*schema.Schem
Schema: eventOrchestrationIncidentCustomFieldsObjectSchema,
},
},
"escalation_policy": {
Type: schema.TypeString,
Optional: true,
},
}

var eventOrchestrationPathGlobalRuleActionsSchema = buildEventOrchestrationPathGlobalRuleActionsSchema()
Expand Down Expand Up @@ -374,6 +378,7 @@ func expandGlobalPathActions(v interface{}) *pagerduty.EventOrchestrationPathRul
actions.Suppress = a["suppress"].(bool)
actions.Suspend = intTypeToIntPtr(a["suspend"].(int))
actions.Priority = a["priority"].(string)
actions.EscalationPolicy = stringTypeToStringPtr(a["escalation_policy"].(string))
actions.Annotate = a["annotate"].(string)
actions.Severity = a["severity"].(string)
actions.EventAction = a["event_action"].(string)
Expand Down Expand Up @@ -439,14 +444,15 @@ func flattenGlobalPathActions(actions *pagerduty.EventOrchestrationPathRuleActio
var actionsMap []map[string]interface{}

flattenedAction := map[string]interface{}{
"drop_event": actions.DropEvent,
"route_to": actions.RouteTo,
"severity": actions.Severity,
"event_action": actions.EventAction,
"suppress": actions.Suppress,
"suspend": actions.Suspend,
"priority": actions.Priority,
"annotate": actions.Annotate,
"drop_event": actions.DropEvent,
"route_to": actions.RouteTo,
"severity": actions.Severity,
"event_action": actions.EventAction,
"suppress": actions.Suppress,
"suspend": actions.Suspend,
"priority": actions.Priority,
"annotate": actions.Annotate,
"escalation_policy": stringPtrToStringType(actions.EscalationPolicy),
}

if actions.Variables != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ func TestAccPagerDutyEventOrchestrationPathGlobal_Basic(t *testing.T) {
resource.TestCheckResourceAttrSet(res, "set.0.rule.1.id"),
resource.TestCheckResourceAttrSet(res, "set.1.rule.0.id"),
resource.TestCheckResourceAttrSet(res, "set.1.rule.1.id"),
resource.TestCheckResourceAttr(
res, "set.0.rule.0.actions.0.escalation_policy", "POLICY3",
),
}...,
)...,
),
Expand Down Expand Up @@ -481,6 +484,7 @@ func testAccCheckPagerDutyEventOrchestrationPathGlobalAllActionsConfig(t, ep, s,
actions {
route_to = "set-1"
priority = "P0IN2KQ"
escalation_policy = pagerduty_escalation_policy.foo.id
annotate = "Routed through an event orchestration"
severity = "critical"
event_action = "trigger"
Expand Down Expand Up @@ -544,6 +548,7 @@ func testAccCheckPagerDutyEventOrchestrationPathGlobalAllActionsConfig(t, ep, s,
actions {
drop_event = true
priority = "P0IN2KW"
escalation_policy = pagerduty_escalation_policy.foo.id
annotate = "Routed through an event orchestration - catch-all rule"
severity = "warning"
event_action = "trigger"
Expand Down Expand Up @@ -589,6 +594,7 @@ func testAccCheckPagerDutyEventOrchestrationPathGlobalAllActionsUpdateConfig(t,
actions {
route_to = "set-2"
priority = "P0IN2KR"
escalation_policy = "POLICY3"
annotate = "Routed through a service orchestration!"
severity = "warning"
event_action = "resolve"
Expand Down Expand Up @@ -629,6 +635,7 @@ func testAccCheckPagerDutyEventOrchestrationPathGlobalAllActionsUpdateConfig(t,
label = "set-2 rule 1"
actions {
suspend = 15
escalation_policy = pagerduty_escalation_policy.foo.id
}
}
rule {
Expand Down Expand Up @@ -666,6 +673,7 @@ func testAccCheckPagerDutyEventOrchestrationPathGlobalAllActionsUpdateConfig(t,
actions {
drop_event = false
priority = "P0IN2KX"
escalation_policy = "POLICY4"
annotate = "[UPD] Routed through an event orchestration - catch-all rule"
severity = "info"
event_action = "resolve"
Expand Down
108 changes: 105 additions & 3 deletions pagerduty/resource_pagerduty_event_orchestration_path_router.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"log"
"net/http"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
Expand All @@ -22,6 +23,7 @@ func resourcePagerDutyEventOrchestrationPathRouter() *schema.Resource {
Importer: &schema.ResourceImporter{
StateContext: resourcePagerDutyEventOrchestrationPathRouterImport,
},
CustomizeDiff: checkDynamicRoutingRule,
Schema: map[string]*schema.Schema{
"event_orchestration": {
Type: schema.TypeString,
Expand Down Expand Up @@ -63,9 +65,29 @@ func resourcePagerDutyEventOrchestrationPathRouter() *schema.Resource {
MaxItems: 1, // there can only be one action for router
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"dynamic_route_to": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"lookup_by": {
Type: schema.TypeString,
Required: true,
},
"regex": {
Type: schema.TypeString,
Required: true,
},
"source": {
Type: schema.TypeString,
Required: true,
},
},
},
},
"route_to": {
Type: schema.TypeString,
Required: true,
Optional: true,
ValidateFunc: func(v interface{}, key string) (warns []string, errs []error) {
value := v.(string)
if value == "unrouted" {
Expand Down Expand Up @@ -113,6 +135,54 @@ func resourcePagerDutyEventOrchestrationPathRouter() *schema.Resource {
}
}

func checkDynamicRoutingRule(context context.Context, diff *schema.ResourceDiff, i interface{}) error {
rNum := diff.Get("set.0.rule.#").(int)
draIdxs := []int{}
errorMsgs := []string{}

for ri := 0; ri < rNum; ri++ {
dra := diff.Get(fmt.Sprintf("set.0.rule.%d.actions.0.dynamic_route_to", ri))
hasDra := isNonEmptyList(dra)
if !hasDra {
continue
}
draIdxs = append(draIdxs, ri)
}
// 1. Only the first rule of the first ("start") set can have the Dynamic Routing action:
if len(draIdxs) > 1 {
idxs := []string{}
for _, idx := range draIdxs {
idxs = append(idxs, fmt.Sprintf("%d", idx))
}
errorMsgs = append(errorMsgs, fmt.Sprintf("A Router can have at most one Dynamic Routing rule; Rules with the dynamic_route_to action found at indexes: %s", strings.Join(idxs, ", ")))
}
// 2. The Dynamic Routing action can only be used in the first rule of the first set:
if len(draIdxs) > 0 && draIdxs[0] != 0 {
errorMsgs = append(errorMsgs, fmt.Sprintf("The Dynamic Routing rule must be the first rule in a Router"))
}
// 3. If the Dynamic Routing rule is the first rule of the first set,
// validate its configuration. It cannot have any conditions or the `route_to` action:
if len(draIdxs) == 1 && draIdxs[0] == 0 {
condNum := diff.Get("set.0.rule.0.condition.#").(int)
// diff.NewValueKnown(str) will return false if the value is based on interpolation that was unavailable at diff time,
// which may be the case for the `route_to` action when it references a pagerduty_service resource.
// Source: https://pkg.go.dev/github.com/hashicorp/terraform-plugin-sdk/helper/schema#ResourceDiff.NewValueKnown
routeToValueKnown := diff.NewValueKnown("set.0.rule.0.actions.0.route_to")
routeTo := diff.Get("set.0.rule.0.actions.0.route_to").(string)
if condNum > 0 {
errorMsgs = append(errorMsgs, fmt.Sprintf("Dynamic Routing rules cannot have conditions"))
}
if !routeToValueKnown || routeToValueKnown && routeTo != "" {
errorMsgs = append(errorMsgs, fmt.Sprintf("Dynamic Routing rules cannot have the `route_to` action"))
}
}

if len(errorMsgs) > 0 {
return fmt.Errorf("Invalid Dynamic Routing rule configuration:\n- %s", strings.Join(errorMsgs, "\n- "))
}
return nil
}

func resourcePagerDutyEventOrchestrationPathRouterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics

Expand Down Expand Up @@ -294,7 +364,12 @@ func expandRouterActions(v interface{}) *pagerduty.EventOrchestrationPathRuleAct
actions := new(pagerduty.EventOrchestrationPathRuleActions)
for _, ai := range v.([]interface{}) {
am := ai.(map[string]interface{})
actions.RouteTo = am["route_to"].(string)
dra := am["dynamic_route_to"]
if isNonEmptyList(dra) {
actions.DynamicRouteTo = expandRouterDynamicRouteToAction(dra)
} else {
actions.RouteTo = am["route_to"].(string)
}
}

return actions
Expand All @@ -311,6 +386,17 @@ func expandCatchAll(v interface{}) *pagerduty.EventOrchestrationPathCatchAll {
return catchAll
}

func expandRouterDynamicRouteToAction(v interface{}) *pagerduty.EventOrchestrationPathDynamicRouteTo {
dr := new(pagerduty.EventOrchestrationPathDynamicRouteTo)
for _, i := range v.([]interface{}) {
dra := i.(map[string]interface{})
dr.LookupBy = dra["lookup_by"].(string)
dr.Regex = dra["regex"].(string)
dr.Source = dra["source"].(string)
}
return dr
}

func flattenSets(orchPathSets []*pagerduty.EventOrchestrationPathSet) []interface{} {
var flattenedSets []interface{}
for _, set := range orchPathSets {
Expand Down Expand Up @@ -344,7 +430,11 @@ func flattenRouterActions(actions *pagerduty.EventOrchestrationPathRuleActions)
var actionsMap []map[string]interface{}

am := make(map[string]interface{})
am["route_to"] = actions.RouteTo
if actions.DynamicRouteTo != nil {
am["dynamic_route_to"] = flattenRouterDynamicRouteToAction(actions.DynamicRouteTo)
} else {
am["route_to"] = actions.RouteTo
}
actionsMap = append(actionsMap, am)
return actionsMap
}
Expand All @@ -360,6 +450,18 @@ func flattenCatchAll(catchAll *pagerduty.EventOrchestrationPathCatchAll) []map[s
return caMap
}

func flattenRouterDynamicRouteToAction(dra *pagerduty.EventOrchestrationPathDynamicRouteTo) []map[string]interface{} {
var dr []map[string]interface{}

dr = append(dr, map[string]interface{}{
"lookup_by": dra.LookupBy,
"regex": dra.Regex,
"source": dra.Source,
})

return dr
}

func resourcePagerDutyEventOrchestrationPathRouterImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
client, err := meta.(*Config).Client()
if err != nil {
Expand Down
Loading

0 comments on commit b473252

Please sign in to comment.