From e5c15fb3669be545655f714a0933b08ed2f00b37 Mon Sep 17 00:00:00 2001 From: Givi Khojanashvili Date: Wed, 4 Dec 2024 13:45:54 +0400 Subject: [PATCH] feat: sys-274 add opsgenie integration resource --- docs/resources/integration_opsgenie.md | 36 +++++ go.mod | 2 +- go.sum | 4 +- internal/provider/provider.go | 2 + .../provider/resource_integration_opsgenie.go | 133 ++++++++++++++++++ .../resource_integration_opsgenie_test.go | 54 +++++++ .../_basic/main.tf | 17 +++ internal/provider/testing.go | 2 +- 8 files changed, 246 insertions(+), 4 deletions(-) create mode 100644 docs/resources/integration_opsgenie.md create mode 100644 internal/provider/resource_integration_opsgenie.go create mode 100644 internal/provider/resource_integration_opsgenie_test.go create mode 100644 internal/provider/testdata/resource_integration_opsgenie/_basic/main.tf diff --git a/docs/resources/integration_opsgenie.md b/docs/resources/integration_opsgenie.md new file mode 100644 index 0000000..7908dab --- /dev/null +++ b/docs/resources/integration_opsgenie.md @@ -0,0 +1,36 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "uptime_integration_opsgenie Resource - terraform-provider-uptime" +subcategory: "" +description: |- + Opsgenie integration resource +--- + +# uptime_integration_opsgenie (Resource) + +Opsgenie integration resource + + + + +## Schema + +### Required + +- `api_endpoint` (String) +- `api_key` (String) +- `name` (String) + +### Optional + +- `auto_resolve` (Boolean) Automatically resolve incident once the check is back up. +- `contact_groups` (Set of String) +- `tags` (String) A comma separated list of labels attached to the alert. You may overwrite the quiet hours setting for urgent alerts by adding the OverwriteQuietHours tag. Leave blank to automatically pull the tags from the check instead. +- `teams` (String) A comma separated list of team names which will be responsible for the alert + +### Read-Only + +- `id` (Number) The ID of this resource. +- `url` (String) + + diff --git a/go.mod b/go.mod index 5108ca1..249cc7b 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/shopspring/decimal v1.4.0 github.com/stretchr/testify v1.10.0 - github.com/uptime-com/uptime-client-go/v2 v2.0.0-20241203124913-62cfc1744b9a + github.com/uptime-com/uptime-client-go/v2 v2.0.0-20241204093759-45854fa6a664 ) require ( diff --git a/go.sum b/go.sum index 021e2ea..a7031da 100644 --- a/go.sum +++ b/go.sum @@ -157,8 +157,8 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/uptime-com/uptime-client-go/v2 v2.0.0-20241203124913-62cfc1744b9a h1:SUXIUdqiAvW42db1yTE+aIuYAdVyMuv7o3VcsCoInsM= -github.com/uptime-com/uptime-client-go/v2 v2.0.0-20241203124913-62cfc1744b9a/go.mod h1:jtWeB/tQ00fLX2r9OwKfTnxQ/PMR0YjmhTuc9RZH2h0= +github.com/uptime-com/uptime-client-go/v2 v2.0.0-20241204093759-45854fa6a664 h1:IphsKMPkm6/EQryzDZtfYxImHyk/HnCkJs1MtkB7NGU= +github.com/uptime-com/uptime-client-go/v2 v2.0.0-20241204093759-45854fa6a664/go.mod h1:jtWeB/tQ00fLX2r9OwKfTnxQ/PMR0YjmhTuc9RZH2h0= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= diff --git a/internal/provider/provider.go b/internal/provider/provider.go index f5dc79a..a29f281 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -138,6 +138,8 @@ func (p *providerImpl) Resources(ctx context.Context) []func() resource.Resource func() resource.Resource { return NewCheckWHOISResource(ctx, p) }, func() resource.Resource { return NewCheckWebhookResource(ctx, p) }, func() resource.Resource { return NewCheckMaintenanceResource(ctx, p) }, + + func() resource.Resource { return NewIntegrationOpsgenieResource(ctx, p) }, func() resource.Resource { return NewContactResource(ctx, p) }, func() resource.Resource { return NewCredentialResource(ctx, p) }, func() resource.Resource { return NewStatusPageResource(ctx, p) }, diff --git a/internal/provider/resource_integration_opsgenie.go b/internal/provider/resource_integration_opsgenie.go new file mode 100644 index 0000000..97a7852 --- /dev/null +++ b/internal/provider/resource_integration_opsgenie.go @@ -0,0 +1,133 @@ +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/uptime-com/uptime-client-go/v2/pkg/upapi" +) + +func NewIntegrationOpsgenieResource(_ context.Context, p *providerImpl) resource.Resource { + return APIResource[IntegrationOpsgenieResourceModel, upapi.IntegrationOpsgenie, upapi.Integration]{ + api: IntegrationOpsgenieResourceAPI{provider: p}, + mod: IntegrationOpsgenieResourceModelAdapter{}, + meta: APIResourceMetadata{ + TypeNameSuffix: "integration_opsgenie", + Schema: schema.Schema{ + Description: "Opsgenie integration resource", + Attributes: map[string]schema.Attribute{ + "id": IDSchemaAttribute(), + "url": URLSchemaAttribute(), + "name": NameSchemaAttribute(), + "contact_groups": ContactGroupsSchemaAttribute(), + "api_endpoint": schema.StringAttribute{ + Required: true, + }, + "api_key": schema.StringAttribute{ + Required: true, + }, + "teams": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + Description: "A comma separated list of team names which will be responsible for the alert", + }, + "tags": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + Description: ("A comma separated list of labels attached to the alert. " + + "You may overwrite the quiet hours setting for urgent alerts by adding the OverwriteQuietHours tag. " + + "Leave blank to automatically pull the tags from the check instead."), + }, + "auto_resolve": schema.BoolAttribute{ + Optional: true, + Computed: true, + Description: "Automatically resolve incident once the check is back up.", + Default: booldefault.StaticBool(false), + }, + }, + }, + }, + } +} + +type IntegrationOpsgenieResourceModel struct { + ID types.Int64 `tfsdk:"id"` + URL types.String `tfsdk:"url"` + Name types.String `tfsdk:"name"` + ContactGroups types.Set `tfsdk:"contact_groups"` + APIEndpoint types.String `tfsdk:"api_endpoint"` + APIKey types.String `tfsdk:"api_key"` + Teams types.String `tfsdk:"teams"` + Tags types.String `tfsdk:"tags"` + AutoResolve types.Bool `tfsdk:"auto_resolve"` +} + +func (m IntegrationOpsgenieResourceModel) PrimaryKey() upapi.PrimaryKey { + return upapi.PrimaryKey(m.ID.ValueInt64()) +} + +type IntegrationOpsgenieResourceModelAdapter struct { + ContactGroupsAttributeAdapter +} + +func (a IntegrationOpsgenieResourceModelAdapter) Get(ctx context.Context, sg StateGetter) (*IntegrationOpsgenieResourceModel, diag.Diagnostics) { + model := *new(IntegrationOpsgenieResourceModel) + diags := sg.Get(ctx, &model) + if diags.HasError() { + return nil, diags + } + return &model, nil +} + +func (a IntegrationOpsgenieResourceModelAdapter) ToAPIArgument(model IntegrationOpsgenieResourceModel) (*upapi.IntegrationOpsgenie, error) { + return &upapi.IntegrationOpsgenie{ + Name: model.Name.ValueString(), + ContactGroups: a.ContactGroups(model.ContactGroups), + APIEndpoint: model.APIEndpoint.ValueString(), + APIKey: model.APIKey.ValueString(), + Teams: model.Teams.ValueString(), + Tags: model.Tags.ValueString(), + Autoresolve: model.AutoResolve.ValueBool(), + }, nil +} + +func (a IntegrationOpsgenieResourceModelAdapter) FromAPIResult(api upapi.Integration) (*IntegrationOpsgenieResourceModel, error) { + return &IntegrationOpsgenieResourceModel{ + ID: types.Int64Value(api.PK), + URL: types.StringValue(api.URL), + Name: types.StringValue(api.Name), + ContactGroups: a.ContactGroupsValue(api.ContactGroups), + APIEndpoint: types.StringValue(api.APIEndpoint), + APIKey: types.StringValue(api.APIKey), + Teams: types.StringValue(api.Teams), + Tags: types.StringValue(api.Tags), + AutoResolve: types.BoolValue(api.Autoresolve), + }, nil +} + +type IntegrationOpsgenieResourceAPI struct { + provider *providerImpl +} + +func (a IntegrationOpsgenieResourceAPI) Create(ctx context.Context, arg upapi.IntegrationOpsgenie) (*upapi.Integration, error) { + return a.provider.api.Integrations().CreateOpsgenie(ctx, arg) +} + +func (a IntegrationOpsgenieResourceAPI) Read(ctx context.Context, pk upapi.PrimaryKeyable) (*upapi.Integration, error) { + return a.provider.api.Integrations().Get(ctx, pk) +} + +func (a IntegrationOpsgenieResourceAPI) Update(ctx context.Context, pk upapi.PrimaryKeyable, arg upapi.IntegrationOpsgenie) (*upapi.Integration, error) { + return a.provider.api.Integrations().UpdateOpsgenie(ctx, pk, arg) +} + +func (a IntegrationOpsgenieResourceAPI) Delete(ctx context.Context, pk upapi.PrimaryKeyable) error { + return a.provider.api.Integrations().Delete(ctx, pk) +} diff --git a/internal/provider/resource_integration_opsgenie_test.go b/internal/provider/resource_integration_opsgenie_test.go new file mode 100644 index 0000000..f5e425b --- /dev/null +++ b/internal/provider/resource_integration_opsgenie_test.go @@ -0,0 +1,54 @@ +package provider + +import ( + "testing" + + petname "github.com/dustinkirkland/golang-petname" + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccIntegrationOpsgenieResource(t *testing.T) { + names := [2]string{ + petname.Generate(3, "-"), + petname.Generate(3, "-"), + } + apiEndpoint := [2]string{ + "https://api.opsgenie.com/v1/json/uptime1", + "https://api.opsgenie.com/v1/json/uptime2", + } + apiKey := [2]string{ + "16c8bfe0-b219-11ef-a5da-4b9e62fe7439", + "274d1848-b219-11ef-a468-9f18e59bdc97", + } + resource.Test(t, testCaseFromSteps(t, []resource.TestStep{ + { + ConfigVariables: config.Variables{ + "name": config.StringVariable(names[0]), + "api_endpoint": config.StringVariable(apiEndpoint[0]), + "api_key": config.StringVariable(apiKey[0]), + }, + ConfigDirectory: config.StaticDirectory("testdata/resource_integration_opsgenie/_basic"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("uptime_integration_opsgenie.test", "id"), + resource.TestCheckResourceAttrSet("uptime_integration_opsgenie.test", "url"), + resource.TestCheckResourceAttr("uptime_integration_opsgenie.test", "name", names[0]), + resource.TestCheckResourceAttr("uptime_integration_opsgenie.test", "api_endpoint", apiEndpoint[0]), + resource.TestCheckResourceAttr("uptime_integration_opsgenie.test", "api_key", apiKey[0]), + ), + }, + { + ConfigVariables: config.Variables{ + "name": config.StringVariable(names[1]), + "api_endpoint": config.StringVariable(apiEndpoint[1]), + "api_key": config.StringVariable(apiKey[1]), + }, + ConfigDirectory: config.StaticDirectory("testdata/resource_integration_opsgenie/_basic"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("uptime_integration_opsgenie.test", "name", names[1]), + resource.TestCheckResourceAttr("uptime_integration_opsgenie.test", "api_endpoint", apiEndpoint[1]), + resource.TestCheckResourceAttr("uptime_integration_opsgenie.test", "api_key", apiKey[1]), + ), + }, + })) +} diff --git a/internal/provider/testdata/resource_integration_opsgenie/_basic/main.tf b/internal/provider/testdata/resource_integration_opsgenie/_basic/main.tf new file mode 100644 index 0000000..e8ed9f1 --- /dev/null +++ b/internal/provider/testdata/resource_integration_opsgenie/_basic/main.tf @@ -0,0 +1,17 @@ +variable name { + type = string +} + +variable api_endpoint { + type = string +} + +variable api_key { + type = string +} + +resource uptime_integration_opsgenie test { + name = var.name + api_endpoint = var.api_endpoint + api_key = var.api_key +} diff --git a/internal/provider/testing.go b/internal/provider/testing.go index 6e8ff15..6c4b570 100644 --- a/internal/provider/testing.go +++ b/internal/provider/testing.go @@ -52,7 +52,7 @@ func testAccAPIClient(t testing.TB) upapi.API { token := os.Getenv("UPTIME_TOKEN") require.NotEmpty(t, token, "UPTIME_TOKEN must be set for acceptance tests") - api, err := upapi.New(upapi.WithToken(token), upapi.WithRateLimit(0.2)) + api, err := upapi.New(upapi.WithToken(token), upapi.WithRateLimit(0.15)) require.NoError(t, err, "failed to initialize uptime.com api client") return api