From fbd94f923b31cf8422e853085c57c5fdddfa460e Mon Sep 17 00:00:00 2001 From: Carlos Gajardo Date: Thu, 2 May 2024 23:00:46 -0400 Subject: [PATCH 1/8] Add util/validate folder --- util/build.go | 23 ++++++ util/enumtypes/int64.go | 105 +++++++++++++++++++++++++ util/enumtypes/string.go | 100 +++++++++++++++++++++++ util/rangetypes/int64.go | 91 +++++++++++++++++++++ util/string_describer.go | 13 +++ util/tztypes/string.go | 90 +++++++++++++++++++++ util/util.go | 12 +-- util/validate/alternatives_for_path.go | 28 +++++++ util/validate/is_allowed_string.go | 43 ++++++++++ util/validate/require.go | 53 +++++++++++++ util/validate/require_a_if_b_equal.go | 57 ++++++++++++++ util/validate/require_list_size.go | 39 +++++++++ util/validate/timezone.go | 33 ++++++++ 13 files changed, 681 insertions(+), 6 deletions(-) create mode 100644 util/build.go create mode 100644 util/enumtypes/int64.go create mode 100644 util/enumtypes/string.go create mode 100644 util/rangetypes/int64.go create mode 100644 util/string_describer.go create mode 100644 util/tztypes/string.go create mode 100644 util/validate/alternatives_for_path.go create mode 100644 util/validate/is_allowed_string.go create mode 100644 util/validate/require.go create mode 100644 util/validate/require_a_if_b_equal.go create mode 100644 util/validate/require_list_size.go create mode 100644 util/validate/timezone.go diff --git a/util/build.go b/util/build.go new file mode 100644 index 000000000..80bec4b65 --- /dev/null +++ b/util/build.go @@ -0,0 +1,23 @@ +package util + +import ( + "fmt" + "strconv" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func StringToUintPointer(p path.Path, s types.String, diags *diag.Diagnostics) *uint { + if s.IsNull() || s.IsUnknown() || s.ValueString() == "" || s.ValueString() == "null" { + return nil + } + if val, err := strconv.Atoi(s.ValueString()); err == nil { + uintvalue := uint(val) + return &uintvalue + } else { + diags.AddError(fmt.Sprintf("Value for %q is not a valid number", p), err.Error()) + } + return nil +} diff --git a/util/enumtypes/int64.go b/util/enumtypes/int64.go new file mode 100644 index 000000000..0aab6bf94 --- /dev/null +++ b/util/enumtypes/int64.go @@ -0,0 +1,105 @@ +package enumtypes + +import ( + "context" + "fmt" + "math/big" + "slices" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +type Int64Value struct { + basetypes.Int64Value + EnumType Int64Type +} + +func NewInt64Null(t Int64Type) Int64Value { + return Int64Value{Int64Value: basetypes.NewInt64Null(), EnumType: t} +} + +func NewInt64Value(v int64, t Int64Type) Int64Value { + return Int64Value{Int64Value: basetypes.NewInt64Value(v), EnumType: t} +} + +func (s Int64Value) Type(_ context.Context) attr.Type { + return s.EnumType +} + +type Int64Type struct { + basetypes.Int64Type + OneOf []int64 +} + +func (t Int64Type) Int64() string { + return "enumtypes.Int64Type" +} + +func (t Int64Type) Equal(o attr.Type) bool { + if t2, ok := o.(Int64Type); ok { + return slices.Equal(t.OneOf, t2.OneOf) + } + return t.Int64Type.Equal(o) +} + +func (t Int64Type) Validate(ctx context.Context, in tftypes.Value, path path.Path) (diags diag.Diagnostics) { + if in.Type() == nil { + return + } + + if !in.Type().Is(tftypes.Number) { + err := fmt.Errorf("expected Int64 value, received %T with value: %v", in, in) + diags.AddAttributeError( + path, + "Type Validation Error", + "An unexpected error was encountered trying to validate an attribute value. This is always an error in the provider. "+ + "Please report the following to the provider developer:\n\n"+err.Error(), + ) + return diags + } + + if !in.IsKnown() || in.IsNull() { + return diags + } + + var valueFloat big.Float + if err := in.As(&valueFloat); err != nil { + diags.AddAttributeError( + path, + "Type Validation Error", + "An unexpected error was encountered trying to validate an attribute value. This is always an error in the provider. "+ + "Please report the following to the provider developer:\n\n"+err.Error(), + ) + return + } + valueInt64, _ := valueFloat.Int64() + + found := false + for _, v := range t.OneOf { + if v == valueInt64 { + found = true + break + } + } + + if !found { + diags.AddAttributeError( + path, + "Invalid Int64 Value", + fmt.Sprintf( + "A string value was provided that is not valid.\n"+ + "Given Value: %v\n"+ + "Expecting One Of: %v", + valueInt64, + t.OneOf, + ), + ) + return + } + + return +} diff --git a/util/enumtypes/string.go b/util/enumtypes/string.go new file mode 100644 index 000000000..1d3a522c0 --- /dev/null +++ b/util/enumtypes/string.go @@ -0,0 +1,100 @@ +package enumtypes + +import ( + "context" + "fmt" + "slices" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +type StringValue struct { + basetypes.StringValue + EnumType StringType +} + +func NewStringNull(t StringType) StringValue { + return StringValue{StringValue: basetypes.NewStringNull(), EnumType: t} +} + +func NewStringValue(v string, t StringType) StringValue { + return StringValue{StringValue: basetypes.NewStringValue(v), EnumType: t} +} + +func (s StringValue) Type(_ context.Context) attr.Type { + return s.EnumType +} + +type StringType struct { + basetypes.StringType + OneOf []string +} + +func (t StringType) String() string { + return "enumtypes.StringType" +} + +func (t StringType) Equal(o attr.Type) bool { + if t2, ok := o.(StringType); ok { + return slices.Equal(t.OneOf, t2.OneOf) + } + return t.StringType.Equal(o) +} + +func (t StringType) Validate(ctx context.Context, in tftypes.Value, path path.Path) (diags diag.Diagnostics) { + if in.Type() == nil { + return + } + + if !in.Type().Is(tftypes.String) { + err := fmt.Errorf("expected String value, received %T with value: %v", in, in) + diags.AddAttributeError( + path, + "Type Validation Error", + "An unexpected error was encountered trying to validate an attribute value. This is always an error in the provider. "+ + "Please report the following to the provider developer:\n\n"+err.Error(), + ) + return diags + } + + if !in.IsKnown() || in.IsNull() { + return diags + } + + var valueString string + if err := in.As(&valueString); err != nil { + diags.AddAttributeError( + path, + "Type Validation Error", + "An unexpected error was encountered trying to validate an attribute value. This is always an error in the provider. "+ + "Please report the following to the provider developer:\n\n"+err.Error(), + ) + return + } + + found := false + for _, v := range t.OneOf { + if v == valueString { + found = true + break + } + } + + if !found { + diags.AddAttributeError( + path, + "Invalid String Value", + "A string value was provided that is not valid.\n"+ + "Given Value: "+valueString+"\n"+ + "Expecting One Of: "+strings.Join(t.OneOf, ", "), + ) + return + } + + return +} diff --git a/util/rangetypes/int64.go b/util/rangetypes/int64.go new file mode 100644 index 000000000..c23467fb1 --- /dev/null +++ b/util/rangetypes/int64.go @@ -0,0 +1,91 @@ +package rangetypes + +import ( + "context" + "fmt" + "math/big" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +type Int64Value struct { + basetypes.Int64Value + RangeType Int64Type +} + +func NewInt64Null(t Int64Type) Int64Value { + return Int64Value{Int64Value: basetypes.NewInt64Null(), RangeType: t} +} + +func NewInt64Value(v int64, t Int64Type) Int64Value { + return Int64Value{Int64Value: basetypes.NewInt64Value(v), RangeType: t} +} + +func (s Int64Value) Type(_ context.Context) attr.Type { + return s.RangeType +} + +type Int64Type struct { + basetypes.Int64Type + Start int64 + End int64 +} + +func (t Int64Type) String() string { + return "rangetypes.Int64Type" +} + +func (t Int64Type) Equal(o attr.Type) bool { + if t2, ok := o.(Int64Type); ok { + return t.Start == t2.Start && t.End == t2.End + } + return t.Int64Type.Equal(o) +} + +func (t Int64Type) addTypeValidationError(err error, path path.Path, diags *diag.Diagnostics) { + diags.AddAttributeError( + path, + "Type Validation Error", + "An unexpected error was encountered trying to validate an attribute value. This is always an error in the provider. "+ + "Please report the following to the provider developer:\n\n"+err.Error(), + ) +} + +func (t Int64Type) Validate(ctx context.Context, in tftypes.Value, path path.Path) (diags diag.Diagnostics) { + if in.Type() == nil { + return + } + + if !in.Type().Is(tftypes.Number) { + err := fmt.Errorf("expected Int64 value, received %T with value: %v", in, in) + t.addTypeValidationError(err, path, &diags) + return + } + + if !in.IsKnown() || in.IsNull() { + return + } + + var valueFloat big.Float + if err := in.As(&valueFloat); err != nil { + t.addTypeValidationError(err, path, &diags) + return + } + valueInt64, _ := valueFloat.Int64() + + if valueInt64 < t.Start || valueInt64 > int64(t.End) { + diags.AddAttributeError( + path, + "Invalid Int64 Value", + fmt.Sprintf("A value was provided that is not inside valid range (%v, %v).\n"+ + "Given Value: %v", t.Start, t.End, valueInt64), + ) + return + } + + return +} diff --git a/util/string_describer.go b/util/string_describer.go new file mode 100644 index 000000000..9528a0d6a --- /dev/null +++ b/util/string_describer.go @@ -0,0 +1,13 @@ +package util + +import "context" + +type StringDescriber struct{ Value string } + +func (d StringDescriber) MarkdownDescription(context.Context) string { + return d.Value +} + +func (d StringDescriber) Description(ctx context.Context) string { + return d.MarkdownDescription(ctx) +} diff --git a/util/tztypes/string.go b/util/tztypes/string.go new file mode 100644 index 000000000..315d9edaf --- /dev/null +++ b/util/tztypes/string.go @@ -0,0 +1,90 @@ +package tztypes + +import ( + "context" + "fmt" + + "github.com/PagerDuty/terraform-provider-pagerduty/util" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +type StringValue struct { + basetypes.StringValue +} + +func NewStringNull() StringValue { + return StringValue{StringValue: basetypes.NewStringNull()} +} + +func NewStringValue(v string) StringValue { + return StringValue{StringValue: basetypes.NewStringValue(v)} +} + +func (s StringValue) Type(_ context.Context) attr.Type { + return StringType{} +} + +type StringType struct { + basetypes.StringType +} + +func (t StringType) String() string { + return "tztypes.StringType" +} + +func (t StringType) Equal(o attr.Type) bool { + _, ok := o.(StringType) + if ok { + return true + } + + return t.StringType.Equal(o) +} + +func (t StringType) Validate(ctx context.Context, in tftypes.Value, path path.Path) (diags diag.Diagnostics) { + if in.Type() == nil { + return + } + + if !in.Type().Is(tftypes.String) { + err := fmt.Errorf("expected String value, received %T with value: %v", in, in) + diags.AddAttributeError( + path, + "Type Validation Error", + "An unexpected error was encountered trying to validate an attribute value. This is always an error in the provider. "+ + "Please report the following to the provider developer:\n\n"+err.Error(), + ) + return diags + } + + if !in.IsKnown() || in.IsNull() { + return diags + } + + var valueString string + if err := in.As(&valueString); err != nil { + diags.AddAttributeError( + path, + "Type Validation Error", + "An unexpected error was encountered trying to validate an attribute value. This is always an error in the provider. "+ + "Please report the following to the provider developer:\n\n"+err.Error(), + ) + return + } + + if !util.IsValidTZ(valueString) { + diags.AddAttributeError( + path, + "Invalid String Value", + "A string value was provided that is not a valid timezone.\n"+ + "Given Value: "+valueString, + ) + return + } + + return +} diff --git a/util/util.go b/util/util.go index 72e91798d..9fa34c6fa 100644 --- a/util/util.go +++ b/util/util.go @@ -292,16 +292,16 @@ func ResourcePagerDutyParseColonCompoundID(id string) (string, string, error) { return parts[0], parts[1], nil } +func IsValidTZ(v string) bool { + foundAt := sort.SearchStrings(validTZ, v) + return foundAt < len(validTZ) && validTZ[foundAt] == v +} + func ValidateTZValueDiagFunc(v interface{}, p cty.Path) diag.Diagnostics { var diags diag.Diagnostics value := v.(string) - valid := false - - foundAt := sort.SearchStrings(validTZ, value) - if foundAt < len(validTZ) && validTZ[foundAt] == value { - valid = true - } + valid := IsValidTZ(value) if !valid { diags = append(diags, diag.Diagnostic{ diff --git a/util/validate/alternatives_for_path.go b/util/validate/alternatives_for_path.go new file mode 100644 index 000000000..599a7990a --- /dev/null +++ b/util/validate/alternatives_for_path.go @@ -0,0 +1,28 @@ +package validate + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +func AlternativesForPath(p path.Path, alt []attr.Value) *alternativesForPathValidator { + return &alternativesForPathValidator{Path: p, Alternatives: alt} +} + +type alternativesForPathValidator struct { + Path path.Path + Alternatives []attr.Value +} + +var _ validator.String = (*alternativesForPathValidator)(nil) + +func (v *alternativesForPathValidator) Description(_ context.Context) string { return "" } +func (v *alternativesForPathValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v *alternativesForPathValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { +} diff --git a/util/validate/is_allowed_string.go b/util/validate/is_allowed_string.go new file mode 100644 index 000000000..4424544ec --- /dev/null +++ b/util/validate/is_allowed_string.go @@ -0,0 +1,43 @@ +package validate + +import ( + "context" + "strings" + "unicode" + + "github.com/PagerDuty/terraform-provider-pagerduty/util" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +type validateIsAllowedString struct { + validateFn func(s string) bool + util.StringDescriber +} + +func (v validateIsAllowedString) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if ok := v.validateFn(req.ConfigValue.ValueString()); !ok { + resp.Diagnostics.AddError(v.Value, "") + } +} + +func IsAllowedString(mode util.StringContentValidationMode) validator.String { + switch mode { + case util.NoNonPrintableChars: + return validateIsAllowedString{ + func(s string) bool { + for _, char := range s { + if !unicode.IsPrint(char) { + return false + } + } + return s != "" && !strings.HasSuffix(s, " ") + }, + util.StringDescriber{Value: "Name can not be blank, nor contain non-printable characters. Trailing white spaces are not allowed either."}, + } + default: + return validateIsAllowedString{ + func(s string) bool { return false }, + util.StringDescriber{Value: "Invalid mode while using func IsAllowedStringValidator(mode StringContentValidationMode)"}, + } + } +} diff --git a/util/validate/require.go b/util/validate/require.go new file mode 100644 index 000000000..903bef2b1 --- /dev/null +++ b/util/validate/require.go @@ -0,0 +1,53 @@ +package validate + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +// Require checks a path is not null. +func Require(p path.Path) resource.ConfigValidator { + return &requirePath{Path: p} +} + +type requirePath struct { + path.Path +} + +func (v *requirePath) Description(ctx context.Context) string { + return "Forces item to be present if its parent is present" +} + +func (v *requirePath) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v *requirePath) ValidateResource(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) { + var parent attr.Value + resp.Diagnostics.Append(req.Config.GetAttribute(ctx, v.Path.ParentPath(), &parent)...) + if resp.Diagnostics.HasError() { + return + } + if parent.IsNull() { + return + } + + var src attr.Value + resp.Diagnostics.Append(req.Config.GetAttribute(ctx, v.Path, &src)...) + if resp.Diagnostics.HasError() { + return + } + + if src.IsNull() { + resp.Diagnostics.AddAttributeError( + v.Path, + fmt.Sprintf("Required %s", v.Path), + fmt.Sprintf("Field %s must have an explicit value", v.Path), + ) + return + } +} diff --git a/util/validate/require_a_if_b_equal.go b/util/validate/require_a_if_b_equal.go new file mode 100644 index 000000000..df03c7d55 --- /dev/null +++ b/util/validate/require_a_if_b_equal.go @@ -0,0 +1,57 @@ +package validate + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +// RequireAIfBEqual checks path `a` is not null when path `b` is equal to `expected`. +func RequireAIfBEqual(a, b path.Path, expected attr.Value) resource.ConfigValidator { + return &requireIfEqual{ + dst: a, + src: b, + expected: expected, + } +} + +type requireIfEqual struct { + dst path.Path + src path.Path + expected attr.Value +} + +func (v *requireIfEqual) Description(ctx context.Context) string { return "" } +func (v *requireIfEqual) MarkdownDescription(ctx context.Context) string { return "" } + +func (v *requireIfEqual) ValidateResource(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) { + var src attr.Value + resp.Diagnostics.Append(req.Config.GetAttribute(ctx, v.src, &src)...) + if resp.Diagnostics.HasError() { + return + } + + if src.IsNull() || src.IsUnknown() { + return + } + + if src.Equal(v.expected) { + var dst attr.Value + resp.Diagnostics.Append(req.Config.GetAttribute(ctx, v.dst, &dst)...) + if resp.Diagnostics.HasError() { + return + } + + if dst.IsNull() || dst.IsUnknown() { + resp.Diagnostics.AddAttributeError( + v.dst, + fmt.Sprintf("Required %s", v.dst), + fmt.Sprintf("When the value of %s equals %s, field %s must have an explicit value", v.src, v.expected, v.dst), + ) + return + } + } +} diff --git a/util/validate/require_list_size.go b/util/validate/require_list_size.go new file mode 100644 index 000000000..c500c8386 --- /dev/null +++ b/util/validate/require_list_size.go @@ -0,0 +1,39 @@ +package validate + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +// RequireList checks path `p` is a list at least with size 1. +func RequireList(p path.Path) resource.ConfigValidator { + return &requireListSize{Path: p} +} + +type requireListSize struct { + path.Path +} + +func (v *requireListSize) Description(ctx context.Context) string { return "" } +func (v *requireListSize) MarkdownDescription(ctx context.Context) string { return "" } + +func (v *requireListSize) ValidateResource(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) { + var src attr.Value + resp.Diagnostics.Append(req.Config.GetAttribute(ctx, v.Path, &src)...) + if resp.Diagnostics.HasError() { + return + } + + if src.IsNull() || src.IsUnknown() { + return + } + + size := 1 + if size < 1 { + resp.Diagnostics.AddAttributeError(v.Path, "Required to be a list with items", "") + return + } +} diff --git a/util/validate/timezone.go b/util/validate/timezone.go new file mode 100644 index 000000000..53d5ab358 --- /dev/null +++ b/util/validate/timezone.go @@ -0,0 +1,33 @@ +package validate + +import ( + "context" + "fmt" + "time" + + "github.com/PagerDuty/terraform-provider-pagerduty/util" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +type timezoneValidator struct { + util.StringDescriber +} + +func Timezone() validator.String { + return &timezoneValidator{ + util.StringDescriber{Value: "checks time zone is supported by the machine's tzdata"}, + } +} + +func (v timezoneValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if req.ConfigValue.IsNull() { + return + } + value := req.ConfigValue.ValueString() + _, err := time.LoadLocation(value) + if err != nil { + resp.Diagnostics.AddAttributeError( + req.Path, fmt.Sprintf("Timezone %q is invalid", value), err.Error(), + ) + } +} From 61357255f939e8ee7c322e8d839694d17db91dc1 Mon Sep 17 00:00:00 2001 From: Carlos Gajardo Date: Tue, 2 Jul 2024 10:28:16 -0400 Subject: [PATCH 2/8] Migrate resource automation actions action Migrate data source automation actions action --- ...rce_pagerduty_automation_actions_action.go | 155 ------ pagerduty/provider.go | 2 - ...rce_pagerduty_automation_actions_action.go | 1 + ...rce_pagerduty_automation_actions_action.go | 119 +++++ ...agerduty_automation_actions_action_test.go | 6 +- ...agerduty_automation_actions_action_test.go | 6 +- pagerdutyplugin/provider.go | 2 + ...rce_pagerduty_automation_actions_action.go | 448 ++++++++++++++++++ ...agerduty_automation_actions_action_test.go | 76 +-- 9 files changed, 615 insertions(+), 200 deletions(-) delete mode 100644 pagerduty/data_source_pagerduty_automation_actions_action.go create mode 100644 pagerdutyplugin/data_source_pagerduty_automation_actions_action.go rename {pagerduty => pagerdutyplugin}/data_source_pagerduty_automation_actions_action_test.go (95%) rename {pagerduty => pagerdutyplugin}/import_pagerduty_automation_actions_action_test.go (75%) create mode 100644 pagerdutyplugin/resource_pagerduty_automation_actions_action.go rename {pagerduty => pagerdutyplugin}/resource_pagerduty_automation_actions_action_test.go (87%) diff --git a/pagerduty/data_source_pagerduty_automation_actions_action.go b/pagerduty/data_source_pagerduty_automation_actions_action.go deleted file mode 100644 index ed16962cc..000000000 --- a/pagerduty/data_source_pagerduty_automation_actions_action.go +++ /dev/null @@ -1,155 +0,0 @@ -package pagerduty - -import ( - "log" - "net/http" - "time" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSourcePagerDutyAutomationActionsAction() *schema.Resource { - return &schema.Resource{ - Read: dataSourcePagerDutyAutomationActionsActionRead, - - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Required: true, - }, - "name": { - Type: schema.TypeString, - Computed: true, - }, - "description": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "action_type": { - Type: schema.TypeString, - Computed: true, - }, - "runner_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "action_data_reference": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "process_automation_job_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "process_automation_job_arguments": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "process_automation_node_filter": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "script": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "invocation_command": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - }, - }, - }, - "type": { - Type: schema.TypeString, - Computed: true, - Optional: true, - }, - "action_classification": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "runner_type": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "creation_time": { - Type: schema.TypeString, - Computed: true, - Optional: true, - }, - "modify_time": { - Type: schema.TypeString, - Computed: true, - Optional: true, - }, - }, - } -} - -func dataSourcePagerDutyAutomationActionsActionRead(d *schema.ResourceData, meta interface{}) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - log.Printf("[INFO] Reading PagerDuty AutomationActionsAction") - - return retry.Retry(2*time.Minute, func() *retry.RetryError { - automationActionsAction, _, err := client.AutomationActionsAction.Get(d.Get("id").(string)) - if err != nil { - if isErrCode(err, http.StatusBadRequest) { - return retry.NonRetryableError(err) - } - - // Delaying retry by 30s as recommended by PagerDuty - // https://developer.pagerduty.com/docs/rest-api-v2/rate-limiting/#what-are-possible-workarounds-to-the-events-api-rate-limit - time.Sleep(30 * time.Second) - return retry.RetryableError(err) - } - - d.SetId(automationActionsAction.ID) - d.Set("name", automationActionsAction.Name) - d.Set("type", automationActionsAction.Type) - d.Set("action_type", automationActionsAction.ActionType) - d.Set("creation_time", automationActionsAction.CreationTime) - - if automationActionsAction.Description != nil { - d.Set("description", &automationActionsAction.Description) - } - - f_adr := flattenActionDataReference(automationActionsAction.ActionDataReference) - if err := d.Set("action_data_reference", f_adr); err != nil { - return retry.NonRetryableError(err) - } - - if automationActionsAction.ModifyTime != nil { - d.Set("modify_time", &automationActionsAction.ModifyTime) - } - - if automationActionsAction.RunnerID != nil { - d.Set("runner_id", &automationActionsAction.RunnerID) - } - - if automationActionsAction.RunnerType != nil { - d.Set("runner_type", &automationActionsAction.RunnerType) - } - - if automationActionsAction.ActionClassification != nil { - d.Set("action_classification", &automationActionsAction.ActionClassification) - } - - return nil - }) -} diff --git a/pagerduty/provider.go b/pagerduty/provider.go index 7de7b701f..bda9c4ad7 100644 --- a/pagerduty/provider.go +++ b/pagerduty/provider.go @@ -105,7 +105,6 @@ func Provider(isMux bool) *schema.Provider { "pagerduty_event_orchestration_global_cache_variable": dataSourcePagerDutyEventOrchestrationGlobalCacheVariable(), "pagerduty_event_orchestration_service_cache_variable": dataSourcePagerDutyEventOrchestrationServiceCacheVariable(), "pagerduty_automation_actions_runner": dataSourcePagerDutyAutomationActionsRunner(), - "pagerduty_automation_actions_action": dataSourcePagerDutyAutomationActionsAction(), "pagerduty_incident_workflow": dataSourcePagerDutyIncidentWorkflow(), "pagerduty_incident_custom_field": dataSourcePagerDutyIncidentCustomField(), "pagerduty_team_members": dataSourcePagerDutyTeamMembers(), @@ -141,7 +140,6 @@ func Provider(isMux bool) *schema.Provider { "pagerduty_event_orchestration_global_cache_variable": resourcePagerDutyEventOrchestrationGlobalCacheVariable(), "pagerduty_event_orchestration_service_cache_variable": resourcePagerDutyEventOrchestrationServiceCacheVariable(), "pagerduty_automation_actions_runner": resourcePagerDutyAutomationActionsRunner(), - "pagerduty_automation_actions_action": resourcePagerDutyAutomationActionsAction(), "pagerduty_automation_actions_action_team_association": resourcePagerDutyAutomationActionsActionTeamAssociation(), "pagerduty_automation_actions_runner_team_association": resourcePagerDutyAutomationActionsRunnerTeamAssociation(), "pagerduty_incident_workflow": resourcePagerDutyIncidentWorkflow(), diff --git a/pagerduty/resource_pagerduty_automation_actions_action.go b/pagerduty/resource_pagerduty_automation_actions_action.go index 907afdc7a..76959418b 100644 --- a/pagerduty/resource_pagerduty_automation_actions_action.go +++ b/pagerduty/resource_pagerduty_automation_actions_action.go @@ -11,6 +11,7 @@ import ( "github.com/heimweh/go-pagerduty/pagerduty" ) +// Deprecated: Migrated to pagerdutyplugin.resourceAutomationActionsAction. Kept for testing. func resourcePagerDutyAutomationActionsAction() *schema.Resource { return &schema.Resource{ Create: resourcePagerDutyAutomationActionsActionCreate, diff --git a/pagerdutyplugin/data_source_pagerduty_automation_actions_action.go b/pagerdutyplugin/data_source_pagerduty_automation_actions_action.go new file mode 100644 index 000000000..c8d0cd496 --- /dev/null +++ b/pagerdutyplugin/data_source_pagerduty_automation_actions_action.go @@ -0,0 +1,119 @@ +package pagerduty + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/PagerDuty/go-pagerduty" + "github.com/PagerDuty/terraform-provider-pagerduty/util" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" +) + +type dataSourceAutomationActionsAction struct{ client *pagerduty.Client } + +var _ datasource.DataSourceWithConfigure = (*dataSourceAutomationActionsAction)(nil) + +func (*dataSourceAutomationActionsAction) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = "pagerduty_automation_actions_action" +} + +func (*dataSourceAutomationActionsAction) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{Required: true}, + "name": schema.StringAttribute{Computed: true}, + "description": schema.StringAttribute{Computed: true}, + "action_type": schema.StringAttribute{Computed: true}, + "action_classification": schema.StringAttribute{Computed: true}, + "type": schema.StringAttribute{Computed: true}, + "runner_id": schema.StringAttribute{Computed: true}, + "runner_type": schema.StringAttribute{Computed: true}, + "creation_time": schema.StringAttribute{Computed: true}, + "modify_time": schema.StringAttribute{Computed: true}, + "action_data_reference": schema.ListAttribute{ + Computed: true, + ElementType: actionDataReferenceObjectType, + }, + }, + } +} + +func (d *dataSourceAutomationActionsAction) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + resp.Diagnostics.Append(ConfigurePagerdutyClient(&d.client, req.ProviderData)...) +} + +func (d *dataSourceAutomationActionsAction) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + log.Println("[INFO] Reading PagerDuty automation actions action") + + var id types.String + resp.Diagnostics.Append(req.Config.GetAttribute(ctx, path.Root("id"), &id)...) + if resp.Diagnostics.HasError() { + return + } + + var found *pagerduty.AutomationAction + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + action, err := d.client.GetAutomationActionWithContext(ctx, id.ValueString()) + if err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + found = action + return nil + }) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty automation actions action %s", id), + err.Error(), + ) + return + } + + if found == nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Unable to locate any automation actions action with id: %s", id), + "", + ) + return + } + + model := dataSourceAutomationActionsActionModel{ + ID: types.StringValue(found.ID), + Name: types.StringValue(found.Name), + Description: types.StringValue(found.Description), + Type: types.StringValue(found.Type), + ActionType: types.StringValue(found.ActionType), + ActionClassification: types.StringNull(), + RunnerID: types.StringValue(found.Runner), + RunnerType: types.StringValue(found.RunnerType), + ModifyTime: types.StringValue(found.ModifyTime), + CreationTime: types.StringValue(found.CreationTime), + ActionDataReference: flattenActionDataReference(found.ActionDataReference, &resp.Diagnostics), + } + if found.ActionClassification != nil { + model.ActionClassification = types.StringValue(*found.ActionClassification) + } + resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) +} + +type dataSourceAutomationActionsActionModel struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + Type types.String `tfsdk:"type"` + ActionType types.String `tfsdk:"action_type"` + ActionClassification types.String `tfsdk:"action_classification"` + RunnerID types.String `tfsdk:"runner_id"` + RunnerType types.String `tfsdk:"runner_type"` + CreationTime types.String `tfsdk:"creation_time"` + ModifyTime types.String `tfsdk:"modify_time"` + ActionDataReference types.List `tfsdk:"action_data_reference"` +} diff --git a/pagerduty/data_source_pagerduty_automation_actions_action_test.go b/pagerdutyplugin/data_source_pagerduty_automation_actions_action_test.go similarity index 95% rename from pagerduty/data_source_pagerduty_automation_actions_action_test.go rename to pagerdutyplugin/data_source_pagerduty_automation_actions_action_test.go index fa10025b6..d7a7b853c 100644 --- a/pagerduty/data_source_pagerduty_automation_actions_action_test.go +++ b/pagerdutyplugin/data_source_pagerduty_automation_actions_action_test.go @@ -13,8 +13,8 @@ func TestAccDataSourcePagerDutyAutomationActionsAction_Basic(t *testing.T) { name := fmt.Sprintf("tf-%s", acctest.RandString(5)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), Steps: []resource.TestStep{ { Config: testAccDataSourcePagerDutyAutomationActionsActionConfig(name), @@ -74,7 +74,7 @@ resource "pagerduty_automation_actions_action" "test" { process_automation_job_id = "pa_job_id_123" process_automation_job_arguments = "-arg 1" process_automation_node_filter = "tags: production" - } + } } data "pagerduty_automation_actions_action" "foo" { diff --git a/pagerduty/import_pagerduty_automation_actions_action_test.go b/pagerdutyplugin/import_pagerduty_automation_actions_action_test.go similarity index 75% rename from pagerduty/import_pagerduty_automation_actions_action_test.go rename to pagerdutyplugin/import_pagerduty_automation_actions_action_test.go index baa5cab2c..bb07b2919 100644 --- a/pagerduty/import_pagerduty_automation_actions_action_test.go +++ b/pagerdutyplugin/import_pagerduty_automation_actions_action_test.go @@ -12,9 +12,9 @@ func TestAccPagerDutyAutomationActionsAction_import(t *testing.T) { actionName := fmt.Sprintf("tf-%s", acctest.RandString(5)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyAutomationActionsActionDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyAutomationActionsActionDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyAutomationActionsActionTypeProcessAutomationConfig(actionName), diff --git a/pagerdutyplugin/provider.go b/pagerdutyplugin/provider.go index 4a6f7e795..aed2f61f5 100644 --- a/pagerdutyplugin/provider.go +++ b/pagerdutyplugin/provider.go @@ -52,6 +52,7 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro func (p *Provider) DataSources(_ context.Context) [](func() datasource.DataSource) { return [](func() datasource.DataSource){ + func() datasource.DataSource { return &dataSourceAutomationActionsAction{} }, func() datasource.DataSource { return &dataSourceBusinessService{} }, func() datasource.DataSource { return &dataSourceExtensionSchema{} }, func() datasource.DataSource { return &dataSourceIntegration{} }, @@ -69,6 +70,7 @@ func (p *Provider) DataSources(_ context.Context) [](func() datasource.DataSourc func (p *Provider) Resources(_ context.Context) [](func() resource.Resource) { return [](func() resource.Resource){ func() resource.Resource { return &resourceAddon{} }, + func() resource.Resource { return &resourceAutomationActionsAction{} }, func() resource.Resource { return &resourceBusinessService{} }, func() resource.Resource { return &resourceExtensionServiceNow{} }, func() resource.Resource { return &resourceExtension{} }, diff --git a/pagerdutyplugin/resource_pagerduty_automation_actions_action.go b/pagerdutyplugin/resource_pagerduty_automation_actions_action.go new file mode 100644 index 000000000..01b7a0b65 --- /dev/null +++ b/pagerdutyplugin/resource_pagerduty_automation_actions_action.go @@ -0,0 +1,448 @@ +package pagerduty + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/PagerDuty/go-pagerduty" + "github.com/PagerDuty/terraform-provider-pagerduty/util" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" +) + +type resourceAutomationActionsAction struct{ client *pagerduty.Client } + +var ( + _ resource.ResourceWithConfigure = (*resourceAutomationActionsAction)(nil) + _ resource.ResourceWithImportState = (*resourceAutomationActionsAction)(nil) + _ resource.ResourceWithValidateConfig = (*resourceAutomationActionsAction)(nil) +) + +func (r *resourceAutomationActionsAction) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "pagerduty_automation_actions_action" +} + +func (r *resourceAutomationActionsAction) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "name": schema.StringAttribute{Required: true}, + "description": schema.StringAttribute{Optional: true}, + "action_type": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.OneOf("script", "process_automation"), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "runner_id": schema.StringAttribute{Optional: true}, + "type": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "action_classification": schema.StringAttribute{ + Optional: true, + Computed: true, + Validators: []validator.String{ + stringvalidator.OneOf("diagnostic", "remediation"), + }, + }, + "runner_type": schema.StringAttribute{Optional: true, Computed: true}, + "creation_time": schema.StringAttribute{Computed: true}, + "modify_time": schema.StringAttribute{Computed: true}, + "action_data_reference": schema.ListAttribute{ + Required: true, + ElementType: actionDataReferenceObjectType, + }, + }, + } +} + +func (r *resourceAutomationActionsAction) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) { + var actionType types.String + + d := req.Config.GetAttribute(ctx, path.Root("action_type"), &actionType) + if resp.Diagnostics.Append(d...); d.HasError() { + return + } + + if actionType.ValueString() == "script" { + var script types.String + scriptPath := path.Root("action_data_reference").AtListIndex(0).AtName("script") + d := req.Config.GetAttribute(ctx, scriptPath, &script) + if resp.Diagnostics.Append(d...); d.HasError() { + return + } + if script.IsNull() { + resp.Diagnostics.AddAttributeError(scriptPath, "action_data_reference script is required", "") + return + } + } + + if actionType.ValueString() == "process_automation" { + var script types.String + scriptPath := path.Root("action_data_reference").AtListIndex(0).AtName("process_automation_job_id") + d := req.Config.GetAttribute(ctx, scriptPath, &script) + if resp.Diagnostics.Append(d...); d.HasError() { + return + } + if script.IsNull() { + resp.Diagnostics.AddAttributeError(scriptPath, "action_data_reference process_automation_job_id is required", "") + return + } + } +} + +var actionDataReferenceObjectType = types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "process_automation_job_id": types.StringType, + "process_automation_job_arguments": types.StringType, + "process_automation_node_filter": types.StringType, + "script": types.StringType, + "invocation_command": types.StringType, + }, +} + +func (r *resourceAutomationActionsAction) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var model resourceAutomationActionsActionModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) + if resp.Diagnostics.HasError() { + return + } + plan := buildPagerdutyAutomationActionsAction(ctx, &model, &resp.Diagnostics) + log.Printf("[INFO] Creating PagerDuty automation action %s", plan.Name) + + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + o := pagerduty.CreateAutomationActionOptions{Action: *plan} + response, err := r.client.CreateAutomationActionWithContext(ctx, o) + if err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + plan.ID = response.ID + return nil + }) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error creating PagerDuty automation action %s", plan.Name), + err.Error(), + ) + return + } + + model, err = requestGetAutomationActionsAction(ctx, r.client, plan.ID, true, &resp.Diagnostics) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty automation action %s", plan.ID), + err.Error(), + ) + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) +} + +func (r *resourceAutomationActionsAction) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var id types.String + + resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &id)...) + if resp.Diagnostics.HasError() { + return + } + log.Printf("[INFO] Reading PagerDuty automation action %s", id) + + state, err := requestGetAutomationActionsAction(ctx, r.client, id.ValueString(), false, &resp.Diagnostics) + if err != nil { + if util.IsNotFoundError(err) { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty automation action %s", id), + err.Error(), + ) + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *resourceAutomationActionsAction) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var model resourceAutomationActionsActionModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) + if resp.Diagnostics.HasError() { + return + } + + plan := buildPagerdutyAutomationActionsAction(ctx, &model, &resp.Diagnostics) + log.Printf("[INFO] Updating PagerDuty automation action %s", plan.ID) + + o := pagerduty.UpdateAutomationActionOptions{Action: *plan} + _, err := r.client.UpdateAutomationActionWithContext(ctx, o) + if err != nil { + if util.IsNotFoundError(err) { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError( + fmt.Sprintf("Error updating PagerDuty automation action %s", plan.ID), + err.Error(), + ) + return + } + + state, err := requestGetAutomationActionsAction(ctx, r.client, plan.ID, false, &resp.Diagnostics) + if err != nil { + if util.IsNotFoundError(err) { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty automation action %s", plan.ID), + err.Error(), + ) + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *resourceAutomationActionsAction) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var id types.String + + resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &id)...) + if resp.Diagnostics.HasError() { + return + } + log.Printf("[INFO] Deleting PagerDuty automation action %s", id) + + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + err := r.client.DeleteAutomationActionWithContext(ctx, id.ValueString()) + if err != nil && !util.IsNotFoundError(err) { + return retry.NonRetryableError(err) + } + return nil + }) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error deleting PagerDuty automation action %s", id), + err.Error(), + ) + } + + resp.State.RemoveResource(ctx) +} + +func (r *resourceAutomationActionsAction) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + resp.Diagnostics.Append(ConfigurePagerdutyClient(&r.client, req.ProviderData)...) +} + +func (r *resourceAutomationActionsAction) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +type resourceAutomationActionsActionModel struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + ActionType types.String `tfsdk:"action_type"` + RunnerID types.String `tfsdk:"runner_id"` + Type types.String `tfsdk:"type"` + ActionClassification types.String `tfsdk:"action_classification"` + RunnerType types.String `tfsdk:"runner_type"` + CreationTime types.String `tfsdk:"creation_time"` + ModifyTime types.String `tfsdk:"modify_time"` + ActionDataReference types.List `tfsdk:"action_data_reference"` +} + +func requestGetAutomationActionsAction(ctx context.Context, client *pagerduty.Client, id string, retryNotFound bool, diags *diag.Diagnostics) (resourceAutomationActionsActionModel, error) { + var model resourceAutomationActionsActionModel + + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + action, err := client.GetAutomationActionWithContext(ctx, id) + if err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + if !retryNotFound && util.IsNotFoundError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + model = flattenAutomationActionsAction(action, diags) + return nil + }) + + return model, err +} + +func buildPagerdutyAutomationActionsAction(ctx context.Context, model *resourceAutomationActionsActionModel, diags *diag.Diagnostics) *pagerduty.AutomationAction { + action := pagerduty.AutomationAction{ + APIObject: pagerduty.APIObject{ID: model.ID.ValueString()}, + Name: model.Name.ValueString(), + ActionType: model.ActionType.ValueString(), + } + + if !model.ActionDataReference.IsNull() || !model.ActionDataReference.IsUnknown() { + action.ActionDataReference = buildActionDataReference(ctx, model.ActionDataReference, diags) + } + + if !model.Description.IsNull() || !model.Description.IsUnknown() { + action.Description = model.Description.ValueString() + } else { + diags.AddAttributeError( + path.Root("description"), + "action description must be specified when creating an action", + "", + ) + } + + if !model.RunnerID.IsNull() || !model.RunnerID.IsUnknown() { + action.Runner = model.RunnerID.ValueString() + } + + if !model.Type.IsNull() || !model.Type.IsUnknown() { + action.Type = model.Type.ValueString() + } + + if !model.ActionClassification.IsNull() || !model.ActionClassification.IsUnknown() { + action.ActionClassification = model.ActionClassification.ValueStringPointer() + } + + if !model.RunnerType.IsNull() || !model.RunnerType.IsUnknown() { + action.RunnerType = model.RunnerType.ValueString() + } + + if !model.CreationTime.IsNull() || !model.CreationTime.IsUnknown() { + action.CreationTime = model.CreationTime.ValueString() + } + + if !model.ModifyTime.IsNull() || !model.ModifyTime.IsUnknown() { + action.ModifyTime = model.ModifyTime.ValueString() + } + + return &action +} + +func buildActionDataReference(ctx context.Context, list types.List, diags *diag.Diagnostics) *pagerduty.ActionDataReference { + var target []struct { + ProcessAutomationJobID types.String `tfsdk:"process_automation_job_id"` + ProcessAutomationJobArguments types.String `tfsdk:"process_automation_job_arguments"` + ProcessAutomationNodeFilter types.String `tfsdk:"process_automation_node_filter"` + Script types.String `tfsdk:"script"` + InvocationCommand types.String `tfsdk:"invocation_command"` + } + + d := list.ElementsAs(ctx, &target, false) + if diags.Append(d...); d.HasError() { + return nil + } + obj := target[0] + + var script, invocationCommand *string + s1 := obj.Script.ValueString() + script = &s1 + s2 := obj.InvocationCommand.ValueString() + invocationCommand = &s2 + + var jobID, jobArguments, nodeFilter *string + s3 := obj.ProcessAutomationJobID.ValueString() + jobID = &s3 + s4 := obj.ProcessAutomationJobArguments.ValueString() + jobArguments = &s4 + s5 := obj.ProcessAutomationNodeFilter.ValueString() + nodeFilter = &s5 + + out := &pagerduty.ActionDataReference{ + Script: script, + InvocationCommand: invocationCommand, + ProcessAutomationJobID: jobID, + ProcessAutomationJobArguments: jobArguments, + ProcessAutomationNodeFilter: nodeFilter, + } + return out +} + +func flattenAutomationActionsAction(response *pagerduty.AutomationAction, diags *diag.Diagnostics) resourceAutomationActionsActionModel { + model := resourceAutomationActionsActionModel{ + ID: types.StringValue(response.ID), + Name: types.StringValue(response.Name), + Type: types.StringValue(response.Type), + ActionType: types.StringValue(response.ActionType), + CreationTime: types.StringValue(response.CreationTime), + ModifyTime: types.StringValue(response.ModifyTime), + Description: types.StringValue(response.Description), + ActionDataReference: flattenActionDataReference(response.ActionDataReference, diags), + } + + if response.Runner != "" { + model.RunnerID = types.StringValue(response.Runner) + } + + if response.RunnerType != "" { + model.RunnerType = types.StringValue(response.RunnerType) + } + + if response.ActionClassification != nil { + model.ActionClassification = types.StringValue(*response.ActionClassification) + } + + return model +} + +func flattenActionDataReference(ref *pagerduty.ActionDataReference, diags *diag.Diagnostics) types.List { + values := map[string]attr.Value{ + "script": types.StringNull(), + "invocation_command": types.StringNull(), + "process_automation_job_id": types.StringNull(), + "process_automation_job_arguments": types.StringNull(), + "process_automation_node_filter": types.StringNull(), + } + + for k, v := range map[string]*string{ + "script": ref.Script, + "invocation_command": ref.InvocationCommand, + "process_automation_job_id": ref.ProcessAutomationJobID, + "process_automation_job_arguments": ref.ProcessAutomationJobArguments, + "process_automation_node_filter": ref.ProcessAutomationNodeFilter, + } { + if v != nil { + values[k] = types.StringValue(*v) + } + } + + obj, d := types.ObjectValue(actionDataReferenceObjectType.AttrTypes, values) + if diags.Append(d...); d.HasError() { + return types.ListNull(actionDataReferenceObjectType) + } + + list, d := types.ListValue(actionDataReferenceObjectType, []attr.Value{obj}) + if diags.Append(d...); d.HasError() { + return types.ListNull(actionDataReferenceObjectType) + } + + return list +} diff --git a/pagerduty/resource_pagerduty_automation_actions_action_test.go b/pagerdutyplugin/resource_pagerduty_automation_actions_action_test.go similarity index 87% rename from pagerduty/resource_pagerduty_automation_actions_action_test.go rename to pagerdutyplugin/resource_pagerduty_automation_actions_action_test.go index e47e21ed6..20557d29e 100644 --- a/pagerduty/resource_pagerduty_automation_actions_action_test.go +++ b/pagerdutyplugin/resource_pagerduty_automation_actions_action_test.go @@ -1,6 +1,7 @@ package pagerduty import ( + "context" "fmt" "testing" @@ -28,9 +29,9 @@ func TestAccPagerDutyAutomationActionsActionTypeProcessAutomation_Basic(t *testi actionClassificationUpdated := "remediation" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyAutomationActionsActionDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyAutomationActionsActionDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyAutomationActionsActionTypeProcessAutomationConfig(actionName), @@ -48,10 +49,10 @@ func TestAccPagerDutyAutomationActionsActionTypeProcessAutomation_Basic(t *testi // Known defect with inconsistent handling of nested aggregates: https://github.com/hashicorp/terraform-plugin-sdk/issues/413 resource.TestCheckResourceAttr( "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_node_filter", "tags: production"), - resource.TestCheckResourceAttr( - "pagerduty_automation_actions_action.foo", "action_data_reference.0.script", ""), - resource.TestCheckResourceAttr( - "pagerduty_automation_actions_action.foo", "action_data_reference.0.invocation_command", ""), + resource.TestCheckNoResourceAttr( + "pagerduty_automation_actions_action.foo", "action_data_reference.0.script"), + resource.TestCheckNoResourceAttr( + "pagerduty_automation_actions_action.foo", "action_data_reference.0.invocation_command"), resource.TestCheckResourceAttrSet("pagerduty_automation_actions_action.foo", "id"), resource.TestCheckResourceAttrSet("pagerduty_automation_actions_action.foo", "creation_time"), resource.TestCheckResourceAttrSet("pagerduty_automation_actions_action.foo", "modify_time"), @@ -70,15 +71,14 @@ func TestAccPagerDutyAutomationActionsActionTypeProcessAutomation_Basic(t *testi resource.TestCheckResourceAttr("pagerduty_automation_actions_action.foo", "action_classification", actionClassificationUpdated), resource.TestCheckResourceAttr( "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_job_id", "updated_pa_job_id_123"), - resource.TestCheckResourceAttr( - "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_job_arguments", ""), - // Known defect with inconsistent handling of nested aggregates: https://github.com/hashicorp/terraform-plugin-sdk/issues/413 - resource.TestCheckResourceAttr( - "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_node_filter", ""), - resource.TestCheckResourceAttr( - "pagerduty_automation_actions_action.foo", "action_data_reference.0.script", ""), - resource.TestCheckResourceAttr( - "pagerduty_automation_actions_action.foo", "action_data_reference.0.invocation_command", ""), + resource.TestCheckNoResourceAttr( + "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_job_arguments"), + resource.TestCheckNoResourceAttr( + "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_node_filter"), + resource.TestCheckNoResourceAttr( + "pagerduty_automation_actions_action.foo", "action_data_reference.0.script"), + resource.TestCheckNoResourceAttr( + "pagerduty_automation_actions_action.foo", "action_data_reference.0.invocation_command"), resource.TestCheckResourceAttrSet("pagerduty_automation_actions_action.foo", "id"), resource.TestCheckResourceAttrSet("pagerduty_automation_actions_action.foo", "creation_time"), resource.TestCheckResourceAttrSet("pagerduty_automation_actions_action.foo", "modify_time"), @@ -98,9 +98,9 @@ func TestAccPagerDutyAutomationActionsActionTypeScript_Basic(t *testing.T) { actionClassificationUpdated := "remediation" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyAutomationActionsActionDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyAutomationActionsActionDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyAutomationActionsActionTypeScriptConfig(actionName), @@ -112,12 +112,12 @@ func TestAccPagerDutyAutomationActionsActionTypeScript_Basic(t *testing.T) { resource.TestCheckResourceAttr("pagerduty_automation_actions_action.foo", "type", "action"), resource.TestCheckNoResourceAttr("pagerduty_automation_actions_action.foo", "action_classification"), // Known defect with inconsistent handling of nested aggregates: https://github.com/hashicorp/terraform-plugin-sdk/issues/413 - resource.TestCheckResourceAttr( - "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_job_id", ""), - resource.TestCheckResourceAttr( - "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_job_arguments", ""), - resource.TestCheckResourceAttr( - "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_node_filter", ""), + resource.TestCheckNoResourceAttr( + "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_job_id"), + resource.TestCheckNoResourceAttr( + "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_job_arguments"), + resource.TestCheckNoResourceAttr( + "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_node_filter"), resource.TestCheckResourceAttr( "pagerduty_automation_actions_action.foo", "action_data_reference.0.script", "java --version"), resource.TestCheckResourceAttr( @@ -139,16 +139,16 @@ func TestAccPagerDutyAutomationActionsActionTypeScript_Basic(t *testing.T) { "pagerduty_automation_actions_action.foo", "description", descriptionUpdated), resource.TestCheckResourceAttr( "pagerduty_automation_actions_action.foo", "action_classification", actionClassificationUpdated), - resource.TestCheckResourceAttr( - "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_job_id", ""), - resource.TestCheckResourceAttr( - "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_job_arguments", ""), - resource.TestCheckResourceAttr( - "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_node_filter", ""), + resource.TestCheckNoResourceAttr( + "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_job_id"), + resource.TestCheckNoResourceAttr( + "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_job_arguments"), + resource.TestCheckNoResourceAttr( + "pagerduty_automation_actions_action.foo", "action_data_reference.0.process_automation_node_filter"), resource.TestCheckResourceAttr( "pagerduty_automation_actions_action.foo", "action_data_reference.0.script", "echo 777"), - resource.TestCheckResourceAttr( - "pagerduty_automation_actions_action.foo", "action_data_reference.0.invocation_command", ""), + resource.TestCheckNoResourceAttr( + "pagerduty_automation_actions_action.foo", "action_data_reference.0.invocation_command"), resource.TestCheckResourceAttrSet("pagerduty_automation_actions_action.foo", "modify_time"), resource.TestCheckNoResourceAttr("pagerduty_automation_actions_action.foo", "runner_type"), resource.TestCheckNoResourceAttr("pagerduty_automation_actions_action.foo", "runner_id"), @@ -159,12 +159,13 @@ func TestAccPagerDutyAutomationActionsActionTypeScript_Basic(t *testing.T) { } func testAccCheckPagerDutyAutomationActionsActionDestroy(s *terraform.State) error { - client, _ := testAccProvider.Meta().(*Config).Client() + client := testAccProvider.client for _, r := range s.RootModule().Resources { if r.Type != "pagerduty_automation_actions_action" { continue } - if _, _, err := client.AutomationActionsAction.Get(r.Primary.ID); err == nil { + ctx := context.Background() + if _, err := client.GetAutomationActionWithContext(ctx, r.Primary.ID); err == nil { return fmt.Errorf("Automation Actions Action still exists") } } @@ -181,8 +182,9 @@ func testAccCheckPagerDutyAutomationActionsActionExists(n string) resource.TestC return fmt.Errorf("No Automation Actions Action ID is set") } - client, _ := testAccProvider.Meta().(*Config).Client() - found, _, err := client.AutomationActionsAction.Get(rs.Primary.ID) + client := testAccProvider.client + ctx := context.Background() + found, err := client.GetAutomationActionWithContext(ctx, rs.Primary.ID) if err != nil { return err } From b3e9d164243cf64e553069bded32d959a5ef589c Mon Sep 17 00:00:00 2001 From: Carlos Gajardo Date: Tue, 2 Jul 2024 17:13:43 -0400 Subject: [PATCH 3/8] Migrate automation actions action service association --- go.mod | 2 + pagerduty/provider.go | 72 +++---- ...tion_actions_action_service_association.go | 144 ------------- ...actions_action_service_association_test.go | 6 +- pagerdutyplugin/provider.go | 1 + ...tion_actions_action_service_association.go | 197 ++++++++++++++++++ ...actions_action_service_association_test.go | 22 +- 7 files changed, 252 insertions(+), 192 deletions(-) delete mode 100644 pagerduty/resource_pagerduty_automation_actions_action_service_association.go rename {pagerduty => pagerdutyplugin}/import_pagerduty_automation_actions_action_service_association_test.go (80%) create mode 100644 pagerdutyplugin/resource_pagerduty_automation_actions_action_service_association.go rename {pagerduty => pagerdutyplugin}/resource_pagerduty_automation_actions_action_service_association_test.go (83%) diff --git a/go.mod b/go.mod index 98a5fd37d..776539d11 100644 --- a/go.mod +++ b/go.mod @@ -76,3 +76,5 @@ require ( google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) + +replace github.com/PagerDuty/go-pagerduty => ../go-pagerduty/ diff --git a/pagerduty/provider.go b/pagerduty/provider.go index bda9c4ad7..ae623a2e3 100644 --- a/pagerduty/provider.go +++ b/pagerduty/provider.go @@ -111,42 +111,42 @@ func Provider(isMux bool) *schema.Provider { }, ResourcesMap: map[string]*schema.Resource{ - "pagerduty_addon": resourcePagerDutyAddon(), - "pagerduty_escalation_policy": resourcePagerDutyEscalationPolicy(), - "pagerduty_maintenance_window": resourcePagerDutyMaintenanceWindow(), - "pagerduty_schedule": resourcePagerDutySchedule(), - "pagerduty_service": resourcePagerDutyService(), - "pagerduty_service_integration": resourcePagerDutyServiceIntegration(), - "pagerduty_team": resourcePagerDutyTeam(), - "pagerduty_team_membership": resourcePagerDutyTeamMembership(), - "pagerduty_user": resourcePagerDutyUser(), - "pagerduty_user_contact_method": resourcePagerDutyUserContactMethod(), - "pagerduty_user_notification_rule": resourcePagerDutyUserNotificationRule(), - "pagerduty_event_rule": resourcePagerDutyEventRule(), - "pagerduty_ruleset": resourcePagerDutyRuleset(), - "pagerduty_ruleset_rule": resourcePagerDutyRulesetRule(), - "pagerduty_business_service": resourcePagerDutyBusinessService(), - "pagerduty_response_play": resourcePagerDutyResponsePlay(), - "pagerduty_service_event_rule": resourcePagerDutyServiceEventRule(), - "pagerduty_slack_connection": resourcePagerDutySlackConnection(), - "pagerduty_business_service_subscriber": resourcePagerDutyBusinessServiceSubscriber(), - "pagerduty_webhook_subscription": resourcePagerDutyWebhookSubscription(), - "pagerduty_event_orchestration": resourcePagerDutyEventOrchestration(), - "pagerduty_event_orchestration_integration": resourcePagerDutyEventOrchestrationIntegration(), - "pagerduty_event_orchestration_global": resourcePagerDutyEventOrchestrationPathGlobal(), - "pagerduty_event_orchestration_router": resourcePagerDutyEventOrchestrationPathRouter(), - "pagerduty_event_orchestration_unrouted": resourcePagerDutyEventOrchestrationPathUnrouted(), - "pagerduty_event_orchestration_service": resourcePagerDutyEventOrchestrationPathService(), - "pagerduty_event_orchestration_global_cache_variable": resourcePagerDutyEventOrchestrationGlobalCacheVariable(), - "pagerduty_event_orchestration_service_cache_variable": resourcePagerDutyEventOrchestrationServiceCacheVariable(), - "pagerduty_automation_actions_runner": resourcePagerDutyAutomationActionsRunner(), - "pagerduty_automation_actions_action_team_association": resourcePagerDutyAutomationActionsActionTeamAssociation(), - "pagerduty_automation_actions_runner_team_association": resourcePagerDutyAutomationActionsRunnerTeamAssociation(), - "pagerduty_incident_workflow": resourcePagerDutyIncidentWorkflow(), - "pagerduty_incident_workflow_trigger": resourcePagerDutyIncidentWorkflowTrigger(), - "pagerduty_automation_actions_action_service_association": resourcePagerDutyAutomationActionsActionServiceAssociation(), - "pagerduty_incident_custom_field": resourcePagerDutyIncidentCustomField(), - "pagerduty_incident_custom_field_option": resourcePagerDutyIncidentCustomFieldOption(), + "pagerduty_addon": resourcePagerDutyAddon(), + "pagerduty_escalation_policy": resourcePagerDutyEscalationPolicy(), + "pagerduty_maintenance_window": resourcePagerDutyMaintenanceWindow(), + "pagerduty_schedule": resourcePagerDutySchedule(), + "pagerduty_service": resourcePagerDutyService(), + "pagerduty_service_integration": resourcePagerDutyServiceIntegration(), + "pagerduty_team": resourcePagerDutyTeam(), + "pagerduty_team_membership": resourcePagerDutyTeamMembership(), + "pagerduty_user": resourcePagerDutyUser(), + "pagerduty_user_contact_method": resourcePagerDutyUserContactMethod(), + "pagerduty_user_notification_rule": resourcePagerDutyUserNotificationRule(), + "pagerduty_event_rule": resourcePagerDutyEventRule(), + "pagerduty_ruleset": resourcePagerDutyRuleset(), + "pagerduty_ruleset_rule": resourcePagerDutyRulesetRule(), + "pagerduty_business_service": resourcePagerDutyBusinessService(), + "pagerduty_response_play": resourcePagerDutyResponsePlay(), + "pagerduty_service_event_rule": resourcePagerDutyServiceEventRule(), + "pagerduty_slack_connection": resourcePagerDutySlackConnection(), + "pagerduty_business_service_subscriber": resourcePagerDutyBusinessServiceSubscriber(), + "pagerduty_webhook_subscription": resourcePagerDutyWebhookSubscription(), + "pagerduty_event_orchestration": resourcePagerDutyEventOrchestration(), + "pagerduty_event_orchestration_integration": resourcePagerDutyEventOrchestrationIntegration(), + "pagerduty_event_orchestration_global": resourcePagerDutyEventOrchestrationPathGlobal(), + "pagerduty_event_orchestration_router": resourcePagerDutyEventOrchestrationPathRouter(), + "pagerduty_event_orchestration_unrouted": resourcePagerDutyEventOrchestrationPathUnrouted(), + "pagerduty_event_orchestration_service": resourcePagerDutyEventOrchestrationPathService(), + "pagerduty_event_orchestration_global_cache_variable": resourcePagerDutyEventOrchestrationGlobalCacheVariable(), + "pagerduty_event_orchestration_service_cache_variable": resourcePagerDutyEventOrchestrationServiceCacheVariable(), + "pagerduty_automation_actions_action": resourcePagerDutyAutomationActionsAction(), + "pagerduty_automation_actions_action_team_association": resourcePagerDutyAutomationActionsActionTeamAssociation(), + "pagerduty_automation_actions_runner": resourcePagerDutyAutomationActionsRunner(), + "pagerduty_automation_actions_runner_team_association": resourcePagerDutyAutomationActionsRunnerTeamAssociation(), + "pagerduty_incident_workflow": resourcePagerDutyIncidentWorkflow(), + "pagerduty_incident_workflow_trigger": resourcePagerDutyIncidentWorkflowTrigger(), + "pagerduty_incident_custom_field": resourcePagerDutyIncidentCustomField(), + "pagerduty_incident_custom_field_option": resourcePagerDutyIncidentCustomFieldOption(), }, } diff --git a/pagerduty/resource_pagerduty_automation_actions_action_service_association.go b/pagerduty/resource_pagerduty_automation_actions_action_service_association.go deleted file mode 100644 index 4b4c4ddff..000000000 --- a/pagerduty/resource_pagerduty_automation_actions_action_service_association.go +++ /dev/null @@ -1,144 +0,0 @@ -package pagerduty - -import ( - "fmt" - "log" - "net/http" - "time" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func resourcePagerDutyAutomationActionsActionServiceAssociation() *schema.Resource { - return &schema.Resource{ - Create: resourcePagerDutyAutomationActionsActionServiceAssociationCreate, - Read: resourcePagerDutyAutomationActionsActionServiceAssociationRead, - Delete: resourcePagerDutyAutomationActionsActionServiceAssociationDelete, - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - Schema: map[string]*schema.Schema{ - "action_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "service_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - }, - } -} - -func resourcePagerDutyAutomationActionsActionServiceAssociationCreate(d *schema.ResourceData, meta interface{}) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - actionID := d.Get("action_id").(string) - serviceID := d.Get("service_id").(string) - - log.Printf("[INFO] Creating PagerDuty AutomationActionsActionServiceAssociation %s:%s", d.Get("action_id").(string), d.Get("service_id").(string)) - - retryErr := retry.Retry(2*time.Minute, func() *retry.RetryError { - if serviceRef, _, err := client.AutomationActionsAction.AssociateToService(actionID, serviceID); err != nil { - if isErrCode(err, 429) { - time.Sleep(2 * time.Second) - return retry.RetryableError(err) - } - - return retry.NonRetryableError(err) - } else if serviceRef != nil { - d.SetId(fmt.Sprintf("%s:%s", actionID, serviceID)) - } - return nil - }) - - if retryErr != nil { - return retryErr - } - - return fetchPagerDutyAutomationActionsActionServiceAssociation(d, meta, handleNotFoundError) -} - -func fetchPagerDutyAutomationActionsActionServiceAssociation(d *schema.ResourceData, meta interface{}, errCallback func(error, *schema.ResourceData) error) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - actionID, serviceID, err := resourcePagerDutyParseColonCompoundID(d.Id()) - if err != nil { - return err - } - - return retry.Retry(2*time.Minute, func() *retry.RetryError { - resp, _, err := client.AutomationActionsAction.GetAssociationToService(actionID, serviceID) - if err != nil { - if isErrCode(err, http.StatusBadRequest) { - return retry.NonRetryableError(err) - } - - errResp := errCallback(err, d) - if errResp != nil { - time.Sleep(2 * time.Second) - return retry.RetryableError(errResp) - } - - return nil - } - - if resp.Service.ID != serviceID { - log.Printf("[WARN] Removing %s since the service: %s is not associated to the action: %s", d.Id(), actionID, serviceID) - d.SetId("") - return nil - } - - d.Set("action_id", actionID) - d.Set("service_id", resp.Service.ID) - - return nil - }) -} - -func resourcePagerDutyAutomationActionsActionServiceAssociationRead(d *schema.ResourceData, meta interface{}) error { - return fetchPagerDutyAutomationActionsActionServiceAssociation(d, meta, handleNotFoundError) -} - -func resourcePagerDutyAutomationActionsActionServiceAssociationDelete(d *schema.ResourceData, meta interface{}) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - actionID, serviceID, err := resourcePagerDutyParseColonCompoundID(d.Id()) - if err != nil { - return err - } - - log.Printf("[INFO] Deleting PagerDuty AutomationActionsActionServiceAssociation %s", d.Id()) - - retryErr := retry.Retry(2*time.Minute, func() *retry.RetryError { - if _, err := client.AutomationActionsAction.DissociateFromService(actionID, serviceID); err != nil { - if isErrCode(err, http.StatusBadRequest) { - return retry.NonRetryableError(err) - } - - return retry.RetryableError(err) - } - return nil - }) - if retryErr != nil { - time.Sleep(2 * time.Second) - return retryErr - } - d.SetId("") - - // giving the API time to catchup - time.Sleep(time.Second) - return nil -} diff --git a/pagerduty/import_pagerduty_automation_actions_action_service_association_test.go b/pagerdutyplugin/import_pagerduty_automation_actions_action_service_association_test.go similarity index 80% rename from pagerduty/import_pagerduty_automation_actions_action_service_association_test.go rename to pagerdutyplugin/import_pagerduty_automation_actions_action_service_association_test.go index 16ad5b803..b05c9ee79 100644 --- a/pagerduty/import_pagerduty_automation_actions_action_service_association_test.go +++ b/pagerdutyplugin/import_pagerduty_automation_actions_action_service_association_test.go @@ -16,9 +16,9 @@ func TestAccPagerDutyAutomationActionsActionServiceAssociation_import(t *testing serviceName := fmt.Sprintf("tf-%s", acctest.RandString(5)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyAutomationActionsActionServiceAssociationDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyAutomationActionsActionServiceAssociationDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyAutomationActionsActionServiceAssociationConfig(username, email, escalationPolicy, serviceName, actionName), diff --git a/pagerdutyplugin/provider.go b/pagerdutyplugin/provider.go index aed2f61f5..5a0f7ad88 100644 --- a/pagerdutyplugin/provider.go +++ b/pagerdutyplugin/provider.go @@ -70,6 +70,7 @@ func (p *Provider) DataSources(_ context.Context) [](func() datasource.DataSourc func (p *Provider) Resources(_ context.Context) [](func() resource.Resource) { return [](func() resource.Resource){ func() resource.Resource { return &resourceAddon{} }, + func() resource.Resource { return &resourceAutomationActionsActionServiceAssociation{} }, func() resource.Resource { return &resourceAutomationActionsAction{} }, func() resource.Resource { return &resourceBusinessService{} }, func() resource.Resource { return &resourceExtensionServiceNow{} }, diff --git a/pagerdutyplugin/resource_pagerduty_automation_actions_action_service_association.go b/pagerdutyplugin/resource_pagerduty_automation_actions_action_service_association.go new file mode 100644 index 000000000..35abcca95 --- /dev/null +++ b/pagerdutyplugin/resource_pagerduty_automation_actions_action_service_association.go @@ -0,0 +1,197 @@ +package pagerduty + +import ( + "context" + "errors" + "fmt" + "log" + "time" + + "github.com/PagerDuty/go-pagerduty" + "github.com/PagerDuty/terraform-provider-pagerduty/util" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" +) + +type resourceAutomationActionsActionServiceAssociation struct{ client *pagerduty.Client } + +var ( + _ resource.ResourceWithConfigure = (*resourceAutomationActionsActionServiceAssociation)(nil) + _ resource.ResourceWithImportState = (*resourceAutomationActionsActionServiceAssociation)(nil) +) + +func (r *resourceAutomationActionsActionServiceAssociation) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "pagerduty_automation_actions_action_service_association" +} + +func (r *resourceAutomationActionsActionServiceAssociation) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "action_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "service_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + }, + } +} + +func (r *resourceAutomationActionsActionServiceAssociation) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var actionID, serviceID types.String + resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("action_id"), &actionID)...) + resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("service_id"), &serviceID)...) + + if resp.Diagnostics.HasError() { + return + } + log.Printf("[INFO] Creating PagerDuty automation action service association %s:%s", actionID, serviceID) + id := fmt.Sprintf("%s:%s", actionID.ValueString(), serviceID.ValueString()) + + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + o := pagerduty.AssociateAutomationActionServiceOptions{ + Service: pagerduty.APIReference{ + ID: serviceID.ValueString(), + Type: "service_reference", + }, + } + if _, err := r.client.AssociateAutomationActionServiceWithContext(ctx, actionID.ValueString(), o); err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + return nil + }) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error creating PagerDuty automation action service association %s", id), + err.Error(), + ) + return + } + + model, err := requestGetAutomationActionsActionServiceAssociation(ctx, r.client, id, true) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty automation action service association %s", id), + err.Error(), + ) + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) +} + +func (r *resourceAutomationActionsActionServiceAssociation) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var id types.String + + resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &id)...) + if resp.Diagnostics.HasError() { + return + } + log.Printf("[INFO] Reading PagerDuty automation action service association %s", id) + + state, err := requestGetAutomationActionsActionServiceAssociation(ctx, r.client, id.ValueString(), false) + if err != nil { + if errors.Is(err, errAutomationActionServiceNotAssociated) || util.IsNotFoundError(err) { + log.Printf("[WARN] Removing automation action service association %s: %s", id, err.Error()) + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty automation action service association %s", id), + err.Error(), + ) + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *resourceAutomationActionsActionServiceAssociation) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { +} + +func (r *resourceAutomationActionsActionServiceAssociation) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var id types.String + + resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &id)...) + if resp.Diagnostics.HasError() { + return + } + log.Printf("[INFO] Deleting PagerDuty automation action service association %s", id) + + actionID, serviceID, err := util.ResourcePagerDutyParseColonCompoundID(id.ValueString()) + if err != nil { + return + } + + err = r.client.DisassociateAutomationActionServiceWithContext(ctx, actionID, serviceID) + if err != nil && !util.IsNotFoundError(err) { + resp.Diagnostics.AddError( + fmt.Sprintf("Error deleting PagerDuty automation action service association %s", id), + err.Error(), + ) + return + } + resp.State.RemoveResource(ctx) +} + +func (r *resourceAutomationActionsActionServiceAssociation) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + resp.Diagnostics.Append(ConfigurePagerdutyClient(&r.client, req.ProviderData)...) +} + +func (r *resourceAutomationActionsActionServiceAssociation) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +type resourceAutomationActionsActionServiceAssociationModel struct { + ID types.String `tfsdk:"id"` + ActionID types.String `tfsdk:"action_id"` + ServiceID types.String `tfsdk:"service_id"` +} + +var errAutomationActionServiceNotAssociated = errors.New("service is not associated to this action") + +func requestGetAutomationActionsActionServiceAssociation(ctx context.Context, client *pagerduty.Client, id string, retryNotFound bool) (resourceAutomationActionsActionServiceAssociationModel, error) { + var model resourceAutomationActionsActionServiceAssociationModel + + actionID, serviceID, err := util.ResourcePagerDutyParseColonCompoundID(id) + if err != nil { + return model, err + } + + err = retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + response, err := client.GetAutomationActionServiceWithContext(ctx, actionID, serviceID) + if err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + if !retryNotFound && util.IsNotFoundError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + if response.Service.ID != serviceID { + return retry.NonRetryableError(errAutomationActionServiceNotAssociated) + } + model.ID = types.StringValue(id) + model.ActionID = types.StringValue(actionID) + model.ServiceID = types.StringValue(serviceID) + return nil + }) + + return model, err +} diff --git a/pagerduty/resource_pagerduty_automation_actions_action_service_association_test.go b/pagerdutyplugin/resource_pagerduty_automation_actions_action_service_association_test.go similarity index 83% rename from pagerduty/resource_pagerduty_automation_actions_action_service_association_test.go rename to pagerdutyplugin/resource_pagerduty_automation_actions_action_service_association_test.go index 9fb4b777a..31cbcc683 100644 --- a/pagerduty/resource_pagerduty_automation_actions_action_service_association_test.go +++ b/pagerdutyplugin/resource_pagerduty_automation_actions_action_service_association_test.go @@ -1,9 +1,11 @@ package pagerduty import ( + "context" "fmt" "testing" + "github.com/PagerDuty/terraform-provider-pagerduty/util" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -28,9 +30,9 @@ func TestAccPagerDutyAutomationActionsActionServiceAssociation_Basic(t *testing. serviceName := fmt.Sprintf("tf-%s", acctest.RandString(5)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyAutomationActionsActionServiceAssociationDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyAutomationActionsActionServiceAssociationDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyAutomationActionsActionServiceAssociationConfig(username, email, escalationPolicy, serviceName, actionName), @@ -45,17 +47,18 @@ func TestAccPagerDutyAutomationActionsActionServiceAssociation_Basic(t *testing. } func testAccCheckPagerDutyAutomationActionsActionServiceAssociationDestroy(s *terraform.State) error { - client, _ := testAccProvider.Meta().(*Config).Client() for _, r := range s.RootModule().Resources { if r.Type != "pagerduty_automation_actions_action_service_association" { continue } - actionID, serviceID, err := resourcePagerDutyParseColonCompoundID(r.Primary.ID) + actionID, serviceID, err := util.ResourcePagerDutyParseColonCompoundID(r.Primary.ID) if err != nil { return err } - if _, _, err := client.AutomationActionsAction.GetAssociationToService(actionID, serviceID); err == nil { + client := testAccProvider.client + ctx := context.Background() + if _, err := client.GetAutomationActionServiceWithContext(ctx, actionID, serviceID); err == nil { return fmt.Errorf("Automation Actions Runner still exists") } } @@ -72,13 +75,14 @@ func testAccCheckPagerDutyAutomationActionsActionServiceAssociationExists(n stri return fmt.Errorf("No Automation Actions Runner ID is set") } - client, _ := testAccProvider.Meta().(*Config).Client() - actionID, serviceID, err := resourcePagerDutyParseColonCompoundID(rs.Primary.ID) + actionID, serviceID, err := util.ResourcePagerDutyParseColonCompoundID(rs.Primary.ID) if err != nil { return err } - found, _, err := client.AutomationActionsAction.GetAssociationToService(actionID, serviceID) + ctx := context.Background() + client := testAccProvider.client + found, err := client.GetAutomationActionServiceWithContext(ctx, actionID, serviceID) if err != nil { return err } From 533bb7c67621d640189292b2df378fd980ff0f91 Mon Sep 17 00:00:00 2001 From: Carlos Gajardo Date: Tue, 9 Jul 2024 06:52:26 -0400 Subject: [PATCH 4/8] Migrate automation actions action team association --- go.mod | 2 - pagerduty/provider.go | 2 - ...omation_actions_action_team_association.go | 148 ------------- ...on_actions_action_team_association_test.go | 6 +- pagerdutyplugin/provider.go | 1 + ...omation_actions_action_team_association.go | 198 ++++++++++++++++++ ...on_actions_action_team_association_test.go | 24 ++- 7 files changed, 217 insertions(+), 164 deletions(-) delete mode 100644 pagerduty/resource_pagerduty_automation_actions_action_team_association.go rename {pagerduty => pagerdutyplugin}/import_pagerduty_automation_actions_action_team_association_test.go (76%) create mode 100644 pagerdutyplugin/resource_pagerduty_automation_actions_action_team_association.go rename {pagerduty => pagerdutyplugin}/resource_pagerduty_automation_actions_action_team_association_test.go (79%) diff --git a/go.mod b/go.mod index 776539d11..98a5fd37d 100644 --- a/go.mod +++ b/go.mod @@ -76,5 +76,3 @@ require ( google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) - -replace github.com/PagerDuty/go-pagerduty => ../go-pagerduty/ diff --git a/pagerduty/provider.go b/pagerduty/provider.go index ae623a2e3..6eaee4510 100644 --- a/pagerduty/provider.go +++ b/pagerduty/provider.go @@ -139,8 +139,6 @@ func Provider(isMux bool) *schema.Provider { "pagerduty_event_orchestration_service": resourcePagerDutyEventOrchestrationPathService(), "pagerduty_event_orchestration_global_cache_variable": resourcePagerDutyEventOrchestrationGlobalCacheVariable(), "pagerduty_event_orchestration_service_cache_variable": resourcePagerDutyEventOrchestrationServiceCacheVariable(), - "pagerduty_automation_actions_action": resourcePagerDutyAutomationActionsAction(), - "pagerduty_automation_actions_action_team_association": resourcePagerDutyAutomationActionsActionTeamAssociation(), "pagerduty_automation_actions_runner": resourcePagerDutyAutomationActionsRunner(), "pagerduty_automation_actions_runner_team_association": resourcePagerDutyAutomationActionsRunnerTeamAssociation(), "pagerduty_incident_workflow": resourcePagerDutyIncidentWorkflow(), diff --git a/pagerduty/resource_pagerduty_automation_actions_action_team_association.go b/pagerduty/resource_pagerduty_automation_actions_action_team_association.go deleted file mode 100644 index 0cf568fc2..000000000 --- a/pagerduty/resource_pagerduty_automation_actions_action_team_association.go +++ /dev/null @@ -1,148 +0,0 @@ -package pagerduty - -import ( - "fmt" - "log" - "net/http" - "time" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func resourcePagerDutyAutomationActionsActionTeamAssociation() *schema.Resource { - return &schema.Resource{ - Create: resourcePagerDutyAutomationActionsActionTeamAssociationCreate, - Read: resourcePagerDutyAutomationActionsActionTeamAssociationRead, - Delete: resourcePagerDutyAutomationActionsActionTeamAssociationDelete, - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - Schema: map[string]*schema.Schema{ - "action_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "team_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - }, - } -} - -func resourcePagerDutyAutomationActionsActionTeamAssociationCreate(d *schema.ResourceData, meta interface{}) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - actionID := d.Get("action_id").(string) - teamID := d.Get("team_id").(string) - - log.Printf("[INFO] Creating PagerDuty AutomationActionsActionTeamAssociation %s:%s", d.Get("action_id").(string), d.Get("team_id").(string)) - - retryErr := retry.Retry(2*time.Minute, func() *retry.RetryError { - if teamRef, _, err := client.AutomationActionsAction.AssociateToTeam(actionID, teamID); err != nil { - if isErrCode(err, http.StatusBadRequest) { - return retry.NonRetryableError(err) - } - - if isErrCode(err, 429) { - time.Sleep(2 * time.Second) - return retry.RetryableError(err) - } - - return retry.NonRetryableError(err) - } else if teamRef != nil { - d.SetId(fmt.Sprintf("%s:%s", actionID, teamID)) - } - return nil - }) - - if retryErr != nil { - return retryErr - } - - return fetchPagerDutyAutomationActionsActionTeamAssociation(d, meta, handleNotFoundError) -} - -func fetchPagerDutyAutomationActionsActionTeamAssociation(d *schema.ResourceData, meta interface{}, errCallback func(error, *schema.ResourceData) error) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - actionID, teamID, err := resourcePagerDutyParseColonCompoundID(d.Id()) - if err != nil { - return err - } - - return retry.Retry(2*time.Minute, func() *retry.RetryError { - resp, _, err := client.AutomationActionsAction.GetAssociationToTeam(actionID, teamID) - if err != nil { - if isErrCode(err, http.StatusBadRequest) { - return retry.NonRetryableError(err) - } - - errResp := errCallback(err, d) - if errResp != nil { - time.Sleep(2 * time.Second) - return retry.RetryableError(errResp) - } - - return nil - } - - if resp.Team.ID != teamID { - log.Printf("[WARN] Removing %s since the user: %s is not a member of: %s", d.Id(), actionID, teamID) - d.SetId("") - return nil - } - - d.Set("action_id", actionID) - d.Set("team_id", resp.Team.ID) - - return nil - }) -} - -func resourcePagerDutyAutomationActionsActionTeamAssociationRead(d *schema.ResourceData, meta interface{}) error { - return fetchPagerDutyAutomationActionsActionTeamAssociation(d, meta, handleNotFoundError) -} - -func resourcePagerDutyAutomationActionsActionTeamAssociationDelete(d *schema.ResourceData, meta interface{}) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - actionID, teamID, err := resourcePagerDutyParseColonCompoundID(d.Id()) - if err != nil { - return err - } - - log.Printf("[INFO] Deleting PagerDuty AutomationActionsActionTeamAssociation %s", d.Id()) - - retryErr := retry.Retry(2*time.Minute, func() *retry.RetryError { - if _, err := client.AutomationActionsAction.DissociateToTeam(actionID, teamID); err != nil { - if isErrCode(err, http.StatusBadRequest) { - return retry.NonRetryableError(err) - } - - return retry.RetryableError(err) - } - return nil - }) - if retryErr != nil { - time.Sleep(2 * time.Second) - return retryErr - } - d.SetId("") - - // giving the API time to catchup - time.Sleep(time.Second) - return nil -} diff --git a/pagerduty/import_pagerduty_automation_actions_action_team_association_test.go b/pagerdutyplugin/import_pagerduty_automation_actions_action_team_association_test.go similarity index 76% rename from pagerduty/import_pagerduty_automation_actions_action_team_association_test.go rename to pagerdutyplugin/import_pagerduty_automation_actions_action_team_association_test.go index cd6c50fee..4fd7eaae2 100644 --- a/pagerduty/import_pagerduty_automation_actions_action_team_association_test.go +++ b/pagerdutyplugin/import_pagerduty_automation_actions_action_team_association_test.go @@ -13,9 +13,9 @@ func TestAccPagerDutyAutomationActionsActionTeamAssociation_import(t *testing.T) teamName := fmt.Sprintf("tf-%s", acctest.RandString(5)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyAutomationActionsActionTeamAssociationDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyAutomationActionsActionTeamAssociationDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyAutomationActionsActionTeamAssociationConfig(actionName, teamName), diff --git a/pagerdutyplugin/provider.go b/pagerdutyplugin/provider.go index 5a0f7ad88..21dfcabcd 100644 --- a/pagerdutyplugin/provider.go +++ b/pagerdutyplugin/provider.go @@ -71,6 +71,7 @@ func (p *Provider) Resources(_ context.Context) [](func() resource.Resource) { return [](func() resource.Resource){ func() resource.Resource { return &resourceAddon{} }, func() resource.Resource { return &resourceAutomationActionsActionServiceAssociation{} }, + func() resource.Resource { return &resourceAutomationActionsActionTeamAssociation{} }, func() resource.Resource { return &resourceAutomationActionsAction{} }, func() resource.Resource { return &resourceBusinessService{} }, func() resource.Resource { return &resourceExtensionServiceNow{} }, diff --git a/pagerdutyplugin/resource_pagerduty_automation_actions_action_team_association.go b/pagerdutyplugin/resource_pagerduty_automation_actions_action_team_association.go new file mode 100644 index 000000000..298cb1509 --- /dev/null +++ b/pagerdutyplugin/resource_pagerduty_automation_actions_action_team_association.go @@ -0,0 +1,198 @@ +package pagerduty + +import ( + "context" + "errors" + "fmt" + "log" + "time" + + "github.com/PagerDuty/go-pagerduty" + "github.com/PagerDuty/terraform-provider-pagerduty/util" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" +) + +type resourceAutomationActionsActionTeamAssociation struct{ client *pagerduty.Client } + +var ( + _ resource.ResourceWithConfigure = (*resourceAutomationActionsActionTeamAssociation)(nil) + _ resource.ResourceWithImportState = (*resourceAutomationActionsActionTeamAssociation)(nil) +) + +func (r *resourceAutomationActionsActionTeamAssociation) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "pagerduty_automation_actions_action_team_association" +} + +func (r *resourceAutomationActionsActionTeamAssociation) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "action_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "team_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + }, + } +} + +func (r *resourceAutomationActionsActionTeamAssociation) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var actionID, teamID types.String + + resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("action_id"), &actionID)...) + resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("team_id"), &teamID)...) + if resp.Diagnostics.HasError() { + return + } + + log.Printf("[INFO] Creating PagerDuty automation action team association %s:%s", actionID, teamID) + id := fmt.Sprintf("%s:%s", actionID.ValueString(), teamID.ValueString()) + + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + o := pagerduty.AssociateAutomationActionTeamOptions{ + Team: pagerduty.APIReference{ + ID: teamID.ValueString(), + Type: "team_reference", + }, + } + if _, err := r.client.AssociateAutomationActionTeamWithContext(ctx, actionID.ValueString(), o); err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + return nil + }) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error creating PagerDuty automation action team association %s", id), + err.Error(), + ) + return + } + + model, err := requestGetAutomationActionsActionTeamAssociation(ctx, r.client, id, true) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty automation action team association %s", id), + err.Error(), + ) + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) +} + +func (r *resourceAutomationActionsActionTeamAssociation) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var id types.String + + resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &id)...) + if resp.Diagnostics.HasError() { + return + } + log.Printf("[INFO] Reading PagerDuty automation action team association %s", id) + + state, err := requestGetAutomationActionsActionTeamAssociation(ctx, r.client, id.ValueString(), false) + if err != nil { + if errors.Is(err, errAutomationActionTeamNotAssociated) || util.IsNotFoundError(err) { + log.Printf("[WARN] Removing automation action team association %s: %s", id, err.Error()) + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty automation action team association %s", id), + err.Error(), + ) + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *resourceAutomationActionsActionTeamAssociation) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { +} + +func (r *resourceAutomationActionsActionTeamAssociation) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var id types.String + + resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &id)...) + if resp.Diagnostics.HasError() { + return + } + log.Printf("[INFO] Deleting PagerDuty automation action team association %s", id) + + actionID, teamID, err := util.ResourcePagerDutyParseColonCompoundID(id.ValueString()) + if err != nil { + return + } + + err = r.client.DisassociateAutomationActionTeamWithContext(ctx, actionID, teamID) + if err != nil && !util.IsNotFoundError(err) { + resp.Diagnostics.AddError( + fmt.Sprintf("Error deleting PagerDuty automation action team association %s", id), + err.Error(), + ) + return + } + resp.State.RemoveResource(ctx) +} + +func (r *resourceAutomationActionsActionTeamAssociation) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + resp.Diagnostics.Append(ConfigurePagerdutyClient(&r.client, req.ProviderData)...) +} + +func (r *resourceAutomationActionsActionTeamAssociation) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +type resourceAutomationActionsActionTeamAssociationModel struct { + ID types.String `tfsdk:"id"` + ActionID types.String `tfsdk:"action_id"` + TeamID types.String `tfsdk:"team_id"` +} + +var errAutomationActionTeamNotAssociated = errors.New("team is not associated to this action") + +func requestGetAutomationActionsActionTeamAssociation(ctx context.Context, client *pagerduty.Client, id string, retryNotFound bool) (resourceAutomationActionsActionTeamAssociationModel, error) { + var model resourceAutomationActionsActionTeamAssociationModel + + actionID, teamID, err := util.ResourcePagerDutyParseColonCompoundID(id) + if err != nil { + return model, err + } + + err = retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + response, err := client.GetAutomationActionTeamWithContext(ctx, actionID, teamID) + if err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + if !retryNotFound && util.IsNotFoundError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + if response.Team.ID != teamID { + return retry.NonRetryableError(errAutomationActionTeamNotAssociated) + } + model.ID = types.StringValue(id) + model.ActionID = types.StringValue(actionID) + model.TeamID = types.StringValue(teamID) + return nil + }) + + return model, err +} diff --git a/pagerduty/resource_pagerduty_automation_actions_action_team_association_test.go b/pagerdutyplugin/resource_pagerduty_automation_actions_action_team_association_test.go similarity index 79% rename from pagerduty/resource_pagerduty_automation_actions_action_team_association_test.go rename to pagerdutyplugin/resource_pagerduty_automation_actions_action_team_association_test.go index af0cba408..d0fd0f918 100644 --- a/pagerduty/resource_pagerduty_automation_actions_action_team_association_test.go +++ b/pagerdutyplugin/resource_pagerduty_automation_actions_action_team_association_test.go @@ -1,9 +1,11 @@ package pagerduty import ( + "context" "fmt" "testing" + "github.com/PagerDuty/terraform-provider-pagerduty/util" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -25,9 +27,9 @@ func TestAccPagerDutyAutomationActionsActionTeamAssociation_Basic(t *testing.T) teamName := fmt.Sprintf("tf-%s", acctest.RandString(5)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyAutomationActionsActionTeamAssociationDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyAutomationActionsActionTeamAssociationDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyAutomationActionsActionTeamAssociationConfig(actionName, teamName), @@ -42,17 +44,20 @@ func TestAccPagerDutyAutomationActionsActionTeamAssociation_Basic(t *testing.T) } func testAccCheckPagerDutyAutomationActionsActionTeamAssociationDestroy(s *terraform.State) error { - client, _ := testAccProvider.Meta().(*Config).Client() + client := testAccProvider.client + for _, r := range s.RootModule().Resources { if r.Type != "pagerduty_automation_actions_action_team_association" { continue } - actionID, teamID, err := resourcePagerDutyParseColonCompoundID(r.Primary.ID) + + actionID, teamID, err := util.ResourcePagerDutyParseColonCompoundID(r.Primary.ID) if err != nil { return err } - if _, _, err := client.AutomationActionsAction.GetAssociationToTeam(actionID, teamID); err == nil { + ctx := context.Background() + if _, err := client.GetAutomationActionTeamWithContext(ctx, actionID, teamID); err == nil { return fmt.Errorf("Automation Actions Runner still exists") } } @@ -69,13 +74,14 @@ func testAccCheckPagerDutyAutomationActionsActionTeamAssociationExists(n string) return fmt.Errorf("No Automation Actions Runner ID is set") } - client, _ := testAccProvider.Meta().(*Config).Client() - actionID, teamID, err := resourcePagerDutyParseColonCompoundID(rs.Primary.ID) + actionID, teamID, err := util.ResourcePagerDutyParseColonCompoundID(rs.Primary.ID) if err != nil { return err } - found, _, err := client.AutomationActionsAction.GetAssociationToTeam(actionID, teamID) + client := testAccProvider.client + ctx := context.Background() + found, err := client.GetAutomationActionTeamWithContext(ctx, actionID, teamID) if err != nil { return err } From 2bd7bd57453e2c309aec7a170c15d60f935cf50a Mon Sep 17 00:00:00 2001 From: Carlos Gajardo Date: Tue, 9 Jul 2024 09:57:55 -0400 Subject: [PATCH 5/8] Migrate automation actions runner team association --- pagerduty/provider.go | 1 - ...omation_actions_runner_team_association.go | 144 ------------- ...on_actions_runner_team_association_test.go | 6 +- pagerdutyplugin/provider.go | 1 + ...omation_actions_runner_team_association.go | 204 ++++++++++++++++++ ...on_actions_runner_team_association_test.go | 22 +- 6 files changed, 221 insertions(+), 157 deletions(-) delete mode 100644 pagerduty/resource_pagerduty_automation_actions_runner_team_association.go rename {pagerduty => pagerdutyplugin}/import_pagerduty_automation_actions_runner_team_association_test.go (76%) create mode 100644 pagerdutyplugin/resource_pagerduty_automation_actions_runner_team_association.go rename {pagerduty => pagerdutyplugin}/resource_pagerduty_automation_actions_runner_team_association_test.go (79%) diff --git a/pagerduty/provider.go b/pagerduty/provider.go index 6eaee4510..490a33655 100644 --- a/pagerduty/provider.go +++ b/pagerduty/provider.go @@ -140,7 +140,6 @@ func Provider(isMux bool) *schema.Provider { "pagerduty_event_orchestration_global_cache_variable": resourcePagerDutyEventOrchestrationGlobalCacheVariable(), "pagerduty_event_orchestration_service_cache_variable": resourcePagerDutyEventOrchestrationServiceCacheVariable(), "pagerduty_automation_actions_runner": resourcePagerDutyAutomationActionsRunner(), - "pagerduty_automation_actions_runner_team_association": resourcePagerDutyAutomationActionsRunnerTeamAssociation(), "pagerduty_incident_workflow": resourcePagerDutyIncidentWorkflow(), "pagerduty_incident_workflow_trigger": resourcePagerDutyIncidentWorkflowTrigger(), "pagerduty_incident_custom_field": resourcePagerDutyIncidentCustomField(), diff --git a/pagerduty/resource_pagerduty_automation_actions_runner_team_association.go b/pagerduty/resource_pagerduty_automation_actions_runner_team_association.go deleted file mode 100644 index eecf1ba8b..000000000 --- a/pagerduty/resource_pagerduty_automation_actions_runner_team_association.go +++ /dev/null @@ -1,144 +0,0 @@ -package pagerduty - -import ( - "fmt" - "log" - "net/http" - "time" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func resourcePagerDutyAutomationActionsRunnerTeamAssociation() *schema.Resource { - return &schema.Resource{ - Create: resourcePagerDutyAutomationActionsRunnerTeamAssociationCreate, - Read: resourcePagerDutyAutomationActionsRunnerTeamAssociationRead, - Delete: resourcePagerDutyAutomationActionsRunnerTeamAssociationDelete, - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - Schema: map[string]*schema.Schema{ - "runner_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "team_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - }, - } -} - -func resourcePagerDutyAutomationActionsRunnerTeamAssociationCreate(d *schema.ResourceData, meta interface{}) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - runnerID := d.Get("runner_id").(string) - teamID := d.Get("team_id").(string) - - log.Printf("[INFO] Creating PagerDuty AutomationActionsRunnerTeamAssociation %s:%s", d.Get("runner_id").(string), d.Get("team_id").(string)) - - retryErr := retry.Retry(2*time.Minute, func() *retry.RetryError { - if teamRef, _, err := client.AutomationActionsRunner.AssociateToTeam(runnerID, teamID); err != nil { - if isErrCode(err, 429) { - time.Sleep(2 * time.Second) - return retry.RetryableError(err) - } - - return retry.NonRetryableError(err) - } else if teamRef != nil { - d.SetId(fmt.Sprintf("%s:%s", runnerID, teamID)) - } - return nil - }) - - if retryErr != nil { - return retryErr - } - - return fetchPagerDutyAutomationActionsRunnerTeamAssociation(d, meta, handleNotFoundError) -} - -func fetchPagerDutyAutomationActionsRunnerTeamAssociation(d *schema.ResourceData, meta interface{}, errCallback func(error, *schema.ResourceData) error) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - runnerID, teamID, err := resourcePagerDutyParseColonCompoundID(d.Id()) - if err != nil { - return err - } - - return retry.Retry(2*time.Minute, func() *retry.RetryError { - resp, _, err := client.AutomationActionsRunner.GetAssociationToTeam(runnerID, teamID) - if err != nil { - if isErrCode(err, http.StatusBadRequest) { - return retry.NonRetryableError(err) - } - - errResp := errCallback(err, d) - if errResp != nil { - time.Sleep(2 * time.Second) - return retry.RetryableError(errResp) - } - - return nil - } - - if resp.Team.ID != teamID { - log.Printf("[WARN] Removing %s since the user: %s is not a member of: %s", d.Id(), runnerID, teamID) - d.SetId("") - return nil - } - - d.Set("runner_id", runnerID) - d.Set("team_id", resp.Team.ID) - - return nil - }) -} - -func resourcePagerDutyAutomationActionsRunnerTeamAssociationRead(d *schema.ResourceData, meta interface{}) error { - return fetchPagerDutyAutomationActionsRunnerTeamAssociation(d, meta, handleNotFoundError) -} - -func resourcePagerDutyAutomationActionsRunnerTeamAssociationDelete(d *schema.ResourceData, meta interface{}) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - runnerID, teamID, err := resourcePagerDutyParseColonCompoundID(d.Id()) - if err != nil { - return err - } - - log.Printf("[INFO] Deleting PagerDuty AutomationActionsRunnerTeamAssociation %s", d.Id()) - - retryErr := retry.Retry(2*time.Minute, func() *retry.RetryError { - if _, err := client.AutomationActionsRunner.DissociateFromTeam(runnerID, teamID); err != nil { - if isErrCode(err, http.StatusBadRequest) { - return retry.NonRetryableError(err) - } - - return retry.RetryableError(err) - } - return nil - }) - if retryErr != nil { - time.Sleep(2 * time.Second) - return retryErr - } - d.SetId("") - - // giving the API time to catchup - time.Sleep(time.Second) - return nil -} diff --git a/pagerduty/import_pagerduty_automation_actions_runner_team_association_test.go b/pagerdutyplugin/import_pagerduty_automation_actions_runner_team_association_test.go similarity index 76% rename from pagerduty/import_pagerduty_automation_actions_runner_team_association_test.go rename to pagerdutyplugin/import_pagerduty_automation_actions_runner_team_association_test.go index 4fcbc95ab..6c3782d95 100644 --- a/pagerduty/import_pagerduty_automation_actions_runner_team_association_test.go +++ b/pagerdutyplugin/import_pagerduty_automation_actions_runner_team_association_test.go @@ -13,9 +13,9 @@ func TestAccPagerDutyAutomationActionsRunnerTeamAssociation_import(t *testing.T) teamName := fmt.Sprintf("tf-%s", acctest.RandString(5)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyAutomationActionsRunnerTeamAssociationDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyAutomationActionsRunnerTeamAssociationDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyAutomationActionsRunnerTeamAssociationConfig(runnerName, teamName), diff --git a/pagerdutyplugin/provider.go b/pagerdutyplugin/provider.go index 21dfcabcd..e3d8eae60 100644 --- a/pagerdutyplugin/provider.go +++ b/pagerdutyplugin/provider.go @@ -73,6 +73,7 @@ func (p *Provider) Resources(_ context.Context) [](func() resource.Resource) { func() resource.Resource { return &resourceAutomationActionsActionServiceAssociation{} }, func() resource.Resource { return &resourceAutomationActionsActionTeamAssociation{} }, func() resource.Resource { return &resourceAutomationActionsAction{} }, + func() resource.Resource { return &resourceAutomationActionsRunnerTeamAssociation{} }, func() resource.Resource { return &resourceBusinessService{} }, func() resource.Resource { return &resourceExtensionServiceNow{} }, func() resource.Resource { return &resourceExtension{} }, diff --git a/pagerdutyplugin/resource_pagerduty_automation_actions_runner_team_association.go b/pagerdutyplugin/resource_pagerduty_automation_actions_runner_team_association.go new file mode 100644 index 000000000..9357355f1 --- /dev/null +++ b/pagerdutyplugin/resource_pagerduty_automation_actions_runner_team_association.go @@ -0,0 +1,204 @@ +package pagerduty + +import ( + "context" + "errors" + "fmt" + "log" + "time" + + "github.com/PagerDuty/go-pagerduty" + "github.com/PagerDuty/terraform-provider-pagerduty/util" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" +) + +type resourceAutomationActionsRunnerTeamAssociation struct{ client *pagerduty.Client } + +var ( + _ resource.ResourceWithConfigure = (*resourceAutomationActionsRunnerTeamAssociation)(nil) + _ resource.ResourceWithImportState = (*resourceAutomationActionsRunnerTeamAssociation)(nil) +) + +func (r *resourceAutomationActionsRunnerTeamAssociation) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "pagerduty_automation_actions_runner_team_association" +} + +func (r *resourceAutomationActionsRunnerTeamAssociation) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "runner_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "team_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + }, + } +} + +func (r *resourceAutomationActionsRunnerTeamAssociation) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var runnerID, teamID types.String + + resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("runner_id"), &runnerID)...) + resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("team_id"), &teamID)...) + if resp.Diagnostics.HasError() { + return + } + + log.Printf("[INFO] Creating PagerDuty automation runner team association %s:%s", runnerID, teamID) + id := fmt.Sprintf("%s:%s", runnerID.ValueString(), teamID.ValueString()) + + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + o := pagerduty.AssociateAutomationActionsRunnerTeamOptions{ + Team: pagerduty.APIReference{ + ID: teamID.ValueString(), + Type: "team_reference", + }, + } + if _, err := r.client.AssociateAutomationActionsRunnerTeamWithContext(ctx, runnerID.ValueString(), o); err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + return nil + }) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error creating PagerDuty automation runner team association %s", id), + err.Error(), + ) + return + } + + model, err := requestGetAutomationActionsRunnerTeamAssociation(ctx, r.client, id, true) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty automation runner team association %s", id), + err.Error(), + ) + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) +} + +func (r *resourceAutomationActionsRunnerTeamAssociation) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var id types.String + + resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &id)...) + if resp.Diagnostics.HasError() { + return + } + log.Printf("[INFO] Reading PagerDuty automation runner team association %s", id) + + state, err := requestGetAutomationActionsRunnerTeamAssociation(ctx, r.client, id.ValueString(), false) + if err != nil { + if errors.Is(err, errAutomationActionsRunnerTeamNotAssociated) || util.IsNotFoundError(err) { + log.Printf("[WARN] Removing automation runner team association %s: %s", id, err.Error()) + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty automation runner team association %s", id), + err.Error(), + ) + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *resourceAutomationActionsRunnerTeamAssociation) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { +} + +func (r *resourceAutomationActionsRunnerTeamAssociation) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var id types.String + + resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &id)...) + if resp.Diagnostics.HasError() { + return + } + log.Printf("[INFO] Deleting PagerDuty automation runner team association %s", id) + + runnerID, teamID, err := util.ResourcePagerDutyParseColonCompoundID(id.ValueString()) + if err != nil { + return + } + + err = retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + err := r.client.DisassociateAutomationActionsRunnerTeamWithContext(ctx, runnerID, teamID) + if err != nil { + return retry.RetryableError(err) + } + return nil + }) + if err != nil && !util.IsNotFoundError(err) { + resp.Diagnostics.AddError( + fmt.Sprintf("Error deleting PagerDuty automation runner team association %s", id), + err.Error(), + ) + return + } + resp.State.RemoveResource(ctx) +} + +func (r *resourceAutomationActionsRunnerTeamAssociation) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + resp.Diagnostics.Append(ConfigurePagerdutyClient(&r.client, req.ProviderData)...) +} + +func (r *resourceAutomationActionsRunnerTeamAssociation) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +type resourceAutomationActionsRunnerTeamAssociationModel struct { + ID types.String `tfsdk:"id"` + RunnerID types.String `tfsdk:"runner_id"` + TeamID types.String `tfsdk:"team_id"` +} + +var errAutomationActionsRunnerTeamNotAssociated = errors.New("team is not associated to this runner") + +func requestGetAutomationActionsRunnerTeamAssociation(ctx context.Context, client *pagerduty.Client, id string, retryNotFound bool) (resourceAutomationActionsRunnerTeamAssociationModel, error) { + var model resourceAutomationActionsRunnerTeamAssociationModel + + runnerID, teamID, err := util.ResourcePagerDutyParseColonCompoundID(id) + if err != nil { + return model, err + } + + err = retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + response, err := client.GetAutomationActionsRunnerTeamWithContext(ctx, runnerID, teamID) + if err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + if !retryNotFound && util.IsNotFoundError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + if response.Team.ID != teamID { + return retry.NonRetryableError(errAutomationActionsRunnerTeamNotAssociated) + } + model.ID = types.StringValue(id) + model.RunnerID = types.StringValue(runnerID) + model.TeamID = types.StringValue(teamID) + return nil + }) + + return model, err +} diff --git a/pagerduty/resource_pagerduty_automation_actions_runner_team_association_test.go b/pagerdutyplugin/resource_pagerduty_automation_actions_runner_team_association_test.go similarity index 79% rename from pagerduty/resource_pagerduty_automation_actions_runner_team_association_test.go rename to pagerdutyplugin/resource_pagerduty_automation_actions_runner_team_association_test.go index 1c58f95ef..59c371217 100644 --- a/pagerduty/resource_pagerduty_automation_actions_runner_team_association_test.go +++ b/pagerdutyplugin/resource_pagerduty_automation_actions_runner_team_association_test.go @@ -1,9 +1,11 @@ package pagerduty import ( + "context" "fmt" "testing" + "github.com/PagerDuty/terraform-provider-pagerduty/util" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -25,9 +27,9 @@ func TestAccPagerDutyAutomationActionsRunnerTeamAssociation_Basic(t *testing.T) teamName := fmt.Sprintf("tf-%s", acctest.RandString(5)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyAutomationActionsRunnerTeamAssociationDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyAutomationActionsRunnerTeamAssociationDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyAutomationActionsRunnerTeamAssociationConfig(runnerName, teamName), @@ -42,17 +44,18 @@ func TestAccPagerDutyAutomationActionsRunnerTeamAssociation_Basic(t *testing.T) } func testAccCheckPagerDutyAutomationActionsRunnerTeamAssociationDestroy(s *terraform.State) error { - client, _ := testAccProvider.Meta().(*Config).Client() for _, r := range s.RootModule().Resources { if r.Type != "pagerduty_automation_actions_runner_team_association" { continue } - runnerID, teamID, err := resourcePagerDutyParseColonCompoundID(r.Primary.ID) + runnerID, teamID, err := util.ResourcePagerDutyParseColonCompoundID(r.Primary.ID) if err != nil { return err } - if _, _, err := client.AutomationActionsRunner.GetAssociationToTeam(runnerID, teamID); err == nil { + client := testAccProvider.client + ctx := context.Background() + if _, err := client.GetAutomationActionsRunnerTeamWithContext(ctx, runnerID, teamID); err == nil { return fmt.Errorf("Automation Actions Runner Team association still exists") } } @@ -69,13 +72,14 @@ func testAccCheckPagerDutyAutomationActionsRunnerTeamAssociationExists(n string) return fmt.Errorf("No Automation Actions Runner ID is set") } - client, _ := testAccProvider.Meta().(*Config).Client() - runnerID, teamID, err := resourcePagerDutyParseColonCompoundID(rs.Primary.ID) + runnerID, teamID, err := util.ResourcePagerDutyParseColonCompoundID(rs.Primary.ID) if err != nil { return err } - found, _, err := client.AutomationActionsRunner.GetAssociationToTeam(runnerID, teamID) + client := testAccProvider.client + ctx := context.Background() + found, err := client.GetAutomationActionsRunnerTeamWithContext(ctx, runnerID, teamID) if err != nil { return err } From d710b92578ee8ba87645fffa1272f32a14859fb4 Mon Sep 17 00:00:00 2001 From: Carlos Gajardo Date: Tue, 9 Jul 2024 15:48:29 -0400 Subject: [PATCH 6/8] Migrate data source automation actions runnner --- ...rce_pagerduty_automation_actions_runner.go | 97 ---------------- pagerduty/provider.go | 1 - ...rce_pagerduty_automation_actions_runner.go | 104 ++++++++++++++++++ ...agerduty_automation_actions_runner_test.go | 4 +- pagerdutyplugin/provider.go | 3 +- 5 files changed, 108 insertions(+), 101 deletions(-) delete mode 100644 pagerduty/data_source_pagerduty_automation_actions_runner.go create mode 100644 pagerdutyplugin/data_source_pagerduty_automation_actions_runner.go rename {pagerduty => pagerdutyplugin}/data_source_pagerduty_automation_actions_runner_test.go (93%) diff --git a/pagerduty/data_source_pagerduty_automation_actions_runner.go b/pagerduty/data_source_pagerduty_automation_actions_runner.go deleted file mode 100644 index b17d7b66c..000000000 --- a/pagerduty/data_source_pagerduty_automation_actions_runner.go +++ /dev/null @@ -1,97 +0,0 @@ -package pagerduty - -import ( - "log" - "net/http" - "time" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSourcePagerDutyAutomationActionsRunner() *schema.Resource { - return &schema.Resource{ - Read: dataSourcePagerDutyAutomationActionsRunnerRead, - - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Required: true, - }, - "name": { - Type: schema.TypeString, - Computed: true, - }, - "type": { - Type: schema.TypeString, - Computed: true, - }, - "runner_type": { - Type: schema.TypeString, - Computed: true, - }, - "creation_time": { - Type: schema.TypeString, - Computed: true, - }, - "last_seen": { - Type: schema.TypeString, - Computed: true, - Optional: true, - }, - "description": { - Type: schema.TypeString, - Computed: true, - Optional: true, - }, - "runbook_base_uri": { - Type: schema.TypeString, - Computed: true, - Optional: true, - }, - }, - } -} - -func dataSourcePagerDutyAutomationActionsRunnerRead(d *schema.ResourceData, meta interface{}) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - log.Printf("[INFO] Reading PagerDuty automation actions runner") - - return retry.Retry(2*time.Minute, func() *retry.RetryError { - runner, _, err := client.AutomationActionsRunner.Get(d.Get("id").(string)) - if err != nil { - if isErrCode(err, http.StatusBadRequest) { - return retry.NonRetryableError(err) - } - - // Delaying retry by 30s as recommended by PagerDuty - // https://developer.pagerduty.com/docs/rest-api-v2/rate-limiting/#what-are-possible-workarounds-to-the-events-api-rate-limit - time.Sleep(30 * time.Second) - return retry.RetryableError(err) - } - - d.SetId(runner.ID) - d.Set("name", runner.Name) - d.Set("type", runner.Type) - d.Set("runner_type", runner.RunnerType) - d.Set("creation_time", runner.CreationTime) - - if runner.Description != nil { - d.Set("description", &runner.Description) - } - - if runner.RunbookBaseUri != nil { - d.Set("runbook_base_uri", &runner.RunbookBaseUri) - } - - if runner.LastSeenTime != nil { - d.Set("last_seen", &runner.LastSeenTime) - } - - return nil - }) -} diff --git a/pagerduty/provider.go b/pagerduty/provider.go index 490a33655..1c3698fea 100644 --- a/pagerduty/provider.go +++ b/pagerduty/provider.go @@ -104,7 +104,6 @@ func Provider(isMux bool) *schema.Provider { "pagerduty_event_orchestration_integration": dataSourcePagerDutyEventOrchestrationIntegration(), "pagerduty_event_orchestration_global_cache_variable": dataSourcePagerDutyEventOrchestrationGlobalCacheVariable(), "pagerduty_event_orchestration_service_cache_variable": dataSourcePagerDutyEventOrchestrationServiceCacheVariable(), - "pagerduty_automation_actions_runner": dataSourcePagerDutyAutomationActionsRunner(), "pagerduty_incident_workflow": dataSourcePagerDutyIncidentWorkflow(), "pagerduty_incident_custom_field": dataSourcePagerDutyIncidentCustomField(), "pagerduty_team_members": dataSourcePagerDutyTeamMembers(), diff --git a/pagerdutyplugin/data_source_pagerduty_automation_actions_runner.go b/pagerdutyplugin/data_source_pagerduty_automation_actions_runner.go new file mode 100644 index 000000000..157c17b63 --- /dev/null +++ b/pagerdutyplugin/data_source_pagerduty_automation_actions_runner.go @@ -0,0 +1,104 @@ +package pagerduty + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/PagerDuty/go-pagerduty" + "github.com/PagerDuty/terraform-provider-pagerduty/util" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" +) + +type dataSourceAutomationActionsRunner struct{ client *pagerduty.Client } + +var _ datasource.DataSourceWithConfigure = (*dataSourceAutomationActionsRunner)(nil) + +func (*dataSourceAutomationActionsRunner) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = "pagerduty_automation_actions_runner" +} + +func (*dataSourceAutomationActionsRunner) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{Required: true}, + "name": schema.StringAttribute{Computed: true}, + "type": schema.StringAttribute{Computed: true}, + "runner_type": schema.StringAttribute{Computed: true}, + "creation_time": schema.StringAttribute{Computed: true}, + "last_seen": schema.StringAttribute{Computed: true, Optional: true}, + "description": schema.StringAttribute{Computed: true, Optional: true}, + "runbook_base_uri": schema.StringAttribute{Computed: true, Optional: true}, + }, + } +} + +func (d *dataSourceAutomationActionsRunner) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + resp.Diagnostics.Append(ConfigurePagerdutyClient(&d.client, req.ProviderData)...) +} + +func (d *dataSourceAutomationActionsRunner) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + log.Println("[INFO] Reading PagerDuty automation actions runner") + + var searchID types.String + resp.Diagnostics.Append(req.Config.GetAttribute(ctx, path.Root("id"), &searchID)...) + if resp.Diagnostics.HasError() { + return + } + + var model dataSourceAutomationActionsRunnerModel + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + runner, err := d.client.GetAutomationActionsRunnerWithContext(ctx, searchID.ValueString()) + if err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + + model = dataSourceAutomationActionsRunnerModel{ + ID: types.StringValue(runner.ID), + Name: types.StringValue(runner.Name), + Type: types.StringValue(runner.Type), + RunnerType: types.StringValue(runner.RunnerType), + CreationTime: types.StringValue(runner.CreationTime), + } + + if runner.Description != "" { + model.Description = types.StringValue(runner.Description) + } + + if runner.RunbookBaseURI != "" { + model.RunbookBaseURI = types.StringValue(runner.RunbookBaseURI) + } + + if runner.LastSeen != "" { + model.LastSeen = types.StringValue(runner.LastSeen) + } + return nil + }) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty automation actions runner %s", searchID), + err.Error(), + ) + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) +} + +type dataSourceAutomationActionsRunnerModel struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Type types.String `tfsdk:"type"` + RunnerType types.String `tfsdk:"runner_type"` + CreationTime types.String `tfsdk:"creation_time"` + LastSeen types.String `tfsdk:"last_seen"` + Description types.String `tfsdk:"description"` + RunbookBaseURI types.String `tfsdk:"runbook_base_uri"` +} diff --git a/pagerduty/data_source_pagerduty_automation_actions_runner_test.go b/pagerdutyplugin/data_source_pagerduty_automation_actions_runner_test.go similarity index 93% rename from pagerduty/data_source_pagerduty_automation_actions_runner_test.go rename to pagerdutyplugin/data_source_pagerduty_automation_actions_runner_test.go index 9c245ec28..ed1782693 100644 --- a/pagerduty/data_source_pagerduty_automation_actions_runner_test.go +++ b/pagerdutyplugin/data_source_pagerduty_automation_actions_runner_test.go @@ -13,8 +13,8 @@ func TestAccDataSourcePagerDutyAutomationActionsRunner_Basic(t *testing.T) { name := fmt.Sprintf("tf-%s", acctest.RandString(5)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), Steps: []resource.TestStep{ { Config: testAccDataSourcePagerDutyAutomationActionsRunnerConfig(name), diff --git a/pagerdutyplugin/provider.go b/pagerdutyplugin/provider.go index e3d8eae60..148b68b4f 100644 --- a/pagerdutyplugin/provider.go +++ b/pagerdutyplugin/provider.go @@ -53,11 +53,12 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro func (p *Provider) DataSources(_ context.Context) [](func() datasource.DataSource) { return [](func() datasource.DataSource){ func() datasource.DataSource { return &dataSourceAutomationActionsAction{} }, + func() datasource.DataSource { return &dataSourceAutomationActionsRunner{} }, func() datasource.DataSource { return &dataSourceBusinessService{} }, func() datasource.DataSource { return &dataSourceExtensionSchema{} }, func() datasource.DataSource { return &dataSourceIntegration{} }, - func() datasource.DataSource { return &dataSourceLicense{} }, func() datasource.DataSource { return &dataSourceLicenses{} }, + func() datasource.DataSource { return &dataSourceLicense{} }, func() datasource.DataSource { return &dataSourcePriority{} }, func() datasource.DataSource { return &dataSourceService{} }, func() datasource.DataSource { return &dataSourceStandardsResourceScores{} }, From edfb8c9ef87329e68bf905ccd48c385f4cad2fa6 Mon Sep 17 00:00:00 2001 From: Carlos Gajardo Date: Thu, 11 Jul 2024 15:42:02 -0400 Subject: [PATCH 7/8] Migrate resource automation actions runner --- pagerduty/provider.go | 1 - ...rce_pagerduty_automation_actions_runner.go | 221 ------------- ...agerduty_automation_actions_runner_test.go | 6 +- pagerdutyplugin/provider.go | 1 + ...rce_pagerduty_automation_actions_runner.go | 313 ++++++++++++++++++ ...agerduty_automation_actions_runner_test.go | 19 +- 6 files changed, 329 insertions(+), 232 deletions(-) delete mode 100644 pagerduty/resource_pagerduty_automation_actions_runner.go rename {pagerduty => pagerdutyplugin}/import_pagerduty_automation_actions_runner_test.go (77%) create mode 100644 pagerdutyplugin/resource_pagerduty_automation_actions_runner.go rename {pagerduty => pagerdutyplugin}/resource_pagerduty_automation_actions_runner_test.go (89%) diff --git a/pagerduty/provider.go b/pagerduty/provider.go index 1c3698fea..112e6b6ea 100644 --- a/pagerduty/provider.go +++ b/pagerduty/provider.go @@ -138,7 +138,6 @@ func Provider(isMux bool) *schema.Provider { "pagerduty_event_orchestration_service": resourcePagerDutyEventOrchestrationPathService(), "pagerduty_event_orchestration_global_cache_variable": resourcePagerDutyEventOrchestrationGlobalCacheVariable(), "pagerduty_event_orchestration_service_cache_variable": resourcePagerDutyEventOrchestrationServiceCacheVariable(), - "pagerduty_automation_actions_runner": resourcePagerDutyAutomationActionsRunner(), "pagerduty_incident_workflow": resourcePagerDutyIncidentWorkflow(), "pagerduty_incident_workflow_trigger": resourcePagerDutyIncidentWorkflowTrigger(), "pagerduty_incident_custom_field": resourcePagerDutyIncidentCustomField(), diff --git a/pagerduty/resource_pagerduty_automation_actions_runner.go b/pagerduty/resource_pagerduty_automation_actions_runner.go deleted file mode 100644 index f8e92aaa0..000000000 --- a/pagerduty/resource_pagerduty_automation_actions_runner.go +++ /dev/null @@ -1,221 +0,0 @@ -package pagerduty - -import ( - "errors" - "log" - "net/http" - "time" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/heimweh/go-pagerduty/pagerduty" -) - -func resourcePagerDutyAutomationActionsRunner() *schema.Resource { - return &schema.Resource{ - Create: resourcePagerDutyAutomationActionsRunnerCreate, - Read: resourcePagerDutyAutomationActionsRunnerRead, - Update: resourcePagerDutyAutomationActionsRunnerUpdate, - Delete: resourcePagerDutyAutomationActionsRunnerDelete, - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - }, - "runner_type": { - Type: schema.TypeString, - Required: true, - ValidateDiagFunc: validateValueDiagFunc([]string{ - "sidecar", - "runbook", - }), - ForceNew: true, // Requires creation of new resource while support for update is not implemented - }, - "description": { - Type: schema.TypeString, - Optional: true, - }, - "runbook_base_uri": { - Type: schema.TypeString, - Optional: true, - }, - "runbook_api_key": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - }, - "type": { - Type: schema.TypeString, - Computed: true, - }, - "creation_time": { - Type: schema.TypeString, - Computed: true, - }, - "last_seen": { - Type: schema.TypeString, - Computed: true, - Optional: true, - }, - }, - } -} - -func buildAutomationActionsRunnerStruct(d *schema.ResourceData) (*pagerduty.AutomationActionsRunner, error) { - automationActionsRunner := pagerduty.AutomationActionsRunner{ - Name: d.Get("name").(string), - RunnerType: d.Get("runner_type").(string), - } - - if automationActionsRunner.RunnerType != "runbook" { - return nil, errors.New("only runners of runner_type runbook can be created") - } - - // The API does not allow new runners without a description, but legacy runners without a description exist - if attr, ok := d.GetOk("description"); ok { - val := attr.(string) - automationActionsRunner.Description = &val - } else { - return nil, errors.New("runner description must be specified when creating a runbook runner") - } - - if attr, ok := d.GetOk("runbook_base_uri"); ok { - val := attr.(string) - automationActionsRunner.RunbookBaseUri = &val - } else { - return nil, errors.New("runbook_base_uri must be specified when creating a runbook runner") - } - - if attr, ok := d.GetOk("runbook_api_key"); ok { - val := attr.(string) - automationActionsRunner.RunbookApiKey = &val - } else { - return nil, errors.New("runbook_api_key must be specified when creating a runbook runner") - } - - return &automationActionsRunner, nil -} - -func resourcePagerDutyAutomationActionsRunnerCreate(d *schema.ResourceData, meta interface{}) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - automationActionsRunner, err := buildAutomationActionsRunnerStruct(d) - if err != nil { - return err - } - - log.Printf("[INFO] Creating PagerDuty AutomationActionsRunner %s", automationActionsRunner.Name) - - retryErr := retry.Retry(2*time.Minute, func() *retry.RetryError { - if automationActionsRunner, _, err := client.AutomationActionsRunner.Create(automationActionsRunner); err != nil { - if isErrCode(err, 400) || isErrCode(err, 429) { - time.Sleep(2 * time.Second) - return retry.RetryableError(err) - } - - return retry.NonRetryableError(err) - } else if automationActionsRunner != nil { - d.SetId(automationActionsRunner.ID) - } - return nil - }) - - if retryErr != nil { - return retryErr - } - - return resourcePagerDutyAutomationActionsRunnerRead(d, meta) -} - -func resourcePagerDutyAutomationActionsRunnerRead(d *schema.ResourceData, meta interface{}) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - log.Printf("[INFO] Reading PagerDuty AutomationActionsRunner %s", d.Id()) - - return retry.Retry(2*time.Minute, func() *retry.RetryError { - if automationActionsRunner, _, err := client.AutomationActionsRunner.Get(d.Id()); err != nil { - if isErrCode(err, http.StatusBadRequest) { - return retry.NonRetryableError(err) - } - - time.Sleep(2 * time.Second) - return retry.RetryableError(err) - } else if automationActionsRunner != nil { - d.Set("name", automationActionsRunner.Name) - d.Set("type", automationActionsRunner.Type) - d.Set("runner_type", automationActionsRunner.RunnerType) - d.Set("creation_time", automationActionsRunner.CreationTime) - - if automationActionsRunner.Description != nil { - d.Set("description", &automationActionsRunner.Description) - } - - if automationActionsRunner.RunbookBaseUri != nil { - d.Set("runbook_base_uri", &automationActionsRunner.RunbookBaseUri) - } - - if automationActionsRunner.LastSeenTime != nil { - d.Set("last_seen", &automationActionsRunner.LastSeenTime) - } - } - return nil - }) -} - -func resourcePagerDutyAutomationActionsRunnerUpdate(d *schema.ResourceData, meta interface{}) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - automationActionsRunner, err := buildAutomationActionsRunnerStruct(d) - if err != nil { - return err - } - - log.Printf("[INFO] Updating PagerDuty AutomationActionsRunner %s", d.Id()) - - if _, _, err := client.AutomationActionsRunner.Update(d.Id(), automationActionsRunner); err != nil { - return err - } - - return nil -} - -func resourcePagerDutyAutomationActionsRunnerDelete(d *schema.ResourceData, meta interface{}) error { - client, err := meta.(*Config).Client() - if err != nil { - return err - } - - log.Printf("[INFO] Deleting PagerDuty AutomationActionsRunner %s", d.Id()) - - retryErr := retry.Retry(2*time.Minute, func() *retry.RetryError { - if _, err := client.AutomationActionsRunner.Delete(d.Id()); err != nil { - if isErrCode(err, http.StatusBadRequest) { - return retry.NonRetryableError(err) - } - - return retry.RetryableError(err) - } - return nil - }) - if retryErr != nil { - time.Sleep(2 * time.Second) - return retryErr - } - d.SetId("") - - // giving the API time to catchup - time.Sleep(time.Second) - return nil -} diff --git a/pagerduty/import_pagerduty_automation_actions_runner_test.go b/pagerdutyplugin/import_pagerduty_automation_actions_runner_test.go similarity index 77% rename from pagerduty/import_pagerduty_automation_actions_runner_test.go rename to pagerdutyplugin/import_pagerduty_automation_actions_runner_test.go index eddf324e3..bc70b786d 100644 --- a/pagerduty/import_pagerduty_automation_actions_runner_test.go +++ b/pagerdutyplugin/import_pagerduty_automation_actions_runner_test.go @@ -12,9 +12,9 @@ func TestAccPagerDutyAutomationActionsRunner_import(t *testing.T) { runnerName := fmt.Sprintf("tf-%s", acctest.RandString(5)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyAutomationActionsRunnerDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyAutomationActionsRunnerDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyAutomationActionsRunnerConfig(runnerName), diff --git a/pagerdutyplugin/provider.go b/pagerdutyplugin/provider.go index 148b68b4f..1ef749f88 100644 --- a/pagerdutyplugin/provider.go +++ b/pagerdutyplugin/provider.go @@ -75,6 +75,7 @@ func (p *Provider) Resources(_ context.Context) [](func() resource.Resource) { func() resource.Resource { return &resourceAutomationActionsActionTeamAssociation{} }, func() resource.Resource { return &resourceAutomationActionsAction{} }, func() resource.Resource { return &resourceAutomationActionsRunnerTeamAssociation{} }, + func() resource.Resource { return &resourceAutomationActionsRunner{} }, func() resource.Resource { return &resourceBusinessService{} }, func() resource.Resource { return &resourceExtensionServiceNow{} }, func() resource.Resource { return &resourceExtension{} }, diff --git a/pagerdutyplugin/resource_pagerduty_automation_actions_runner.go b/pagerdutyplugin/resource_pagerduty_automation_actions_runner.go new file mode 100644 index 000000000..6bcd2ce98 --- /dev/null +++ b/pagerdutyplugin/resource_pagerduty_automation_actions_runner.go @@ -0,0 +1,313 @@ +package pagerduty + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/PagerDuty/go-pagerduty" + "github.com/PagerDuty/terraform-provider-pagerduty/util" + "github.com/PagerDuty/terraform-provider-pagerduty/util/validate" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" +) + +type resourceAutomationActionsRunner struct{ client *pagerduty.Client } + +var ( + _ resource.ResourceWithConfigure = (*resourceAutomationActionsRunner)(nil) + _ resource.ResourceWithImportState = (*resourceAutomationActionsRunner)(nil) + _ resource.ResourceWithValidateConfig = (*resourceAutomationActionsRunner)(nil) +) + +func (r *resourceAutomationActionsRunner) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "pagerduty_automation_actions_runner" +} + +func (r *resourceAutomationActionsRunner) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "name": schema.StringAttribute{Required: true}, + "runner_type": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.OneOf("sidecar", "runbook"), + }, + PlanModifiers: []planmodifier.String{ + // Requires creation of new resource while support for update is not implemented + stringplanmodifier.RequiresReplace(), + }, + }, + "description": schema.StringAttribute{Optional: true}, + "runbook_base_uri": schema.StringAttribute{Optional: true}, + "runbook_api_key": schema.StringAttribute{Optional: true, Sensitive: true}, + "last_seen": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "type": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "creation_time": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + }, + } +} + +func (r *resourceAutomationActionsRunner) ConfigValidators(ctx context.Context) []resource.ConfigValidator { + runnerTypePath := path.Root("runner_type") + runnerTypeValue := types.StringValue("runbook") + return []resource.ConfigValidator{ + validate.RequireAIfBEqual(path.Root("description"), runnerTypePath, runnerTypeValue), + validate.RequireAIfBEqual(path.Root("description"), runnerTypePath, runnerTypeValue), + validate.RequireAIfBEqual(path.Root("description"), runnerTypePath, runnerTypeValue), + } +} + +func (r *resourceAutomationActionsRunner) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) { + runnerTypePath := path.Root("runner_type") + + var runnerType types.String + d := req.Config.GetAttribute(ctx, runnerTypePath, &runnerType) + if resp.Diagnostics.Append(d...); d.HasError() { + return + } + + if runnerType.ValueString() != "runbook" { + resp.Diagnostics.AddAttributeError(runnerTypePath, "", "only runners of runner_type runbook can be created") + return + } +} + +func (r *resourceAutomationActionsRunner) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var model resourceAutomationActionsRunnerModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) + if resp.Diagnostics.HasError() { + return + } + plan := buildPagerdutyAutomationActionsRunner(&model) + log.Printf("[INFO] Creating PagerDuty automation actions runner %s", plan.Name) + + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + response, err := r.client.CreateAutomationActionsRunnerWithContext(ctx, plan) + if err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + plan.ID = response.ID + return nil + }) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error creating PagerDuty automation actions runner %s", plan.Name), + err.Error(), + ) + return + } + + model, err = requestGetAutomationActionsRunner(ctx, r.client, plan.ID, &plan.RunbookAPIKey, true) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty automation actions runner %s", plan.ID), + err.Error(), + ) + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) +} + +func (r *resourceAutomationActionsRunner) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var id types.String + resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &id)...) + if resp.Diagnostics.HasError() { + return + } + + runbookAPIKey := extractString(ctx, req.State, "runbook_api_key", &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + log.Printf("[INFO] Reading PagerDuty automation actions runner %s", id) + + state, err := requestGetAutomationActionsRunner(ctx, r.client, id.ValueString(), runbookAPIKey, false) + if err != nil { + if util.IsNotFoundError(err) { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty automation actions runner %s", id), + err.Error(), + ) + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *resourceAutomationActionsRunner) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var model resourceAutomationActionsRunnerModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) + if resp.Diagnostics.HasError() { + return + } + + plan := buildPagerdutyAutomationActionsRunner(&model) + log.Printf("[INFO] Updating PagerDuty automation actions runner %s", plan.ID) + + runner, err := r.client.UpdateAutomationActionsRunnerWithContext(ctx, plan) + if err != nil { + if util.IsNotFoundError(err) { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError( + fmt.Sprintf("Error updating PagerDuty automation actions runner %s", plan.ID), + err.Error(), + ) + return + } + model = flattenAutomationActionsRunner(runner, &plan.RunbookAPIKey) + + resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) +} + +func (r *resourceAutomationActionsRunner) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var id types.String + + resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &id)...) + if resp.Diagnostics.HasError() { + return + } + log.Printf("[INFO] Deleting PagerDuty automation actions runner %s", id) + + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + if err := r.client.DeleteAutomationActionsRunnerWithContext(ctx, id.ValueString()); err != nil { + if util.IsNotFoundError(err) { + return nil + } + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + return nil + }) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error deleting PagerDuty automation actions runner %s", id), + err.Error(), + ) + return + } + resp.State.RemoveResource(ctx) +} + +func (r *resourceAutomationActionsRunner) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + resp.Diagnostics.Append(ConfigurePagerdutyClient(&r.client, req.ProviderData)...) +} + +func (r *resourceAutomationActionsRunner) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +type resourceAutomationActionsRunnerModel struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Type types.String `tfsdk:"type"` + RunnerType types.String `tfsdk:"runner_type"` + Description types.String `tfsdk:"description"` + RunbookBaseURI types.String `tfsdk:"runbook_base_uri"` + RunbookAPIKey types.String `tfsdk:"runbook_api_key"` + LastSeen types.String `tfsdk:"last_seen"` + CreationTime types.String `tfsdk:"creation_time"` +} + +func requestGetAutomationActionsRunner(ctx context.Context, client *pagerduty.Client, id string, runbookAPIKey *string, retryNotFound bool) (resourceAutomationActionsRunnerModel, error) { + var model resourceAutomationActionsRunnerModel + + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + runner, err := client.GetAutomationActionsRunnerWithContext(ctx, id) + if err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + if !retryNotFound && util.IsNotFoundError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + model = flattenAutomationActionsRunner(runner, runbookAPIKey) + return nil + }) + + return model, err +} + +func buildPagerdutyAutomationActionsRunner(model *resourceAutomationActionsRunnerModel) pagerduty.AutomationActionsRunner { + runner := pagerduty.AutomationActionsRunner{ + Name: model.Name.ValueString(), + RunnerType: model.RunnerType.ValueString(), + } + + if !model.Description.IsNull() && !model.Description.IsUnknown() { + runner.Description = model.Description.ValueString() + } + + if !model.RunbookBaseURI.IsNull() && !model.RunbookBaseURI.IsUnknown() { + runner.RunbookBaseURI = model.RunbookBaseURI.ValueString() + } + + if !model.RunbookAPIKey.IsNull() && !model.RunbookAPIKey.IsUnknown() { + runner.RunbookAPIKey = model.RunbookAPIKey.ValueString() + } + + runner.ID = model.ID.ValueString() + return runner +} + +func flattenAutomationActionsRunner(response *pagerduty.AutomationActionsRunner, runbookAPIKey *string) resourceAutomationActionsRunnerModel { + model := resourceAutomationActionsRunnerModel{ + ID: types.StringValue(response.ID), + Name: types.StringValue(response.Name), + Type: types.StringValue(response.Type), + RunnerType: types.StringValue(response.RunnerType), + CreationTime: types.StringValue(response.CreationTime), + LastSeen: types.StringValue(response.LastSeen), + } + + if response.Description != "" { + model.Description = types.StringValue(response.Description) + } + + if response.RunbookBaseURI != "" { + model.RunbookBaseURI = types.StringValue(response.RunbookBaseURI) + } + + if response.RunbookAPIKey != "" { + model.RunbookAPIKey = types.StringValue(response.RunbookAPIKey) + } else if runbookAPIKey != nil { + model.RunbookAPIKey = types.StringValue(*runbookAPIKey) + } + + return model +} diff --git a/pagerduty/resource_pagerduty_automation_actions_runner_test.go b/pagerdutyplugin/resource_pagerduty_automation_actions_runner_test.go similarity index 89% rename from pagerduty/resource_pagerduty_automation_actions_runner_test.go rename to pagerdutyplugin/resource_pagerduty_automation_actions_runner_test.go index 2c721747b..bd939cded 100644 --- a/pagerduty/resource_pagerduty_automation_actions_runner_test.go +++ b/pagerdutyplugin/resource_pagerduty_automation_actions_runner_test.go @@ -1,6 +1,7 @@ package pagerduty import ( + "context" "fmt" "testing" @@ -27,9 +28,9 @@ func TestAccPagerDutyAutomationActionsRunner_Basic(t *testing.T) { descriptionUpdated := fmt.Sprintf("Description of %s-updated", runnerName) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyAutomationActionsRunnerDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyAutomationActionsRunnerDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyAutomationActionsRunnerConfig(runnerName), @@ -62,12 +63,14 @@ func TestAccPagerDutyAutomationActionsRunner_Basic(t *testing.T) { } func testAccCheckPagerDutyAutomationActionsRunnerDestroy(s *terraform.State) error { - client, _ := testAccProvider.Meta().(*Config).Client() + client := testAccProvider.client + ctx := context.Background() + for _, r := range s.RootModule().Resources { if r.Type != "pagerduty_automation_actions_runner" { continue } - if _, _, err := client.AutomationActionsRunner.Get(r.Primary.ID); err == nil { + if _, err := client.GetAutomationActionsRunnerWithContext(ctx, r.Primary.ID); err == nil { return fmt.Errorf("Automation Actions Runner still exists") } } @@ -84,8 +87,10 @@ func testAccCheckPagerDutyAutomationActionsRunnerExists(n string) resource.TestC return fmt.Errorf("No Automation Actions Runner ID is set") } - client, _ := testAccProvider.Meta().(*Config).Client() - found, _, err := client.AutomationActionsRunner.Get(rs.Primary.ID) + client := testAccProvider.client + ctx := context.Background() + + found, err := client.GetAutomationActionsRunnerWithContext(ctx, rs.Primary.ID) if err != nil { return err } From 6f1797f9554f5a67dc54b39665488ee2809cc344 Mon Sep 17 00:00:00 2001 From: Carlos Gajardo Date: Fri, 12 Jul 2024 16:51:34 -0400 Subject: [PATCH 8/8] Add vendor reference [DO NOT MERGE] --- go.mod | 2 + go.sum | 4 +- .../go-pagerduty/automation_action.go | 332 ++++++++++++++++++ .../go-pagerduty/event_orchestration.go | 24 +- vendor/modules.txt | 3 +- 5 files changed, 350 insertions(+), 15 deletions(-) create mode 100644 vendor/github.com/PagerDuty/go-pagerduty/automation_action.go diff --git a/go.mod b/go.mod index 98a5fd37d..7c3428834 100644 --- a/go.mod +++ b/go.mod @@ -76,3 +76,5 @@ require ( google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) + +replace github.com/PagerDuty/go-pagerduty => github.com/cjgajard/go-pagerduty v0.0.0-20240712203852-d83eacdec105 diff --git a/go.sum b/go.sum index c7794d9a7..2c46ff679 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/PagerDuty/go-pagerduty v1.8.1-0.20240524180345-9b652f07c450 h1:G8EOmgL7i+vO6hYAnjpVbPDqUpPY7ThF6NfasZntop0= -github.com/PagerDuty/go-pagerduty v1.8.1-0.20240524180345-9b652f07c450/go.mod h1:ilimTqwHSBjmvKeYA/yayDBZvzf/CX4Pwa9Qbhekzok= github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= @@ -11,6 +9,8 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cjgajard/go-pagerduty v0.0.0-20240712203852-d83eacdec105 h1:794KlUOMDDRNJCbSqu8REHBrSY0x4ehJLZg0U1oN2AU= +github.com/cjgajard/go-pagerduty v0.0.0-20240712203852-d83eacdec105/go.mod h1:ilimTqwHSBjmvKeYA/yayDBZvzf/CX4Pwa9Qbhekzok= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= diff --git a/vendor/github.com/PagerDuty/go-pagerduty/automation_action.go b/vendor/github.com/PagerDuty/go-pagerduty/automation_action.go new file mode 100644 index 000000000..11e26e173 --- /dev/null +++ b/vendor/github.com/PagerDuty/go-pagerduty/automation_action.go @@ -0,0 +1,332 @@ +package pagerduty + +import ( + "context" + + "github.com/google/go-querystring/query" +) + +type ActionDataReference struct { + Script *string `json:"script,omitempty"` + InvocationCommand *string `json:"invocation_command,omitempty"` + ProcessAutomationJobID *string `json:"process_automation_job_id,omitempty"` + ProcessAutomationJobArguments *string `json:"process_automation_job_arguments,omitempty"` + ProcessAutomationNodeFilter *string `json:"process_automation_node_filter,omitempty"` +} + +type Priviledges struct { + Permissions []string `json:"permissions"` +} + +type AutomationAction struct { + APIObject + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + ActionType string `json:"action_type,omitempty"` + ActionClassification *string `json:"action_classification,omitempty"` + Runner string `json:"runner,omitempty"` + RunnerType string `json:"runner_type,omitempty"` + Services []APIObject `json:"services,omitempty"` + Teams []APIObject `json:"teams,omitempty"` + ActionDataReference *ActionDataReference `json:"action_data_reference,omitempty"` + Priviledges *Priviledges `json:"priviledges,omitempty"` + Metadata interface{} `json:"metadata,omitempty"` + CreationTime string `json:"creation_time,omitempty"` + ModifyTime string `json:"modify_time,omitempty"` + LastRun string `json:"last_run,omitempty"` + LastRunBy *APIObject `json:"last_run_by,omitempty"` +} + +type AutomationActionResponse struct { + Action AutomationAction `json:"action,omitempty"` +} + +type CreateAutomationActionOptions AutomationActionResponse + +func (c *Client) CreateAutomationActionWithContext(ctx context.Context, o CreateAutomationActionOptions) (*AutomationAction, error) { + resp, err := c.post(ctx, "/automation_actions/actions", o, nil) + if err != nil { + return nil, err + } + + var response AutomationActionResponse + if err := c.decodeJSON(resp, &response); err != nil { + return nil, err + } + + return &response.Action, nil +} + +func (c *Client) GetAutomationActionWithContext(ctx context.Context, id string) (*AutomationAction, error) { + resp, err := c.get(ctx, "/automation_actions/actions/"+id, nil) + if err != nil { + return nil, err + } + + var response AutomationActionResponse + if err := c.decodeJSON(resp, &response); err != nil { + return nil, err + } + + return &response.Action, nil +} + +func (c *Client) DeleteAutomationActionWithContext(ctx context.Context, id string) error { + _, err := c.delete(ctx, "/automation_actions/actions/"+id) + return err +} + +type UpdateAutomationActionOptions AutomationActionResponse + +func (c *Client) UpdateAutomationActionWithContext(ctx context.Context, o UpdateAutomationActionOptions) (*AutomationAction, error) { + resp, err := c.put(ctx, "/automation_actions/actions/"+o.Action.ID, o, nil) + if err != nil { + return nil, err + } + + var response AutomationActionResponse + if err := c.decodeJSON(resp, &response); err != nil { + return nil, err + } + + return &response.Action, nil +} + +type AssociateAutomationActionServiceOptions struct { + Service APIReference `json:"service,omitempty"` +} + +type AutomationActionServiceResponse struct { + Service APIObject `json:"service,omitempty"` +} + +func (c *Client) AssociateAutomationActionServiceWithContext(ctx context.Context, id string, o AssociateAutomationActionServiceOptions) (*AutomationActionServiceResponse, error) { + resp, err := c.post(ctx, "/automation_actions/actions/"+id+"/services", o, nil) + if err != nil { + return nil, err + } + + var response AutomationActionServiceResponse + if err := c.decodeJSON(resp, &response); err != nil { + return nil, err + } + + return &response, nil +} + +func (c *Client) DisassociateAutomationActionServiceWithContext(ctx context.Context, actionID, serviceID string) error { + _, err := c.delete(ctx, "/automation_actions/actions/"+actionID+"/services/"+serviceID) + return err +} + +func (c *Client) GetAutomationActionServiceWithContext(ctx context.Context, actionID, serviceID string) (*AutomationActionServiceResponse, error) { + resp, err := c.get(ctx, "/automation_actions/actions/"+actionID+"/services/"+serviceID, nil) + if err != nil { + return nil, err + } + + var response AutomationActionServiceResponse + if err := c.decodeJSON(resp, &response); err != nil { + return nil, err + } + + return &response, nil +} + +type AssociateAutomationActionTeamOptions struct { + Team APIReference `json:"team,omitempty"` +} + +type AutomationActionTeamResponse struct { + Team APIObject `json:"team,omitempty"` +} + +func (c *Client) AssociateAutomationActionTeamWithContext(ctx context.Context, id string, o AssociateAutomationActionTeamOptions) (*AutomationActionTeamResponse, error) { + resp, err := c.post(ctx, "/automation_actions/actions/"+id+"/teams", o, nil) + if err != nil { + return nil, err + } + + var response AutomationActionTeamResponse + if err := c.decodeJSON(resp, &response); err != nil { + return nil, err + } + + return &response, nil +} + +func (c *Client) DisassociateAutomationActionTeamWithContext(ctx context.Context, actionID, teamID string) error { + _, err := c.delete(ctx, "/automation_actions/actions/"+actionID+"/teams/"+teamID) + return err +} + +func (c *Client) GetAutomationActionTeamWithContext(ctx context.Context, actionID, teamID string) (*AutomationActionTeamResponse, error) { + resp, err := c.get(ctx, "/automation_actions/actions/"+actionID+"/teams/"+teamID, nil) + if err != nil { + return nil, err + } + + var response AutomationActionTeamResponse + if err := c.decodeJSON(resp, &response); err != nil { + return nil, err + } + + return &response, nil +} + +type AutomationActionsRunnerActions struct { + Actions []APIObject `json:"actions,omitempty"` + More bool `json:"more,omitempty"` +} + +type AutomationActionsRunner struct { + APIObject + Secret string `json:"secret,omitempty"` + RunnerType string `json:"runner_type,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + LastSeen string `json:"last_seen,omitempty"` + Status string `json:"status,omitempty"` + CreationTime string `json:"creation_time,omitempty"` + RunbookBaseURI string `json:"runbook_base_uri,omitempty"` + RunbookAPIKey string `json:"runbook_api_key,omitempty"` + Teams []APIObject `json:"teams,omitempty"` + Priviledges *Priviledges `json:"priviledges,omitempty"` + AssociatedActions *AutomationActionsRunnerActions `json:"associated_actions,omitempty"` + Metadata interface{} `json:"metadata,omitempty"` +} + +type AutomationActionsRunnerResponse struct { + Runner AutomationActionsRunner `json:"runner,omitempty"` +} + +func (c *Client) CreateAutomationActionsRunnerWithContext(ctx context.Context, a AutomationActionsRunner) (*AutomationActionsRunner, error) { + d := map[string]AutomationActionsRunner{ + "runner": a, + } + + resp, err := c.post(ctx, "/automation_actions/runners/", d, nil) + if err != nil { + return nil, err + } + + var response AutomationActionsRunnerResponse + if err := c.decodeJSON(resp, &response); err != nil { + return nil, err + } + + return &response.Runner, nil +} + +type ListAutomationActionsRunnersOptions struct { + Cursor string `url:"include,omitempty"` + Limit uint `url:"limit,omitempty"` + Name string `url:"name,omitempty"` + Includes []string `url:"include,brackets,omitempty"` +} + +type ListAutomationActionsRunnersResponse struct { + Runners []AutomationActionsRunner `json:"runners,omitempty"` + Priviledges *Priviledges `json:"priviledges,omitempty"` + cursor + // NextCursor string `json:"next_cursor,omitempty"` + // Limit uint `json:"limit,omitempty"` + cursorHandler +} + +func (c *Client) ListAutomationActionsRunnersWithContext(ctx context.Context, o ListAutomationActionsRunnersOptions) (*ListAutomationActionsRunnersResponse, error) { + v, err := query.Values(o) + if err != nil { + return nil, err + } + + resp, err := c.get(ctx, "/automation_actions/runners?"+v.Encode(), nil) + if err != nil { + return nil, err + } + + var response ListAutomationActionsRunnersResponse + if err := c.decodeJSON(resp, &response); err != nil { + return nil, err + } + + return &response, nil +} + +func (c *Client) GetAutomationActionsRunnerWithContext(ctx context.Context, id string) (*AutomationActionsRunner, error) { + resp, err := c.get(ctx, "/automation_actions/runners/"+id, nil) + if err != nil { + return nil, err + } + + var response AutomationActionsRunnerResponse + if err := c.decodeJSON(resp, &response); err != nil { + return nil, err + } + + return &response.Runner, nil +} + +func (c *Client) UpdateAutomationActionsRunnerWithContext(ctx context.Context, a AutomationActionsRunner) (*AutomationActionsRunner, error) { + d := map[string]AutomationActionsRunner{ + "runner": a, + } + + resp, err := c.put(ctx, "/automation_actions/runners/"+a.ID, d, nil) + if err != nil { + return nil, err + } + + var response AutomationActionsRunnerResponse + if err := c.decodeJSON(resp, &response); err != nil { + return nil, err + } + + return &response.Runner, nil +} + +func (c *Client) DeleteAutomationActionsRunnerWithContext(ctx context.Context, id string) error { + _, err := c.delete(ctx, "/automation_actions/runners/"+id) + return err +} + +type AssociateAutomationActionsRunnerTeamOptions struct { + Team APIReference `json:"team,omitempty"` +} + +type AutomationActionsRunnerTeamResponse struct { + Team APIObject `json:"team,omitempty"` +} + +func (c *Client) AssociateAutomationActionsRunnerTeamWithContext(ctx context.Context, id string, o AssociateAutomationActionsRunnerTeamOptions) (*AutomationActionsRunnerTeamResponse, error) { + resp, err := c.post(ctx, "/automation_actions/runners/"+id+"/teams", o, nil) + if err != nil { + return nil, err + } + + var response AutomationActionsRunnerTeamResponse + if err := c.decodeJSON(resp, &response); err != nil { + return nil, err + } + + return &response, nil +} + +func (c *Client) DisassociateAutomationActionsRunnerTeamWithContext(ctx context.Context, actionID, teamID string) error { + _, err := c.delete(ctx, "/automation_actions/runners/"+actionID+"/teams/"+teamID) + return err +} + +func (c *Client) GetAutomationActionsRunnerTeamWithContext(ctx context.Context, actionID, teamID string) (*AutomationActionsRunnerTeamResponse, error) { + resp, err := c.get(ctx, "/automation_actions/runners/"+actionID+"/teams/"+teamID, nil) + if err != nil { + return nil, err + } + + var response AutomationActionsRunnerTeamResponse + if err := c.decodeJSON(resp, &response); err != nil { + return nil, err + } + + return &response, nil +} diff --git a/vendor/github.com/PagerDuty/go-pagerduty/event_orchestration.go b/vendor/github.com/PagerDuty/go-pagerduty/event_orchestration.go index 7abc60d79..609318fb7 100644 --- a/vendor/github.com/PagerDuty/go-pagerduty/event_orchestration.go +++ b/vendor/github.com/PagerDuty/go-pagerduty/event_orchestration.go @@ -272,17 +272,17 @@ type ServiceOrchestrationRuleCondition struct { // ServiceOrchestrationRuleActions are the actions that will be taken to change the resulting alert and incident. type ServiceOrchestrationRuleActions struct { - RouteTo string `json:"route_to,omitempty"` - Suppress bool `json:"suppress,omitempty"` - Suspend uint `json:"suspend,omitempty"` - Priority string `json:"priority,omitempty"` - Annotate string `json:"annotate,omitempty"` - PagerDutyAutomationActions []*PagerDutyAutomationAction `json:"pagerduty_automation_actions,omitempty"` - AutomationActions []*AutomationAction `json:"automation_actions,omitempty"` - Severity string `json:"severity,omitempty"` - EventAction string `json:"event_action,omitempty"` - Variables []*OrchestrationVariable `json:"variables,omitempty"` - Extractions []*OrchestrationExtraction `json:"extractions,omitempty"` + RouteTo string `json:"route_to,omitempty"` + Suppress bool `json:"suppress,omitempty"` + Suspend uint `json:"suspend,omitempty"` + Priority string `json:"priority,omitempty"` + Annotate string `json:"annotate,omitempty"` + PagerDutyAutomationActions []*PagerDutyAutomationAction `json:"pagerduty_automation_actions,omitempty"` + AutomationActions []*OrchestrationAutomationAction `json:"automation_actions,omitempty"` + Severity string `json:"severity,omitempty"` + EventAction string `json:"event_action,omitempty"` + Variables []*OrchestrationVariable `json:"variables,omitempty"` + Extractions []*OrchestrationExtraction `json:"extractions,omitempty"` } type ServiceOrchestrationActive struct { @@ -451,7 +451,7 @@ type PagerDutyAutomationAction struct { ActionID string `json:"action_id,omitempty"` } -type AutomationAction struct { +type OrchestrationAutomationAction struct { Name string `json:"name,omitempty"` URL string `json:"url,omitempty"` AutoSend bool `json:"auto_send,omitempty"` diff --git a/vendor/modules.txt b/vendor/modules.txt index 26f324e00..6a9d0a5c6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# github.com/PagerDuty/go-pagerduty v1.8.1-0.20240524180345-9b652f07c450 +# github.com/PagerDuty/go-pagerduty v1.8.1-0.20240524180345-9b652f07c450 => github.com/cjgajard/go-pagerduty v0.0.0-20240712203852-d83eacdec105 ## explicit; go 1.19 github.com/PagerDuty/go-pagerduty # github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c @@ -559,3 +559,4 @@ google.golang.org/protobuf/types/known/timestamppb # gopkg.in/ini.v1 v1.67.0 ## explicit gopkg.in/ini.v1 +# github.com/PagerDuty/go-pagerduty => github.com/cjgajard/go-pagerduty v0.0.0-20240712203852-d83eacdec105