diff --git a/newrelic/config.go b/newrelic/config.go index da96c64475..7d278967a8 100644 --- a/newrelic/config.go +++ b/newrelic/config.go @@ -4,6 +4,7 @@ import ( "log" "github.com/hashicorp/terraform/helper/logging" + nrinfra "github.com/paul91/go-newrelic-infra/api" newrelic "github.com/paultyng/go-newrelic/api" ) @@ -27,3 +28,24 @@ func (c *Config) Client() (*newrelic.Client, error) { return &client, nil } + +// ClientInfra returns a new client for accessing New Relic +func (c *Config) ClientInfra() (*nrinfra.Client, error) { + nrConfig := nrinfra.Config{ + APIKey: c.APIKey, + Debug: logging.IsDebugOrHigher(), + BaseURL: c.APIURL, + } + + client := nrinfra.New(nrConfig) + + log.Printf("[INFO] New Relic Infra client configured") + + return &client, nil +} + +// ProviderConfig for the custom provider +type ProviderConfig struct { + Client *newrelic.Client + InfraClient *nrinfra.Client +} diff --git a/newrelic/data_source_newrelic_application.go b/newrelic/data_source_newrelic_application.go index e76a787824..491d5838e6 100644 --- a/newrelic/data_source_newrelic_application.go +++ b/newrelic/data_source_newrelic_application.go @@ -33,7 +33,7 @@ func dataSourceNewRelicApplication() *schema.Resource { } func dataSourceNewRelicApplicationRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client log.Printf("[INFO] Reading New Relic applications") diff --git a/newrelic/data_source_newrelic_key_transaction.go b/newrelic/data_source_newrelic_key_transaction.go index f5d0751f91..d1a51836b2 100644 --- a/newrelic/data_source_newrelic_key_transaction.go +++ b/newrelic/data_source_newrelic_key_transaction.go @@ -23,7 +23,7 @@ func dataSourceNewRelicKeyTransaction() *schema.Resource { } func dataSourceNewRelicKeyTransactionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client log.Printf("[INFO] Reading New Relic key transactions") diff --git a/newrelic/provider.go b/newrelic/provider.go index e5e6103373..2772dfd789 100644 --- a/newrelic/provider.go +++ b/newrelic/provider.go @@ -1,6 +1,7 @@ package newrelic import ( + "fmt" "log" "github.com/hashicorp/terraform/helper/schema" @@ -22,6 +23,11 @@ func Provider() terraform.ResourceProvider { Optional: true, DefaultFunc: schema.EnvDefaultFunc("NEWRELIC_API_URL", "https://api.newrelic.com/v2"), }, + "infra_api_url": { + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc("NEWRELIC_INFRA_API_URL", "https://infra-api.newrelic.com/v2"), + }, }, DataSourcesMap: map[string]*schema.Resource{ @@ -30,12 +36,13 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "newrelic_alert_channel": resourceNewRelicAlertChannel(), - "newrelic_alert_condition": resourceNewRelicAlertCondition(), - "newrelic_nrql_alert_condition": resourceNewRelicNrqlAlertCondition(), - "newrelic_alert_policy": resourceNewRelicAlertPolicy(), - "newrelic_alert_policy_channel": resourceNewRelicAlertPolicyChannel(), - "newrelic_dashboard": resourceNewRelicDashboard(), + "newrelic_alert_channel": resourceNewRelicAlertChannel(), + "newrelic_alert_condition": resourceNewRelicAlertCondition(), + "newrelic_nrql_alert_condition": resourceNewRelicNrqlAlertCondition(), + "newrelic_infra_alert_condition": resourceNewRelicInfraAlertCondition(), + "newrelic_alert_policy": resourceNewRelicAlertPolicy(), + "newrelic_alert_policy_channel": resourceNewRelicAlertPolicyChannel(), + "newrelic_dashboard": resourceNewRelicDashboard(), }, ConfigureFunc: providerConfigure, @@ -48,5 +55,27 @@ func providerConfigure(data *schema.ResourceData) (interface{}, error) { APIURL: data.Get("api_url").(string), } log.Println("[INFO] Initializing New Relic client") - return config.Client() + + client, err := config.Client() + if err != nil { + return nil, fmt.Errorf("Error initializing New Relic client: %s", err) + } + + infraConfig := Config{ + APIKey: data.Get("api_key").(string), + APIURL: data.Get("infra_api_url").(string), + } + log.Println("[INFO] Initializing New Relic Infra client") + + clientInfra, err := infraConfig.ClientInfra() + if err != nil { + return nil, fmt.Errorf("Error initializing New Relic Infra client: %s", err) + } + + providerConfig := ProviderConfig{ + Client: client, + InfraClient: clientInfra, + } + + return &providerConfig, nil } diff --git a/newrelic/resource_newrelic_alert_channel.go b/newrelic/resource_newrelic_alert_channel.go index e8a642d2d7..16b55c20d0 100644 --- a/newrelic/resource_newrelic_alert_channel.go +++ b/newrelic/resource_newrelic_alert_channel.go @@ -104,7 +104,7 @@ func buildAlertChannelStruct(d *schema.ResourceData) *newrelic.AlertChannel { } func resourceNewRelicAlertChannelCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client channel := buildAlertChannelStruct(d) log.Printf("[INFO] Creating New Relic alert channel %s", channel.Name) @@ -120,7 +120,7 @@ func resourceNewRelicAlertChannelCreate(d *schema.ResourceData, meta interface{} } func resourceNewRelicAlertChannelRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client id, err := strconv.ParseInt(d.Id(), 10, 32) if err != nil { @@ -149,7 +149,7 @@ func resourceNewRelicAlertChannelRead(d *schema.ResourceData, meta interface{}) } func resourceNewRelicAlertChannelDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client id, err := strconv.ParseInt(d.Id(), 10, 32) if err != nil { diff --git a/newrelic/resource_newrelic_alert_channel_test.go b/newrelic/resource_newrelic_alert_channel_test.go index a062e26ca9..acf2f1f93b 100644 --- a/newrelic/resource_newrelic_alert_channel_test.go +++ b/newrelic/resource_newrelic_alert_channel_test.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - newrelic "github.com/paultyng/go-newrelic/api" ) func TestAccNewRelicAlertChannel_Basic(t *testing.T) { @@ -51,7 +50,7 @@ func TestAccNewRelicAlertChannel_Basic(t *testing.T) { } func testAccCheckNewRelicAlertChannelDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*newrelic.Client) + client := testAccProvider.Meta().(*ProviderConfig).Client for _, r := range s.RootModule().Resources { if r.Type != "newrelic_alert_channel" { continue @@ -82,7 +81,7 @@ func testAccCheckNewRelicAlertChannelExists(n string) resource.TestCheckFunc { return fmt.Errorf("No channel ID is set") } - client := testAccProvider.Meta().(*newrelic.Client) + client := testAccProvider.Meta().(*ProviderConfig).Client id, err := strconv.ParseInt(rs.Primary.ID, 10, 32) if err != nil { diff --git a/newrelic/resource_newrelic_alert_condition.go b/newrelic/resource_newrelic_alert_condition.go index 6472f236b5..0959e52423 100644 --- a/newrelic/resource_newrelic_alert_condition.go +++ b/newrelic/resource_newrelic_alert_condition.go @@ -278,7 +278,7 @@ func readAlertConditionStruct(condition *newrelic.AlertCondition, d *schema.Reso } func resourceNewRelicAlertConditionCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client condition := buildAlertConditionStruct(d) log.Printf("[INFO] Creating New Relic alert condition %s", condition.Name) @@ -294,7 +294,7 @@ func resourceNewRelicAlertConditionCreate(d *schema.ResourceData, meta interface } func resourceNewRelicAlertConditionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client log.Printf("[INFO] Reading New Relic alert condition %s", d.Id()) @@ -320,7 +320,7 @@ func resourceNewRelicAlertConditionRead(d *schema.ResourceData, meta interface{} } func resourceNewRelicAlertConditionUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client condition := buildAlertConditionStruct(d) ids, err := parseIDs(d.Id(), 2) @@ -345,7 +345,7 @@ func resourceNewRelicAlertConditionUpdate(d *schema.ResourceData, meta interface } func resourceNewRelicAlertConditionDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client ids, err := parseIDs(d.Id(), 2) if err != nil { diff --git a/newrelic/resource_newrelic_alert_condition_test.go b/newrelic/resource_newrelic_alert_condition_test.go index a587659401..76089b7d61 100644 --- a/newrelic/resource_newrelic_alert_condition_test.go +++ b/newrelic/resource_newrelic_alert_condition_test.go @@ -7,7 +7,6 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - newrelic "github.com/paultyng/go-newrelic/api" ) func TestAccNewRelicAlertCondition_Basic(t *testing.T) { @@ -111,7 +110,7 @@ func TestAccNewRelicAlertCondition_ZeroThreshold(t *testing.T) { // TODO: func_ TestAccNewRelicAlertCondition_Multi(t *testing.T) { func testAccCheckNewRelicAlertConditionDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*newrelic.Client) + client := testAccProvider.Meta().(*ProviderConfig).Client for _, r := range s.RootModule().Resources { if r.Type != "newrelic_alert_condition" { continue @@ -144,7 +143,7 @@ func testAccCheckNewRelicAlertConditionExists(n string) resource.TestCheckFunc { return fmt.Errorf("No alert condition ID is set") } - client := testAccProvider.Meta().(*newrelic.Client) + client := testAccProvider.Meta().(*ProviderConfig).Client ids, err := parseIDs(rs.Primary.ID, 2) if err != nil { diff --git a/newrelic/resource_newrelic_alert_policy.go b/newrelic/resource_newrelic_alert_policy.go index befc04ceaf..b5ac173d39 100644 --- a/newrelic/resource_newrelic_alert_policy.go +++ b/newrelic/resource_newrelic_alert_policy.go @@ -56,7 +56,7 @@ func buildAlertPolicyStruct(d *schema.ResourceData) *newrelic.AlertPolicy { } func resourceNewRelicAlertPolicyCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client policy := buildAlertPolicyStruct(d) log.Printf("[INFO] Creating New Relic alert policy %s", policy.Name) @@ -72,7 +72,7 @@ func resourceNewRelicAlertPolicyCreate(d *schema.ResourceData, meta interface{}) } func resourceNewRelicAlertPolicyRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client id, err := strconv.ParseInt(d.Id(), 10, 32) if err != nil { @@ -100,7 +100,7 @@ func resourceNewRelicAlertPolicyRead(d *schema.ResourceData, meta interface{}) e } func resourceNewRelicAlertPolicyDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client id, err := strconv.ParseInt(d.Id(), 10, 32) if err != nil { diff --git a/newrelic/resource_newrelic_alert_policy_channel.go b/newrelic/resource_newrelic_alert_policy_channel.go index df3eee640b..d97387e170 100644 --- a/newrelic/resource_newrelic_alert_policy_channel.go +++ b/newrelic/resource_newrelic_alert_policy_channel.go @@ -48,7 +48,7 @@ func resourceNewRelicAlertPolicyChannel() *schema.Resource { } func resourceNewRelicAlertPolicyChannelCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client policyID := d.Get("policy_id").(int) channelID := d.Get("channel_id").(int) @@ -75,7 +75,7 @@ func resourceNewRelicAlertPolicyChannelCreate(d *schema.ResourceData, meta inter } func resourceNewRelicAlertPolicyChannelRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client ids, err := parseIDs(d.Id(), 2) if err != nil { @@ -104,7 +104,7 @@ func resourceNewRelicAlertPolicyChannelRead(d *schema.ResourceData, meta interfa } func resourceNewRelicAlertPolicyChannelDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client ids, err := parseIDs(d.Id(), 2) if err != nil { diff --git a/newrelic/resource_newrelic_alert_policy_channel_test.go b/newrelic/resource_newrelic_alert_policy_channel_test.go index 7caef10df1..f728f8f3e5 100644 --- a/newrelic/resource_newrelic_alert_policy_channel_test.go +++ b/newrelic/resource_newrelic_alert_policy_channel_test.go @@ -7,7 +7,6 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - newrelic "github.com/paultyng/go-newrelic/api" ) func TestAccNewRelicAlertPolicyChannel_Basic(t *testing.T) { @@ -34,7 +33,7 @@ func TestAccNewRelicAlertPolicyChannel_Basic(t *testing.T) { } func testAccCheckNewRelicAlertPolicyChannelDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*newrelic.Client) + client := testAccProvider.Meta().(*ProviderConfig).Client for _, r := range s.RootModule().Resources { if r.Type != "newrelic_alert_policy_channel" { continue @@ -70,7 +69,7 @@ func testAccCheckNewRelicAlertPolicyChannelExists(n string) resource.TestCheckFu return fmt.Errorf("No resource ID is set") } - client := testAccProvider.Meta().(*newrelic.Client) + client := testAccProvider.Meta().(*ProviderConfig).Client ids, err := parseIDs(rs.Primary.ID, 2) if err != nil { diff --git a/newrelic/resource_newrelic_alert_policy_test.go b/newrelic/resource_newrelic_alert_policy_test.go index a76b452eb9..366e64e6ff 100644 --- a/newrelic/resource_newrelic_alert_policy_test.go +++ b/newrelic/resource_newrelic_alert_policy_test.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - newrelic "github.com/paultyng/go-newrelic/api" ) func TestAccNewRelicAlertPolicy_Basic(t *testing.T) { @@ -43,7 +42,7 @@ func TestAccNewRelicAlertPolicy_Basic(t *testing.T) { } func testAccCheckNewRelicAlertPolicyDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*newrelic.Client) + client := testAccProvider.Meta().(*ProviderConfig).Client for _, r := range s.RootModule().Resources { if r.Type != "newrelic_alert_policy" { continue @@ -74,7 +73,7 @@ func testAccCheckNewRelicAlertPolicyExists(n string) resource.TestCheckFunc { return fmt.Errorf("No policy ID is set") } - client := testAccProvider.Meta().(*newrelic.Client) + client := testAccProvider.Meta().(*ProviderConfig).Client id, err := strconv.ParseInt(rs.Primary.ID, 10, 32) if err != nil { diff --git a/newrelic/resource_newrelic_dashboard.go b/newrelic/resource_newrelic_dashboard.go index fd83f3de90..697208be04 100644 --- a/newrelic/resource_newrelic_dashboard.go +++ b/newrelic/resource_newrelic_dashboard.go @@ -208,7 +208,7 @@ func flattenDashboard(dashboard *newrelic.Dashboard, d *schema.ResourceData) err } func resourceNewRelicDashboardCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client dashboard := expandDashboard(d) log.Printf("[INFO] Creating New Relic dashboard: %s", dashboard.Title) @@ -223,7 +223,7 @@ func resourceNewRelicDashboardCreate(d *schema.ResourceData, meta interface{}) e } func resourceNewRelicDashboardRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client log.Printf("[INFO] Reading New Relic dashboard %s", d.Id()) @@ -246,7 +246,7 @@ func resourceNewRelicDashboardRead(d *schema.ResourceData, meta interface{}) err } func resourceNewRelicDashboardUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client dashboard := expandDashboard(d) id, err := strconv.Atoi(d.Id()) @@ -266,7 +266,7 @@ func resourceNewRelicDashboardUpdate(d *schema.ResourceData, meta interface{}) e } func resourceNewRelicDashboardDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client id, err := strconv.Atoi(d.Id()) if err != nil { diff --git a/newrelic/resource_newrelic_dashboard_test.go b/newrelic/resource_newrelic_dashboard_test.go index 074897a676..afb404e992 100644 --- a/newrelic/resource_newrelic_dashboard_test.go +++ b/newrelic/resource_newrelic_dashboard_test.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - newrelic "github.com/paultyng/go-newrelic/api" ) func TestAccNewRelicDashboard_Basic(t *testing.T) { @@ -88,7 +87,7 @@ func TestAccNewRelicDashboard_Basic(t *testing.T) { } func testAccCheckNewRelicDashboardDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*newrelic.Client) + client := testAccProvider.Meta().(*ProviderConfig).Client for _, r := range s.RootModule().Resources { if r.Type != "newrelic_dashboard" { continue @@ -119,7 +118,7 @@ func testAccCheckNewRelicDashboardExists(n string) resource.TestCheckFunc { return fmt.Errorf("No dashboard ID is set") } - client := testAccProvider.Meta().(*newrelic.Client) + client := testAccProvider.Meta().(*ProviderConfig).Client id, err := strconv.ParseInt(rs.Primary.ID, 10, 32) if err != nil { diff --git a/newrelic/resource_newrelic_infra_alert_condition.go b/newrelic/resource_newrelic_infra_alert_condition.go new file mode 100644 index 0000000000..dcf8da38aa --- /dev/null +++ b/newrelic/resource_newrelic_infra_alert_condition.go @@ -0,0 +1,310 @@ +package newrelic + +import ( + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + newrelic "github.com/paul91/go-newrelic-infra/api" +) + +var thresholdConditionTypes = map[string][]string{ + "infra_process_running": { + "duration_minutes", + "value", + }, + "infra_metric": { + "duration_minutes", + "value", + "time_function", + }, + "infra_host_not_reporting": { + "duration_minutes", + }, +} + +// thresholdSchema returns the schema to use for threshold. +// +func thresholdSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Optional: true, + }, + "duration": { + Type: schema.TypeInt, + Required: true, + }, + "time_function": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"any", "all"}, false), + }, + }, + } +} + +func resourceNewRelicInfraAlertCondition() *schema.Resource { + validThresholdConditionTypes := make([]string, 0, len(thresholdConditionTypes)) + for k := range thresholdConditionTypes { + validThresholdConditionTypes = append(validThresholdConditionTypes, k) + } + + return &schema.Resource{ + Create: resourceNewRelicInfraAlertConditionCreate, + Read: resourceNewRelicInfraAlertConditionRead, + Update: resourceNewRelicInfraAlertConditionUpdate, + Delete: resourceNewRelicInfraAlertConditionDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "policy_id": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(validThresholdConditionTypes, false), + }, + "event": { + Type: schema.TypeString, + Optional: true, + }, + "where": { + Type: schema.TypeString, + Optional: true, + }, + "process_where": { + Type: schema.TypeString, + Optional: true, + }, + "comparison": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"above", "below", "equal"}, false), + }, + "select": { + Type: schema.TypeString, + Optional: true, + }, + "created_at": { + Type: schema.TypeInt, + Computed: true, + }, + "updated_at": { + Type: schema.TypeInt, + Computed: true, + }, + "critical": { + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Optional: true, + Elem: thresholdSchema(), + //TODO: ValidateFunc from thresholdConditionTypes map + }, + "warning": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + MinItems: 1, + ForceNew: true, + Elem: thresholdSchema(), + //TODO: ValidateFunc from thresholdConditionTypes map + }, + }, + } +} + +func buildInfraAlertConditionStruct(d *schema.ResourceData) *newrelic.AlertInfraCondition { + + condition := newrelic.AlertInfraCondition{ + Name: d.Get("name").(string), + Enabled: d.Get("enabled").(bool), + PolicyID: d.Get("policy_id").(int), + Event: d.Get("event").(string), + Comparison: d.Get("comparison").(string), + Select: d.Get("select").(string), + Type: d.Get("type").(string), + Critical: expandAlertThreshold(d.Get("critical")), + } + + if attr, ok := d.GetOk("warning"); ok { + condition.Warning = expandAlertThreshold(attr) + } + + if attr, ok := d.GetOk("where"); ok { + condition.Where = attr.(string) + } + + if attr, ok := d.GetOk("process_where"); ok { + condition.ProcessWhere = attr.(string) + } + + return &condition +} + +func readInfraAlertConditionStruct(condition *newrelic.AlertInfraCondition, d *schema.ResourceData) error { + ids, err := parseIDs(d.Id(), 2) + if err != nil { + return err + } + + policyID := ids[0] + + d.Set("policy_id", policyID) + d.Set("name", condition.Name) + d.Set("enabled", condition.Enabled) + d.Set("created_at", condition.CreatedAt) + d.Set("updated_at", condition.UpdatedAt) + + if condition.Where != "" { + d.Set("where", condition.Where) + } + + if condition.ProcessWhere != "" { + d.Set("process_where", condition.ProcessWhere) + } + + if err := d.Set("critical", flattenAlertThreshold(condition.Critical)); err != nil { + return err + } + + if condition.Warning != nil { + if err := d.Set("warning", flattenAlertThreshold(condition.Warning)); err != nil { + return err + } + } + + return nil +} + +func resourceNewRelicInfraAlertConditionCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ProviderConfig).InfraClient + condition := buildInfraAlertConditionStruct(d) + + log.Printf("[INFO] Creating New Relic Infra alert condition %s", condition.Name) + + condition, err := client.CreateAlertInfraCondition(*condition) + if err != nil { + return err + } + + d.SetId(serializeIDs([]int{condition.PolicyID, condition.ID})) + + return resourceNewRelicInfraAlertConditionRead(d, meta) +} + +func resourceNewRelicInfraAlertConditionRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ProviderConfig).InfraClient + + log.Printf("[INFO] Reading New Relic Infra alert condition %s", d.Id()) + + ids, err := parseIDs(d.Id(), 2) + if err != nil { + return err + } + + policyID := ids[0] + id := ids[1] + + condition, err := client.GetAlertInfraCondition(policyID, id) + if err != nil { + if err == newrelic.ErrNotFound { + d.SetId("") + return nil + } + + return err + } + + return readInfraAlertConditionStruct(condition, d) +} + +func resourceNewRelicInfraAlertConditionUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ProviderConfig).InfraClient + condition := buildInfraAlertConditionStruct(d) + + ids, err := parseIDs(d.Id(), 2) + if err != nil { + return err + } + + policyID := ids[0] + id := ids[1] + + condition.PolicyID = policyID + condition.ID = id + + log.Printf("[INFO] Updating New Relic Infra alert condition %d", id) + + _, err = client.UpdateAlertInfraCondition(*condition) + if err != nil { + return err + } + + return resourceNewRelicInfraAlertConditionRead(d, meta) +} + +func resourceNewRelicInfraAlertConditionDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ProviderConfig).InfraClient + + ids, err := parseIDs(d.Id(), 2) + if err != nil { + return err + } + + policyID := ids[0] + id := ids[1] + + log.Printf("[INFO] Deleting New Relic Infra alert condition %d", id) + + if err := client.DeleteAlertInfraCondition(policyID, id); err != nil { + return err + } + + d.SetId("") + + return nil +} + +func expandAlertThreshold(v interface{}) *newrelic.AlertInfraThreshold { + rah := v.([]interface{})[0].(map[string]interface{}) + alertInfraThreshold := &newrelic.AlertInfraThreshold{ + Duration: rah["duration"].(int), + } + + if val, ok := rah["value"]; ok { + alertInfraThreshold.Value = val.(int) + } + + if val, ok := rah["time_function"]; ok { + alertInfraThreshold.Function = val.(string) + } + + return alertInfraThreshold +} + +func flattenAlertThreshold(v *newrelic.AlertInfraThreshold) []interface{} { + alertInfraThreshold := map[string]interface{}{ + "duration": v.Duration, + "value": v.Value, + "time_function": v.Function, + } + + return []interface{}{alertInfraThreshold} +} diff --git a/newrelic/resource_newrelic_infra_alert_condition_test.go b/newrelic/resource_newrelic_infra_alert_condition_test.go new file mode 100644 index 0000000000..c486913f47 --- /dev/null +++ b/newrelic/resource_newrelic_infra_alert_condition_test.go @@ -0,0 +1,295 @@ +package newrelic + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccNewRelicInfraAlertCondition_Basic(t *testing.T) { + rName := acctest.RandString(5) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNewRelicInfraAlertConditionDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckNewRelicInfraAlertConditionConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckNewRelicInfraAlertConditionExists("newrelic_infra_alert_condition.foo"), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "name", fmt.Sprintf("tf-test-%s", rName)), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "critical.0.duration", "10"), + ), + }, + resource.TestStep{ + Config: testAccCheckNewRelicInfraAlertConditionConfigUpdated(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckNewRelicInfraAlertConditionExists("newrelic_infra_alert_condition.foo"), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "name", fmt.Sprintf("tf-test-updated-%s", rName)), + ), + }, + }, + }) +} + +func TestAccNewRelicInfraAlertCondition_Where(t *testing.T) { + rName := acctest.RandString(5) + whereClause := "(`hostname` LIKE '%cassandra%')" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNewRelicInfraAlertConditionDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckNewRelicInfraAlertConditionConfigWithWhere(rName, whereClause), + Check: resource.ComposeTestCheckFunc( + testAccCheckNewRelicInfraAlertConditionExists("newrelic_infra_alert_condition.foo"), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "name", fmt.Sprintf("tf-test-%s", rName)), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "critical.0.duration", "10"), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "critical.0.value", "0"), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "where", whereClause), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "process_where", "commandName = 'java'"), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "comparison", "equal"), + ), + }, + }, + }) +} + +func TestAccNewRelicInfraAlertCondition_Thresholds(t *testing.T) { + rName := acctest.RandString(5) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNewRelicInfraAlertConditionDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckNewRelicInfraAlertConditionConfigWithThreshold(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckNewRelicInfraAlertConditionExists("newrelic_infra_alert_condition.foo"), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "name", fmt.Sprintf("tf-test-%s", rName)), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "critical.0.duration", "10"), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "critical.0.value", "10"), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "critical.0.time_function", "any"), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "warning.0.value", "20"), + ), + }, + resource.TestStep{ + Config: testAccCheckNewRelicInfraAlertConditionConfigWithThresholdUpdated(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckNewRelicInfraAlertConditionExists("newrelic_infra_alert_condition.foo"), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "name", fmt.Sprintf("tf-test-%s", rName)), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "critical.0.duration", "20"), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "critical.0.value", "15"), + resource.TestCheckResourceAttr( + "newrelic_infra_alert_condition.foo", "critical.0.time_function", "all"), + resource.TestCheckNoResourceAttr( + "newrelic_infra_alert_condition.foo", "warning"), + ), + }, + }, + }) +} + +func testAccCheckNewRelicInfraAlertConditionDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ProviderConfig).InfraClient + for _, r := range s.RootModule().Resources { + if r.Type != "newrelic_infra_alert_condition" { + continue + } + + ids, err := parseIDs(r.Primary.ID, 2) + if err != nil { + return err + } + + policyID := ids[0] + id := ids[1] + + _, err = client.GetAlertInfraCondition(policyID, id) + if err == nil { + return fmt.Errorf("Infra Alert condition still exists") + } + + } + return nil +} + +func testAccCheckNewRelicInfraAlertConditionExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No alert condition ID is set") + } + + client := testAccProvider.Meta().(*ProviderConfig).InfraClient + + ids, err := parseIDs(rs.Primary.ID, 2) + if err != nil { + return err + } + + policyID := ids[0] + id := ids[1] + + found, err := client.GetAlertInfraCondition(policyID, id) + if err != nil { + return err + } + + if found.ID != id { + return fmt.Errorf("Alert condition not found: %v - %v", id, found) + } + + return nil + } +} + +func testAccCheckNewRelicInfraAlertConditionConfig(rName string) string { + return fmt.Sprintf(` + +resource "newrelic_alert_policy" "foo" { + name = "tf-test-%[1]s" +} + +resource "newrelic_infra_alert_condition" "foo" { + policy_id = "${newrelic_alert_policy.foo.id}" + + name = "tf-test-%[1]s" + type = "infra_metric" + event = "StorageSample" + select = "diskFreePercent" + comparison = "below" + + critical { + duration = 10 + value = 10 + time_function = "any" + } +} +`, rName) +} + +func testAccCheckNewRelicInfraAlertConditionConfigUpdated(rName string) string { + return fmt.Sprintf(` +resource "newrelic_alert_policy" "foo" { + name = "tf-test-%[1]s" +} + +resource "newrelic_infra_alert_condition" "foo" { + policy_id = "${newrelic_alert_policy.foo.id}" + + name = "tf-test-updated-%[1]s" + type = "infra_metric" + event = "StorageSample" + select = "diskFreePercent" + comparison = "below" + + critical { + duration = 10 + value = 10 + time_function = "any" + } +} +`, rName) +} + +func testAccCheckNewRelicInfraAlertConditionConfigWithThreshold(rName string) string { + return fmt.Sprintf(` +resource "newrelic_alert_policy" "foo" { + name = "tf-test-%[1]s" +} + +resource "newrelic_infra_alert_condition" "foo" { + policy_id = "${newrelic_alert_policy.foo.id}" + + name = "tf-test-%[1]s" + type = "infra_metric" + event = "StorageSample" + select = "diskFreePercent" + comparison = "below" + + critical { + duration = 10 + value = 10 + time_function = "any" + } + + warning { + duration = 10 + value = 20 + time_function = "any" + } +} +`, rName) +} + +func testAccCheckNewRelicInfraAlertConditionConfigWithThresholdUpdated(rName string) string { + return fmt.Sprintf(` +resource "newrelic_alert_policy" "foo" { + name = "tf-test-%[1]s" +} + +resource "newrelic_infra_alert_condition" "foo" { + policy_id = "${newrelic_alert_policy.foo.id}" + + name = "tf-test-%[1]s" + type = "infra_metric" + event = "StorageSample" + select = "diskFreePercent" + comparison = "below" + + critical { + duration = 20 + value = 15 + time_function = "all" + } +} +`, rName) +} + +func testAccCheckNewRelicInfraAlertConditionConfigWithWhere(rName, where string) string { + return fmt.Sprintf(` +resource "newrelic_alert_policy" "foo" { + name = "tf-test-%[1]s" +} + +resource "newrelic_infra_alert_condition" "foo" { + policy_id = "${newrelic_alert_policy.foo.id}" + + name = "tf-test-%[1]s" + type = "infra_process_running" + process_where = "commandName = 'java'" + comparison = "equal" + where = "%[2]s" + + critical { + duration = 10 + value = 0 + } +} +`, rName, where) +} diff --git a/newrelic/resource_newrelic_nrql_alert_condition.go b/newrelic/resource_newrelic_nrql_alert_condition.go index 5d93a262b6..8fb800956f 100644 --- a/newrelic/resource_newrelic_nrql_alert_condition.go +++ b/newrelic/resource_newrelic_nrql_alert_condition.go @@ -182,7 +182,7 @@ func readNrqlAlertConditionStruct(condition *newrelic.AlertNrqlCondition, d *sch } func resourceNewRelicNrqlAlertConditionCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client condition := buildNrqlAlertConditionStruct(d) log.Printf("[INFO] Creating New Relic NRQL alert condition %s", condition.Name) @@ -198,7 +198,7 @@ func resourceNewRelicNrqlAlertConditionCreate(d *schema.ResourceData, meta inter } func resourceNewRelicNrqlAlertConditionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client log.Printf("[INFO] Reading New Relic NRQL alert condition %s", d.Id()) @@ -224,7 +224,7 @@ func resourceNewRelicNrqlAlertConditionRead(d *schema.ResourceData, meta interfa } func resourceNewRelicNrqlAlertConditionUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client condition := buildNrqlAlertConditionStruct(d) ids, err := parseIDs(d.Id(), 2) @@ -249,7 +249,7 @@ func resourceNewRelicNrqlAlertConditionUpdate(d *schema.ResourceData, meta inter } func resourceNewRelicNrqlAlertConditionDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*newrelic.Client) + client := meta.(*ProviderConfig).Client ids, err := parseIDs(d.Id(), 2) if err != nil { diff --git a/newrelic/resource_newrelic_nrql_alert_condition_test.go b/newrelic/resource_newrelic_nrql_alert_condition_test.go index 552ec5336a..75265ab6a0 100644 --- a/newrelic/resource_newrelic_nrql_alert_condition_test.go +++ b/newrelic/resource_newrelic_nrql_alert_condition_test.go @@ -7,7 +7,6 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - newrelic "github.com/paultyng/go-newrelic/api" ) func TestAccNewRelicNrqlAlertCondition_Basic(t *testing.T) { @@ -84,7 +83,7 @@ func TestAccNewRelicNrqlAlertCondition_Basic(t *testing.T) { // TODO: func_ TestAccNewRelicNrqlAlertCondition_Multi(t *testing.T) { func testAccCheckNewRelicNrqlAlertConditionDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*newrelic.Client) + client := testAccProvider.Meta().(*ProviderConfig).Client for _, r := range s.RootModule().Resources { if r.Type != "newrelic_nrql_alert_condition" { continue @@ -117,7 +116,7 @@ func testAccCheckNewRelicNrqlAlertConditionExists(n string) resource.TestCheckFu return fmt.Errorf("No alert condition ID is set") } - client := testAccProvider.Meta().(*newrelic.Client) + client := testAccProvider.Meta().(*ProviderConfig).Client ids, err := parseIDs(rs.Primary.ID, 2) if err != nil { diff --git a/vendor/github.com/paul91/go-newrelic-infra/LICENSE b/vendor/github.com/paul91/go-newrelic-infra/LICENSE new file mode 100644 index 0000000000..e04f2d0b9c --- /dev/null +++ b/vendor/github.com/paul91/go-newrelic-infra/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 Paul Tyng + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/paul91/go-newrelic-infra/api/alert_infra_conditions.go b/vendor/github.com/paul91/go-newrelic-infra/api/alert_infra_conditions.go new file mode 100644 index 0000000000..3fd5aa5c90 --- /dev/null +++ b/vendor/github.com/paul91/go-newrelic-infra/api/alert_infra_conditions.go @@ -0,0 +1,122 @@ +package api + +import ( + "fmt" + "net/url" + "strconv" +) + +func (c *Client) queryAlertInfraConditions(policyID int) ([]AlertInfraCondition, error) { + conditions := []AlertInfraCondition{} + + // TODO: CHANGE TO INFRA ENDPOINT + reqURL, err := url.Parse("/alerts/conditions") + if err != nil { + return nil, err + } + + qs := reqURL.Query() + qs.Set("policy_id", strconv.Itoa(policyID)) + + reqURL.RawQuery = qs.Encode() + + nextPath := reqURL.String() + + for nextPath != "" { + resp := struct { + InfraConditions []AlertInfraCondition `json:"data,omitempty"` + }{} + + nextPath, err = c.Do("GET", nextPath, nil, &resp) + if err != nil { + return nil, err + } + + for _, c := range resp.InfraConditions { + c.PolicyID = policyID + } + + conditions = append(conditions, resp.InfraConditions...) + } + + return conditions, nil +} + +// GetAlertInfraCondition gets information about a Infra alert condition given an ID and policy ID. +func (c *Client) GetAlertInfraCondition(policyID int, id int) (*AlertInfraCondition, error) { + conditions, err := c.queryAlertInfraConditions(policyID) + if err != nil { + return nil, err + } + + for _, condition := range conditions { + if condition.ID == id { + return &condition, nil + } + } + + return nil, ErrNotFound +} + +// ListAlertInfraConditions returns Infra alert conditions for the specified policy. +func (c *Client) ListAlertInfraConditions(policyID int) ([]AlertInfraCondition, error) { + return c.queryAlertInfraConditions(policyID) +} + +// CreateAlertInfraCondition creates an Infra alert condition given the passed configuration. +func (c *Client) CreateAlertInfraCondition(condition AlertInfraCondition) (*AlertInfraCondition, error) { + policyID := condition.PolicyID + + req := struct { + Condition AlertInfraCondition `json:"data"` + }{ + Condition: condition, + } + + resp := struct { + Condition AlertInfraCondition `json:"data,omitempty"` + }{} + + u := &url.URL{Path: "/alerts/conditions"} + _, err := c.Do("POST", u.String(), req, &resp) + if err != nil { + return nil, err + } + + resp.Condition.PolicyID = policyID + + return &resp.Condition, nil +} + +// UpdateAlertInfraCondition updates an Infra alert condition with the specified changes. +func (c *Client) UpdateAlertInfraCondition(condition AlertInfraCondition) (*AlertInfraCondition, error) { + policyID := condition.PolicyID + id := condition.ID + + req := struct { + Condition AlertInfraCondition `json:"data"` + }{ + Condition: condition, + } + + resp := struct { + Condition AlertInfraCondition `json:"data,omitempty"` + }{} + + u := &url.URL{Path: fmt.Sprintf("/alerts/conditions/%v", id)} + _, err := c.Do("PUT", u.String(), req, &resp) + if err != nil { + return nil, err + } + + resp.Condition.PolicyID = policyID + + return &resp.Condition, nil +} + +// DeleteAlertInfraCondition removes the Infra alert condition given the specified ID and policy ID. +func (c *Client) DeleteAlertInfraCondition(policyID int, id int) error { + u := &url.URL{Path: fmt.Sprintf("/alerts/conditions/%v", id)} + _, err := c.Do("DELETE", u.String(), nil, nil) + return err +} diff --git a/vendor/github.com/paul91/go-newrelic-infra/api/client.go b/vendor/github.com/paul91/go-newrelic-infra/api/client.go new file mode 100644 index 0000000000..5a2e21cf02 --- /dev/null +++ b/vendor/github.com/paul91/go-newrelic-infra/api/client.go @@ -0,0 +1,116 @@ +package api + +import ( + "crypto/tls" + "fmt" + + "github.com/tomnomnom/linkheader" + + resty "gopkg.in/resty.v0" +) + +// Client represents the client state for the API. +type Client struct { + RestyClient *resty.Client +} + +// ErrorResponse represents an error response from New Relic. +type ErrorResponse struct { + Detail *ErrorDetail `json:"error,omitempty"` +} + +func (e *ErrorResponse) Error() string { + if e != nil && e.Detail != nil { + return e.Detail.Title + } + return "Unknown error" +} + +// ErrorDetail represents the details of an ErrorResponse from New Relic. +type ErrorDetail struct { + Title string `json:"title,omitempty"` +} + +// Config contains all the configuration data for the API Client +type Config struct { + APIKey string + BaseURL string + Debug bool + TLSConfig *tls.Config +} + +// New returns a new Client for the specified apiKey. +func New(config Config) Client { + r := resty.New() + + baseURL := config.BaseURL + if baseURL == "" { + baseURL = "https://infra-api.newrelic.com/v2" + } + + r.SetHeader("X-Api-Key", config.APIKey) + r.SetHostURL(baseURL) + + if config.TLSConfig != nil { + r.SetTLSClientConfig(config.TLSConfig) + } + if config.Debug { + r.SetDebug(true) + } + + c := Client{ + RestyClient: r, + } + + return c +} + +// Do exectes an API request with the specified parameters. +func (c *Client) Do(method string, path string, body interface{}, response interface{}) (string, error) { + r := c.RestyClient.R(). + SetError(&ErrorResponse{}). + SetHeader("Content-Type", "application/json") + + if body != nil { + r = r.SetBody(body) + } + + if response != nil { + r = r.SetResult(response) + } + + apiResponse, err := r.Execute(method, path) + + if err != nil { + return "", err + } + + nextPath := "" + header := apiResponse.Header().Get("Link") + if header != "" { + links := linkheader.Parse(header) + + for _, link := range links.FilterByRel("next") { + nextPath = link.URL + break + } + } + + statusClass := apiResponse.StatusCode() / 100 % 10 + + if statusClass == 2 { + return nextPath, nil + } + + rawError := apiResponse.Error() + + if rawError != nil { + apiError := rawError.(*ErrorResponse) + + if apiError.Detail != nil { + return "", apiError + } + } + + return "", fmt.Errorf("Unexpected status %v returned from API", apiResponse.StatusCode()) +} diff --git a/vendor/github.com/paul91/go-newrelic-infra/api/types.go b/vendor/github.com/paul91/go-newrelic-infra/api/types.go new file mode 100644 index 0000000000..b1305f20ee --- /dev/null +++ b/vendor/github.com/paul91/go-newrelic-infra/api/types.go @@ -0,0 +1,33 @@ +package api + +import "errors" + +var ( + // ErrNotFound is returned when the resource was not found in New Relic. + ErrNotFound = errors.New("newrelic-infra: Resource not found") +) + +// AlertInfraThreshold represents an Infra alerting condition +type AlertInfraThreshold struct { + Value int `json:"value,omitempty"` + Duration int `json:"duration_minutes,omitempty"` + Function string `json:"time_function,omitempty"` +} + +// AlertInfraCondition represents a New Relic Infra Alert condition. +type AlertInfraCondition struct { + PolicyID int `json:"policy_id,omitempty"` + ID int `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + Comparison string `json:"comparison,omitempty"` + CreatedAt int `json:"created_at_epoch_millis,omitempty"` + UpdatedAt int `json:"updated_at_epoch_millis,omitempty"` + Enabled bool `json:"enabled,omitempty"` + Event string `json:"event_type,omitempty"` + Select string `json:"select_value,omitempty"` + Where string `json:"where_clause,omitempty"` + ProcessWhere string `json:"process_where_clause,omitempty"` + Warning *AlertInfraThreshold `json:"warning_threshold,omitempty"` + Critical *AlertInfraThreshold `json:"critical_threshold,omitempty"` +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 525def9581..165d9040f4 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -603,6 +603,12 @@ "revision": "7d12ae2201fc160e486197614a6f65afcf3f8170", "revisionTime": "2016-11-16T22:44:47Z" }, + { + "checksumSHA1": "A4ukEy0b231dZFyWkMmluFwgtdc=", + "path": "github.com/paul91/go-newrelic-infra/api", + "revision": "6fa45b9900dbfbc6cb6c48943478425c52264f79", + "revisionTime": "2018-03-27T16:02:37Z" + }, { "checksumSHA1": "8UonU0IfV3BjknE1g2mhZ4rTwBg=", "path": "github.com/paultyng/go-newrelic/api", diff --git a/website/docs/r/infra_alert_condition.html.markdown b/website/docs/r/infra_alert_condition.html.markdown new file mode 100644 index 0000000000..c028f6024d --- /dev/null +++ b/website/docs/r/infra_alert_condition.html.markdown @@ -0,0 +1,63 @@ +--- +layout: "newrelic" +page_title: "New Relic: newrelic_infra_alert_condition" +sidebar_current: "docs-newrelic-resource-infra-alert-condition" +description: |- + Create and manage an Infrastructure alert condition for a policy in New Relic. +--- + +# newrelic\_infra_alert\_condition + +## Example Usage + +```hcl +resource "newrelic_alert_policy" "foo" { + name = "foo" +} + +resource "newrelic_infra_alert_condition" "foo" { + policy_id = "${newrelic_alert_policy.foo.id}" + + name = "High disk usage" + type = "infra_metric" + event = "StorageSample" + select = "diskUsedPercent" + comparison = "above" + + critical { + duration = 25 + value = 90 + time_function = "all" + } +} +``` + +## Argument Reference + +The following arguments are supported: + + * `policy_id` - (Required) The ID of the alert policy where this condition should be used. + * `name` - (Required) The Infrastructure alert condition's name. + * `enabled` - (Optional) Set whether to enable the alert condition. Defaults to `true`. + * `type` - (Required) The type of Infrastructure alert condition: "infra_process_running", "infra_metric", or "infra_host_not_reporting". + * `event` - (Required) The metric event; for example, system metrics, process metrics, storage metrics, or network metrics. + * `select` - (Required) The attribute name to identify the type of metric condition; for example, "network", "process", "system", or "storage". + * `comparison` - (Required) The operator used to evaluate the threshold value; "above", "below", "equal". + * `critical` - (Required) Identifies the critical threshold parameters for triggering an alert notification. See [Thresholds](#thresholds) below for details. + * `warning` - (Optional) Identifies the warning threshold parameters for triggering an alert notification. See [Thresholds](#thresholds) below for details. + * `where` - (Optional) Infrastructure host filter for the alert condition. + * `process_where` - (Optional) Any filters applied to processes; for example: `"commandName = 'java'"`. + +## Thresholds + +The `critical` and `warning` threshold mapping supports the following arguments: + + * `duration` - (Required) Identifies the number of minutes the threshold must be passed or met for the alert to trigger. + * `value` - (Optional) Threshold value, computed against the `comparison` operator. Supported by "infra_metric" and "infra_process_running" alert condition types. + * `time_function` - (Optional) Indicates if the condition needs to be sustained or to just break the threshold once; `all` or `any`. Supported by the "infra_metric" alert condition type. + +## Attributes Reference + +The following attributes are exported: + + * `id` - The ID of the Infrastructure alert condition. diff --git a/website/newrelic.erb b/website/newrelic.erb index f4bd0af39d..f5f0321600 100644 --- a/website/newrelic.erb +++ b/website/newrelic.erb @@ -40,6 +40,9 @@ > newrelic_nrql_alert_condition + > + newrelic_nrql_alert_condition + > newrelic_dashboard