-
Notifications
You must be signed in to change notification settings - Fork 248
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(key_transaction): add new resource to manage key transactions (#…
- Loading branch information
1 parent
6a1d90c
commit c237c0b
Showing
6 changed files
with
365 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
package newrelic | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/newrelic/newrelic-client-go/v2/pkg/common" | ||
"github.com/newrelic/newrelic-client-go/v2/pkg/entities" | ||
"github.com/newrelic/newrelic-client-go/v2/pkg/keytransaction" | ||
) | ||
|
||
func resourceNewRelicKeyTransaction() *schema.Resource { | ||
return &schema.Resource{ | ||
CreateContext: resourceNewRelicKeyTransactionCreate, | ||
ReadContext: resourceNewRelicKeyTransactionRead, | ||
UpdateContext: resourceNewRelicKeyTransactionUpdate, | ||
DeleteContext: resourceNewRelicKeyTransactionDelete, | ||
Importer: &schema.ResourceImporter{ | ||
StateContext: schema.ImportStatePassthroughContext, | ||
}, | ||
Schema: map[string]*schema.Schema{ | ||
"apdex_index": { | ||
Type: schema.TypeFloat, | ||
Description: "The acceptable amount of the time spent in the backend before customers get frustrated (Apdex target)", | ||
Required: true, | ||
}, | ||
"application_guid": { | ||
Type: schema.TypeString, | ||
Description: "The GUID of the application.", | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"browser_apdex_target": { | ||
Type: schema.TypeFloat, | ||
Description: "The acceptable amount of time for rendering a page in a browser before customers get frustrated (browser Apdex target).", | ||
Required: true, | ||
}, | ||
"metric_name": { | ||
Type: schema.TypeString, | ||
Description: "The name of the metric underlying this key transaction", | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"name": { | ||
Type: schema.TypeString, | ||
Description: "The name of the key transaction.", | ||
Required: true, | ||
}, | ||
"domain": { | ||
Type: schema.TypeString, | ||
Description: "Domain of the entity.", | ||
Required: false, | ||
Computed: true, | ||
}, | ||
"type": { | ||
Type: schema.TypeString, | ||
Description: "Type of the entity.", | ||
Required: false, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceNewRelicKeyTransactionCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client := meta.(*ProviderConfig).NewClient | ||
|
||
apdexIndex, | ||
applicationGUID, | ||
browserApdexTarget, | ||
metricName, | ||
name := resourceNewRelicKeyTransactionFetchValuesFromConfig(d) | ||
|
||
log.Printf("[INFO] Creating New Relic Key Transaction %s", name) | ||
createKeyTransactionResult, err := client.KeyTransaction.KeyTransactionCreate( | ||
apdexIndex, | ||
keytransaction.EntityGUID(applicationGUID), | ||
browserApdexTarget, | ||
metricName, | ||
name, | ||
) | ||
|
||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
if createKeyTransactionResult == nil { | ||
return diag.FromErr(fmt.Errorf("something went wrong while creating the key transaction")) | ||
} | ||
|
||
resourceNewRelicKeyTransactionSetValuesToState(d, *createKeyTransactionResult, keytransaction.KeyTransactionUpdateResult{}) | ||
|
||
return nil | ||
} | ||
|
||
func resourceNewRelicKeyTransactionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
providerConfig := meta.(*ProviderConfig) | ||
client := providerConfig.NewClient | ||
|
||
guid := d.Id() | ||
|
||
resp, err := client.Entities.GetEntityWithContext(ctx, common.EntityGUID(guid)) | ||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
if resp == nil { | ||
d.SetId("") | ||
return diag.FromErr(fmt.Errorf("no key transaction found with given guid %s", guid)) | ||
} | ||
|
||
switch (*resp).(type) { | ||
case *entities.KeyTransactionEntity: | ||
entity := (*resp).(*entities.KeyTransactionEntity) | ||
_ = d.Set("apdex_index", entity.ApdexTarget) | ||
_ = d.Set("application_guid", entity.Application.GUID) | ||
_ = d.Set("browser_apdex_target", entity.BrowserApdexTarget) | ||
_ = d.Set("metric_name", entity.MetricName) | ||
_ = d.Set("name", entity.Name) | ||
default: | ||
return diag.FromErr(fmt.Errorf("entity with GUID %s was not a key transaction", guid)) | ||
} | ||
return nil | ||
} | ||
|
||
func resourceNewRelicKeyTransactionUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client := meta.(*ProviderConfig).NewClient | ||
|
||
apdexIndex, | ||
_, | ||
browserApdexTarget, | ||
_, | ||
name := resourceNewRelicKeyTransactionFetchValuesFromConfig(d) | ||
|
||
guid := keytransaction.EntityGUID(d.Id()) | ||
log.Printf("[INFO] Updating New Relic Key Transaction %s", name) | ||
|
||
updateKeyTransactionResult, err := client.KeyTransaction.KeyTransactionUpdate(apdexIndex, browserApdexTarget, guid, name) | ||
|
||
if err != nil { | ||
|
||
return diag.FromErr(err) | ||
} | ||
if updateKeyTransactionResult == nil { | ||
return diag.FromErr(fmt.Errorf("something went wrong while updating the key transaction")) | ||
} | ||
|
||
resourceNewRelicKeyTransactionSetValuesToState(d, keytransaction.KeyTransactionCreateResult{}, *updateKeyTransactionResult) | ||
return nil | ||
} | ||
|
||
func resourceNewRelicKeyTransactionDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client := meta.(*ProviderConfig).NewClient | ||
keyTransactionGUID := keytransaction.EntityGUID(d.Id()) | ||
|
||
log.Printf("[INFO] Deleting New Relic Key Transaction %s", d.Id()) | ||
_, err := client.KeyTransaction.KeyTransactionDelete(keyTransactionGUID) | ||
|
||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
return nil | ||
} | ||
|
||
func resourceNewRelicKeyTransactionFetchValuesFromConfig(d *schema.ResourceData) ( | ||
apdexIndex float64, | ||
applicationGUID string, | ||
browserApdexTarget float64, | ||
metricName string, | ||
name string, | ||
) { | ||
apdexIndex = d.Get("apdex_index").(float64) | ||
applicationGUID = d.Get("application_guid").(string) | ||
browserApdexTarget = d.Get("browser_apdex_target").(float64) | ||
metricName = d.Get("metric_name").(string) | ||
name = d.Get("name").(string) | ||
|
||
return apdexIndex, applicationGUID, browserApdexTarget, metricName, name | ||
} | ||
|
||
func resourceNewRelicKeyTransactionSetValuesToState( | ||
d *schema.ResourceData, | ||
createKeyTransactionResult keytransaction.KeyTransactionCreateResult, | ||
updateKeyTransactionResult keytransaction.KeyTransactionUpdateResult, | ||
) { | ||
if createKeyTransactionResult.GUID != "" && updateKeyTransactionResult.Name == "" { | ||
d.SetId(string(createKeyTransactionResult.GUID)) | ||
_ = d.Set("apdex_index", createKeyTransactionResult.ApdexTarget) | ||
_ = d.Set("application_guid", createKeyTransactionResult.Application.GUID) | ||
_ = d.Set("browser_apdex_target", createKeyTransactionResult.BrowserApdexTarget) | ||
_ = d.Set("metric_name", createKeyTransactionResult.MetricName) | ||
_ = d.Set("name", createKeyTransactionResult.Name) | ||
entity := createKeyTransactionResult.Application.Entity.(*keytransaction.ApmApplicationEntityOutline) | ||
_ = d.Set("domain", entity.Domain) | ||
_ = d.Set("type", entity.Type) | ||
} else if createKeyTransactionResult.GUID == "" && updateKeyTransactionResult.Name != "" { | ||
_ = d.Set("apdex_index", updateKeyTransactionResult.ApdexTarget) | ||
_ = d.Set("application_guid", updateKeyTransactionResult.Application.GUID) | ||
_ = d.Set("browser_apdex_target", updateKeyTransactionResult.BrowserApdexTarget) | ||
_ = d.Set("name", updateKeyTransactionResult.Name) | ||
entity := updateKeyTransactionResult.Application.Entity.(*keytransaction.ApmApplicationEntityOutline) | ||
_ = d.Set("domain", entity.Domain) | ||
_ = d.Set("type", entity.Type) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
//go:build integration | ||
// +build integration | ||
|
||
package newrelic | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform" | ||
"github.com/newrelic/newrelic-client-go/v2/pkg/common" | ||
"github.com/newrelic/newrelic-client-go/v2/pkg/entities" | ||
) | ||
|
||
func TestAccNewRelicKeyTransaction_Basic(t *testing.T) { | ||
randomName := fmt.Sprintf("tf-test-%s", acctest.RandString(5)) | ||
resourceName := "newrelic_key_transaction.foo" | ||
resource.ParallelTest(t, resource.TestCase{ | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
// Create | ||
{ | ||
Config: testAccNewRelicKeyTransactionBasicConfiguration(fmt.Sprintf("%s", randomName)), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccNewRelicCheckKeyTransactionExists(resourceName), | ||
), | ||
}, | ||
// Update | ||
{ | ||
Config: testAccNewRelicKeyTransactionBasicConfiguration(fmt.Sprintf("%s-updated", randomName)), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccNewRelicCheckKeyTransactionExists(resourceName), | ||
), | ||
}, | ||
// Import | ||
{ | ||
ResourceName: resourceName, | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
ImportStateVerifyIgnore: []string{ | ||
"domain", | ||
"type", | ||
}, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccNewRelicKeyTransaction_DuplicateNameError(t *testing.T) { | ||
resource.ParallelTest(t, resource.TestCase{ | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
// create a key transaction with a name that already exists in the UI | ||
// this is expected to throw an error, as only one key transaction may be created per metric name | ||
{ | ||
Config: testAccNewRelicKeyTransactionBasicConfiguration(fmt.Sprintf("%s", "terraform_acceptance_test_key_transaction_donot_delete")), | ||
ExpectError: regexp.MustCompile("\\s*"), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccNewRelicKeyTransactionBasicConfiguration(name string) string { | ||
return fmt.Sprintf(` | ||
resource "newrelic_key_transaction" "foo" { | ||
apdex_index = 0.5 | ||
application_guid = "MzgwNjUyNnxBUE18QVBQTElDQVRJT058NTUzNDQ4MjAy" | ||
browser_apdex_target = 0.5 | ||
metric_name = "test" | ||
name = "%[1]s" | ||
} | ||
`, name) | ||
} | ||
|
||
func testAccNewRelicCheckKeyTransactionExists(resourceName string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources[resourceName] | ||
|
||
if !ok { | ||
return fmt.Errorf("not found: %s", resourceName) | ||
} | ||
if rs.Primary.ID == "" { | ||
return fmt.Errorf("no key transaction ID is set") | ||
} | ||
|
||
client := testAccProvider.Meta().(*ProviderConfig).NewClient | ||
|
||
found, err := client.Entities.GetEntity(common.EntityGUID(rs.Primary.ID)) | ||
if err != nil { | ||
return fmt.Errorf(err.Error()) | ||
} | ||
|
||
x := (*found).(*entities.KeyTransactionEntity) | ||
if x.GUID != common.EntityGUID(rs.Primary.ID) { | ||
return fmt.Errorf("key transaction not found") | ||
} | ||
|
||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
--- | ||
layout: "newrelic" | ||
page_title: "New Relic: newrelic_key_transaction" | ||
sidebar_current: "docs-newrelic-resource-key-transaction" | ||
description: |- | ||
Create a new New Relic Key Transaction. | ||
--- | ||
|
||
# Resource: newrelic\_key\_transaction | ||
|
||
Use this resource to create a new Key Transaction in New Relic. | ||
|
||
-> **NOTE:** For more information on Key Transactions, head over to [this page](https://docs.newrelic.com/docs/apm/transactions/key-transactions/introduction-key-transactions/) in New Relic's docs. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
resource "newrelic_key_transaction" "foo" { | ||
application_guid = "MzgfNjUyNnxBUE19QVBQTElDQVHJT068NTUfNDT4MjUy" | ||
apdex_index = 0.5 | ||
browser_apdex_target = 0.5 | ||
metric_name = "WebTransaction/Function/__main__:foo_bar" | ||
name = "Sample Key Transaction" | ||
} | ||
``` | ||
## Argument Reference | ||
|
||
The following arguments are supported by this resource. | ||
|
||
* `application_guid` - (Required) The GUID of the APM Application comprising transactions, of which one would be made a key transaction. | ||
* `metric_name` - (Required) - The name of the underlying metric monitored by the key transaction to be created. | ||
* `name` - (Required) - The name of the key transaction. | ||
* `apdex_index` - (Required) A decimal value, measuring user satisfaction with response times, ranging from 0 (frustrated) to 1 (satisfied). | ||
* `browser_apdex_target` - (Required) A decimal value representing the response time threshold for satisfactory experience (e.g., 0.5 seconds). | ||
|
||
-> **NOTE:** It may be noted that the `metric_name` and `application_guid` of a Key Transaction _cannot_ be updated in a key transaction that has already been created; since this is not supported. As a consequence, altering the values of `application_guid` and/or `metric_name` of a `newrelic_key_transaction` resource created (to try updating these values) would result in `terraform plan` prompting a forced destruction and re-creation of the resource. | ||
|
||
## Attributes Reference | ||
|
||
In addition to all arguments above, the following attributes are exported by this resource. | ||
|
||
* `id` - The GUID of the created key transaction. | ||
* `domain` - The domain of the entity monitored by the key transaction. | ||
* `type` - The type of the entity monitored by the key transaction. | ||
|
||
## Import | ||
A Key Transaction in New Relic may be imported into Terraform using its GUID specified in the `<id>` field, in the following command. | ||
|
||
```bash | ||
$ terraform import newrelic_key_transaction.foo <id> | ||
``` |