Skip to content

Commit

Permalink
Feature: AppDynamics data link (#558)
Browse files Browse the repository at this point in the history
* add target_appd_url with url input validation

* add acceptance tests for new appd data link

* bump signalfx-go version

* set datalink type as APPD_LINK

* chore: cleanup comments

* adjust appd_link url parsing to match expected appd_link input

* fix typo

* Refactor url validation to use regex

* Update expected message

* chore: update go.sum

* use regex Compile instead of MustCompile

* fix linter errors - capitalizations and unnecessary nil check

* update data link docs
  • Loading branch information
chrjosep authored Jan 14, 2025
1 parent 789a2c4 commit e7a73ea
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 1 deletion.
61 changes: 60 additions & 1 deletion signalfx/resource_signalfx_data_link.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding/json"
"fmt"
"log"
"regexp"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -128,6 +129,25 @@ func dataLinkResource() *schema.Resource {
},
},
},
"target_appd_url": {
Type: schema.TypeSet,
Optional: true,
Description: "Link to AppDynamics URL",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "User-assigned target name. Use this value to differentiate between the link targets for a data link object.",
},
"url": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "URL string for an AppDyanmics data link target.",
},
},
},
},
},

Create: dataLinkCreate,
Expand Down Expand Up @@ -206,6 +226,33 @@ func getPayloadDataLink(d *schema.ResourceData) (*datalink.CreateUpdateDataLinkR
}
}

if val, ok := d.GetOk("target_appd_url"); ok {
appdURLs := val.(*schema.Set).List()

appdURLPatternRegex := "^https?:\\/\\/[a-zA-Z0-9-]+\\.saas\\.appdynamics\\.com\\/.*application=\\d+.*component=\\d+.*"
re, err := regexp.Compile(appdURLPatternRegex)

if err != nil {
return dataLink, err
}

for _, tfLink := range appdURLs {
tfLink := tfLink.(map[string]interface{})

dl := &datalink.Target{
Name: tfLink["name"].(string),
URL: tfLink["url"].(string),
Type: datalink.APPD_LINK,
}
match := re.MatchString(dl.URL)
if !match {
return dataLink, fmt.Errorf("enter a valid AppD Link. The link needs to include the contoller URL, application ID, and Application component")
}

dataLink.Targets = append(dataLink.Targets, dl)
}
}

if val, ok := d.GetOk("target_external_url"); ok {
exURLs := val.(*schema.Set).List()

Expand Down Expand Up @@ -249,7 +296,7 @@ func getPayloadDataLink(d *schema.ResourceData) (*datalink.CreateUpdateDataLinkR
}

if len(dataLink.Targets) < 1 {
return dataLink, fmt.Errorf("You must provide one or more of `target_signalfx_dashboard`, `target_external_url`, or `target_splunk`")
return dataLink, fmt.Errorf("You must provide one or more of `target_signalfx_dashboard`, `target_external_url`, `target_appd_url` or `target_splunk`")
}

return dataLink, nil
Expand Down Expand Up @@ -290,6 +337,7 @@ func dataLinkAPIToTF(d *schema.ResourceData, dl *datalink.DataLink) error {
var internalLinks []map[string]interface{}
var externalLinks []map[string]interface{}
var splunkLinks []map[string]interface{}
var appdLinks []map[string]interface{}

for _, t := range dl.Targets {
switch t.Type {
Expand All @@ -316,6 +364,12 @@ func dataLinkAPIToTF(d *schema.ResourceData, dl *datalink.DataLink) error {
"property_key_mapping": t.PropertyKeyMapping,
}
splunkLinks = append(splunkLinks, tfTarget)
case datalink.APPD_LINK:
tfTarget := map[string]interface{}{
"name": t.Name,
"url": t.URL,
}
appdLinks = append(appdLinks, tfTarget)
default:
return fmt.Errorf("Unknown link type: %s", t.Type)
}
Expand All @@ -335,6 +389,11 @@ func dataLinkAPIToTF(d *schema.ResourceData, dl *datalink.DataLink) error {
return err
}
}
if len(appdLinks) > 0 {
if err := d.Set("target_appd_url", appdLinks); err != nil {
return err
}
}

return nil
}
Expand Down
54 changes: 54 additions & 0 deletions signalfx/resource_signalfx_data_link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,30 @@ resource "signalfx_data_link" "big_test_data_link" {
}
`

const newDataLinkAppdConfig = `
resource "signalfx_data_link" "big_test_data_link" {
property_name = "pname"
property_value = "pvalue"
target_appd_url {
name = "appd_url"
url = "https://example.saas.appdynamics.com/controller/#/application=3039831&component=3677819"
}
}
`

const newDataLinkAppdConfigBadURLErr = `
resource "signalfx_data_link" "big_test_data_link" {
property_name = "pname"
property_value = "pvalue"
target_appd_url {
name = "appd_url"
url = "https://example.saas.appdynamics.com/controller/#/application=3039831"
}
}
`

func TestAccCreateDashboardDataLinkFails(t *testing.T) {
resource.Test(t, resource.TestCase{
Providers: testAccProviders,
Expand Down Expand Up @@ -140,6 +164,36 @@ func TestAccCreateUpdateDataLinkWithoutPropertyValue(t *testing.T) {
})
}

func TestAccCreateAppdDataLinkFails(t *testing.T) {
resource.Test(t, resource.TestCase{
Providers: testAccProviders,
CheckDestroy: testAccDataLinkDestroy,
Steps: []resource.TestStep{
{
Config: newDataLinkAppdConfigBadUrlErr,

Check failure on line 173 in signalfx/resource_signalfx_data_link_test.go

View workflow job for this annotation

GitHub Actions / lint

undefined: newDataLinkAppdConfigBadUrlErr (typecheck)

Check failure on line 173 in signalfx/resource_signalfx_data_link_test.go

View workflow job for this annotation

GitHub Actions / test

undefined: newDataLinkAppdConfigBadUrlErr
ExpectError: regexp.MustCompile("Enter a valid AppD Link. The link needs to include the contoller URL, application ID, and Application component."),
},
},
})
}
func TestAccCreateAppdDataLink(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccDataLinkDestroy,
Steps: []resource.TestStep{
{
Config: newDataLinkAppdConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckDataLinkResourceExists,
resource.TestCheckResourceAttr("signalfx_data_link.big_test_data_link", "property_name", "pname"),
resource.TestCheckResourceAttr("signalfx_data_link.big_test_data_link", "property_value", "pvalue"),
),
},
},
})
}

func testDataLinkUpdated(s *terraform.State) error {
for _, rs := range s.RootModule().Resources {
switch rs.Type {
Expand Down
14 changes: 14 additions & 0 deletions website/docs/r/data_link.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ resource "signalfx_data_link" "my_data_link_dash" {
}
}
}
# A link to an AppDynamics Service
resource "signalfx_data_link" "my_data_link_appd" {
property_name = "pname3"
property_value = "pvalue"
target_appd_url {
name = "appd_url"
url = "https://www.example.saas.appdynamics.com/#/application=1234&component=5678"
}
}
```

## Arguments
Expand All @@ -64,6 +75,9 @@ The following arguments are supported in the resource block:
* `target_splunk` - (Optional) Link to an external URL
* `name` (Required) User-assigned target name. Use this value to differentiate between the link targets for a data link object.
* `property_key_mapping` - Describes the relationship between Splunk Observability Cloud metadata keys and external system properties when the key names are different.
* `target_appd_url` - (Optional) Link to an AppDynamics URL
* `name` (Required) User-assigned target name. Use this value to differentiate between the link targets for a data link object.
* `url`- (Required) URL string for an AppDynamics instance.

## Attributes

Expand Down

0 comments on commit e7a73ea

Please sign in to comment.