github.com/nevins-b/terraform@v0.3.8-0.20170215184714-bbae22007d5a/builtin/providers/datadog/resource_datadog_monitor.go (about)

     1  package datadog
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"log"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/hashicorp/terraform/helper/schema"
    11  	"github.com/zorkian/go-datadog-api"
    12  )
    13  
    14  func resourceDatadogMonitor() *schema.Resource {
    15  	return &schema.Resource{
    16  		Create: resourceDatadogMonitorCreate,
    17  		Read:   resourceDatadogMonitorRead,
    18  		Update: resourceDatadogMonitorUpdate,
    19  		Delete: resourceDatadogMonitorDelete,
    20  		Exists: resourceDatadogMonitorExists,
    21  		Importer: &schema.ResourceImporter{
    22  			State: resourceDatadogMonitorImport,
    23  		},
    24  
    25  		Schema: map[string]*schema.Schema{
    26  			"name": {
    27  				Type:     schema.TypeString,
    28  				Required: true,
    29  			},
    30  			"message": {
    31  				Type:     schema.TypeString,
    32  				Required: true,
    33  				StateFunc: func(val interface{}) string {
    34  					return strings.TrimSpace(val.(string))
    35  				},
    36  			},
    37  			"escalation_message": {
    38  				Type:     schema.TypeString,
    39  				Optional: true,
    40  				StateFunc: func(val interface{}) string {
    41  					return strings.TrimSpace(val.(string))
    42  				},
    43  			},
    44  			"query": {
    45  				Type:     schema.TypeString,
    46  				Required: true,
    47  				StateFunc: func(val interface{}) string {
    48  					return strings.TrimSpace(val.(string))
    49  				},
    50  			},
    51  			"type": {
    52  				Type:     schema.TypeString,
    53  				Required: true,
    54  			},
    55  
    56  			// Options
    57  			"thresholds": {
    58  				Type:     schema.TypeMap,
    59  				Optional: true,
    60  				Elem: &schema.Resource{
    61  					Schema: map[string]*schema.Schema{
    62  						"ok": {
    63  							Type:     schema.TypeFloat,
    64  							Optional: true,
    65  						},
    66  						"warning": {
    67  							Type:     schema.TypeFloat,
    68  							Optional: true,
    69  						},
    70  						"critical": {
    71  							Type:     schema.TypeFloat,
    72  							Optional: true,
    73  						},
    74  					},
    75  				},
    76  				DiffSuppressFunc: suppressDataDogFloatIntDiff,
    77  			},
    78  			"notify_no_data": {
    79  				Type:     schema.TypeBool,
    80  				Optional: true,
    81  				Default:  false,
    82  			},
    83  			"no_data_timeframe": {
    84  				Type:     schema.TypeInt,
    85  				Optional: true,
    86  			},
    87  			"renotify_interval": {
    88  				Type:     schema.TypeInt,
    89  				Optional: true,
    90  			},
    91  			"notify_audit": {
    92  				Type:     schema.TypeBool,
    93  				Optional: true,
    94  			},
    95  			"timeout_h": {
    96  				Type:     schema.TypeInt,
    97  				Optional: true,
    98  			},
    99  			"require_full_window": {
   100  				Type:     schema.TypeBool,
   101  				Optional: true,
   102  			},
   103  			"locked": {
   104  				Type:     schema.TypeBool,
   105  				Optional: true,
   106  			},
   107  			// TODO should actually be map[string]int
   108  			"silenced": {
   109  				Type:     schema.TypeMap,
   110  				Optional: true,
   111  				Elem: &schema.Schema{
   112  					Type: schema.TypeString,
   113  					Elem: &schema.Schema{
   114  						Type: schema.TypeInt},
   115  				},
   116  			},
   117  			"include_tags": {
   118  				Type:     schema.TypeBool,
   119  				Optional: true,
   120  			},
   121  			"tags": {
   122  				Type:     schema.TypeList,
   123  				Optional: true,
   124  				Elem:     &schema.Schema{Type: schema.TypeString},
   125  			},
   126  		},
   127  	}
   128  }
   129  
   130  func buildMonitorStruct(d *schema.ResourceData) *datadog.Monitor {
   131  
   132  	var thresholds datadog.ThresholdCount
   133  
   134  	if r, ok := d.GetOk("thresholds.ok"); ok {
   135  		thresholds.Ok = json.Number(r.(string))
   136  	}
   137  	if r, ok := d.GetOk("thresholds.warning"); ok {
   138  		thresholds.Warning = json.Number(r.(string))
   139  	}
   140  	if r, ok := d.GetOk("thresholds.critical"); ok {
   141  		thresholds.Critical = json.Number(r.(string))
   142  	}
   143  
   144  	o := datadog.Options{
   145  		Thresholds:   thresholds,
   146  		NotifyNoData: d.Get("notify_no_data").(bool),
   147  	}
   148  	if attr, ok := d.GetOk("silenced"); ok {
   149  		s := make(map[string]int)
   150  		// TODO: this is not very defensive, test if we can fail on non int input
   151  		for k, v := range attr.(map[string]interface{}) {
   152  			s[k], _ = strconv.Atoi(v.(string))
   153  		}
   154  		o.Silenced = s
   155  	}
   156  	if attr, ok := d.GetOk("no_data_timeframe"); ok {
   157  		o.NoDataTimeframe = datadog.NoDataTimeframe(attr.(int))
   158  	}
   159  	if attr, ok := d.GetOk("renotify_interval"); ok {
   160  		o.RenotifyInterval = attr.(int)
   161  	}
   162  	if attr, ok := d.GetOk("notify_audit"); ok {
   163  		o.NotifyAudit = attr.(bool)
   164  	}
   165  	if attr, ok := d.GetOk("timeout_h"); ok {
   166  		o.TimeoutH = attr.(int)
   167  	}
   168  	if attr, ok := d.GetOk("escalation_message"); ok {
   169  		o.EscalationMessage = attr.(string)
   170  	}
   171  	if attr, ok := d.GetOk("include_tags"); ok {
   172  		o.IncludeTags = attr.(bool)
   173  	}
   174  	if attr, ok := d.GetOk("require_full_window"); ok {
   175  		o.RequireFullWindow = attr.(bool)
   176  	}
   177  	if attr, ok := d.GetOk("locked"); ok {
   178  		o.Locked = attr.(bool)
   179  	}
   180  
   181  	m := datadog.Monitor{
   182  		Type:    d.Get("type").(string),
   183  		Query:   d.Get("query").(string),
   184  		Name:    d.Get("name").(string),
   185  		Message: d.Get("message").(string),
   186  		Options: o,
   187  	}
   188  
   189  	if attr, ok := d.GetOk("tags"); ok {
   190  		tags := []string{}
   191  		for _, s := range attr.([]interface{}) {
   192  			tags = append(tags, s.(string))
   193  		}
   194  		m.Tags = tags
   195  	}
   196  
   197  	return &m
   198  }
   199  
   200  func resourceDatadogMonitorExists(d *schema.ResourceData, meta interface{}) (b bool, e error) {
   201  	// Exists - This is called to verify a resource still exists. It is called prior to Read,
   202  	// and lowers the burden of Read to be able to assume the resource exists.
   203  	client := meta.(*datadog.Client)
   204  
   205  	i, err := strconv.Atoi(d.Id())
   206  	if err != nil {
   207  		return false, err
   208  	}
   209  
   210  	if _, err = client.GetMonitor(i); err != nil {
   211  		if strings.Contains(err.Error(), "404 Not Found") {
   212  			return false, nil
   213  		}
   214  		return false, err
   215  	}
   216  
   217  	return true, nil
   218  }
   219  
   220  func resourceDatadogMonitorCreate(d *schema.ResourceData, meta interface{}) error {
   221  
   222  	client := meta.(*datadog.Client)
   223  
   224  	m := buildMonitorStruct(d)
   225  	m, err := client.CreateMonitor(m)
   226  	if err != nil {
   227  		return fmt.Errorf("error updating monitor: %s", err.Error())
   228  	}
   229  
   230  	d.SetId(strconv.Itoa(m.Id))
   231  
   232  	return nil
   233  }
   234  
   235  func resourceDatadogMonitorRead(d *schema.ResourceData, meta interface{}) error {
   236  	client := meta.(*datadog.Client)
   237  
   238  	i, err := strconv.Atoi(d.Id())
   239  	if err != nil {
   240  		return err
   241  	}
   242  
   243  	m, err := client.GetMonitor(i)
   244  	if err != nil {
   245  		return err
   246  	}
   247  
   248  	thresholds := make(map[string]string)
   249  	for k, v := range map[string]json.Number{
   250  		"ok":       m.Options.Thresholds.Ok,
   251  		"warning":  m.Options.Thresholds.Warning,
   252  		"critical": m.Options.Thresholds.Critical,
   253  	} {
   254  		s := v.String()
   255  		if s != "" {
   256  			thresholds[k] = s
   257  		}
   258  	}
   259  
   260  	tags := []string{}
   261  	for _, s := range m.Tags {
   262  		tags = append(tags, s)
   263  	}
   264  
   265  	log.Printf("[DEBUG] monitor: %v", m)
   266  	d.Set("name", m.Name)
   267  	d.Set("message", m.Message)
   268  	d.Set("query", m.Query)
   269  	d.Set("type", m.Type)
   270  	d.Set("thresholds", thresholds)
   271  	d.Set("notify_no_data", m.Options.NotifyNoData)
   272  	d.Set("no_data_timeframe", m.Options.NoDataTimeframe)
   273  	d.Set("renotify_interval", m.Options.RenotifyInterval)
   274  	d.Set("notify_audit", m.Options.NotifyAudit)
   275  	d.Set("timeout_h", m.Options.TimeoutH)
   276  	d.Set("escalation_message", m.Options.EscalationMessage)
   277  	d.Set("silenced", m.Options.Silenced)
   278  	d.Set("include_tags", m.Options.IncludeTags)
   279  	d.Set("tags", tags)
   280  	d.Set("require_full_window", m.Options.RequireFullWindow)
   281  	d.Set("locked", m.Options.Locked)
   282  
   283  	return nil
   284  }
   285  
   286  func resourceDatadogMonitorUpdate(d *schema.ResourceData, meta interface{}) error {
   287  	client := meta.(*datadog.Client)
   288  
   289  	m := &datadog.Monitor{}
   290  
   291  	i, err := strconv.Atoi(d.Id())
   292  	if err != nil {
   293  		return err
   294  	}
   295  
   296  	m.Id = i
   297  	if attr, ok := d.GetOk("name"); ok {
   298  		m.Name = attr.(string)
   299  	}
   300  	if attr, ok := d.GetOk("message"); ok {
   301  		m.Message = attr.(string)
   302  	}
   303  	if attr, ok := d.GetOk("query"); ok {
   304  		m.Query = attr.(string)
   305  	}
   306  
   307  	if attr, ok := d.GetOk("tags"); ok {
   308  		s := make([]string, 0)
   309  		for _, v := range attr.([]interface{}) {
   310  			s = append(s, v.(string))
   311  		}
   312  		m.Tags = s
   313  	}
   314  
   315  	o := datadog.Options{
   316  		NotifyNoData: d.Get("notify_no_data").(bool),
   317  	}
   318  	if attr, ok := d.GetOk("thresholds"); ok {
   319  		thresholds := attr.(map[string]interface{})
   320  		if thresholds["ok"] != nil {
   321  			o.Thresholds.Ok = json.Number(thresholds["ok"].(string))
   322  		}
   323  		if thresholds["warning"] != nil {
   324  			o.Thresholds.Warning = json.Number(thresholds["warning"].(string))
   325  		}
   326  		if thresholds["critical"] != nil {
   327  			o.Thresholds.Critical = json.Number(thresholds["critical"].(string))
   328  		}
   329  	}
   330  
   331  	if attr, ok := d.GetOk("no_data_timeframe"); ok {
   332  		o.NoDataTimeframe = datadog.NoDataTimeframe(attr.(int))
   333  	}
   334  	if attr, ok := d.GetOk("renotify_interval"); ok {
   335  		o.RenotifyInterval = attr.(int)
   336  	}
   337  	if attr, ok := d.GetOk("notify_audit"); ok {
   338  		o.NotifyAudit = attr.(bool)
   339  	}
   340  	if attr, ok := d.GetOk("timeout_h"); ok {
   341  		o.TimeoutH = attr.(int)
   342  	}
   343  	if attr, ok := d.GetOk("escalation_message"); ok {
   344  		o.EscalationMessage = attr.(string)
   345  	}
   346  	if attr, ok := d.GetOk("silenced"); ok {
   347  		// TODO: this is not very defensive, test if we can fail non int input
   348  		s := make(map[string]int)
   349  		for k, v := range attr.(map[string]interface{}) {
   350  			s[k], _ = strconv.Atoi(v.(string))
   351  		}
   352  		o.Silenced = s
   353  	}
   354  	if attr, ok := d.GetOk("include_tags"); ok {
   355  		o.IncludeTags = attr.(bool)
   356  	}
   357  	if attr, ok := d.GetOk("require_full_window"); ok {
   358  		o.RequireFullWindow = attr.(bool)
   359  	}
   360  	if attr, ok := d.GetOk("locked"); ok {
   361  		o.Locked = attr.(bool)
   362  	}
   363  
   364  	m.Options = o
   365  
   366  	if err = client.UpdateMonitor(m); err != nil {
   367  		return fmt.Errorf("error updating monitor: %s", err.Error())
   368  	}
   369  
   370  	return resourceDatadogMonitorRead(d, meta)
   371  }
   372  
   373  func resourceDatadogMonitorDelete(d *schema.ResourceData, meta interface{}) error {
   374  	client := meta.(*datadog.Client)
   375  
   376  	i, err := strconv.Atoi(d.Id())
   377  	if err != nil {
   378  		return err
   379  	}
   380  
   381  	if err = client.DeleteMonitor(i); err != nil {
   382  		return err
   383  	}
   384  
   385  	return nil
   386  }
   387  
   388  func resourceDatadogMonitorImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
   389  	if err := resourceDatadogMonitorRead(d, meta); err != nil {
   390  		return nil, err
   391  	}
   392  	return []*schema.ResourceData{d}, nil
   393  }
   394  
   395  // Ignore any diff that results from the mix of ints or floats returned from the
   396  // DataDog API.
   397  func suppressDataDogFloatIntDiff(k, old, new string, d *schema.ResourceData) bool {
   398  	oF, err := strconv.ParseFloat(old, 64)
   399  	if err != nil {
   400  		log.Printf("Error parsing float of old value (%s): %s", old, err)
   401  		return false
   402  	}
   403  
   404  	nF, err := strconv.ParseFloat(new, 64)
   405  	if err != nil {
   406  		log.Printf("Error parsing float of new value (%s): %s", new, err)
   407  		return false
   408  	}
   409  
   410  	// if the float values of these attributes are equivalent, ignore this
   411  	// diff
   412  	if oF == nF {
   413  		return true
   414  	}
   415  	return false
   416  }