github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/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  	"gopkg.in/zorkian/go-datadog-api.v2"
    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  			"new_host_delay": {
    84  				Type:     schema.TypeInt,
    85  				Computed: true,
    86  				Optional: true,
    87  			},
    88  			"no_data_timeframe": {
    89  				Type:     schema.TypeInt,
    90  				Optional: true,
    91  			},
    92  			"renotify_interval": {
    93  				Type:     schema.TypeInt,
    94  				Optional: true,
    95  			},
    96  			"notify_audit": {
    97  				Type:     schema.TypeBool,
    98  				Optional: true,
    99  			},
   100  			"timeout_h": {
   101  				Type:     schema.TypeInt,
   102  				Optional: true,
   103  			},
   104  			"require_full_window": {
   105  				Type:     schema.TypeBool,
   106  				Optional: true,
   107  				Default:  true,
   108  			},
   109  			"locked": {
   110  				Type:     schema.TypeBool,
   111  				Optional: true,
   112  			},
   113  			"silenced": {
   114  				Type:     schema.TypeMap,
   115  				Optional: true,
   116  				Elem:     schema.TypeInt,
   117  			},
   118  			"include_tags": {
   119  				Type:     schema.TypeBool,
   120  				Optional: true,
   121  				Default:  true,
   122  			},
   123  			"tags": {
   124  				Type:     schema.TypeList,
   125  				Optional: true,
   126  				Elem:     &schema.Schema{Type: schema.TypeString},
   127  			},
   128  		},
   129  	}
   130  }
   131  
   132  func buildMonitorStruct(d *schema.ResourceData) *datadog.Monitor {
   133  
   134  	var thresholds datadog.ThresholdCount
   135  
   136  	if r, ok := d.GetOk("thresholds.ok"); ok {
   137  		thresholds.SetOk(json.Number(r.(string)))
   138  	}
   139  	if r, ok := d.GetOk("thresholds.warning"); ok {
   140  		thresholds.SetWarning(json.Number(r.(string)))
   141  	}
   142  	if r, ok := d.GetOk("thresholds.critical"); ok {
   143  		thresholds.SetCritical(json.Number(r.(string)))
   144  	}
   145  
   146  	o := datadog.Options{
   147  		Thresholds:        &thresholds,
   148  		NotifyNoData:      datadog.Bool(d.Get("notify_no_data").(bool)),
   149  		RequireFullWindow: datadog.Bool(d.Get("require_full_window").(bool)),
   150  		IncludeTags:       datadog.Bool(d.Get("include_tags").(bool)),
   151  	}
   152  	if attr, ok := d.GetOk("silenced"); ok {
   153  		s := make(map[string]int)
   154  		// TODO: this is not very defensive, test if we can fail on non int input
   155  		for k, v := range attr.(map[string]interface{}) {
   156  			s[k] = v.(int)
   157  		}
   158  		o.Silenced = s
   159  	}
   160  	if attr, ok := d.GetOk("notify_no_data"); ok {
   161  		o.SetNotifyNoData(attr.(bool))
   162  	}
   163  	if attr, ok := d.GetOk("new_host_delay"); ok {
   164  		o.SetNewHostDelay(attr.(int))
   165  	}
   166  	if attr, ok := d.GetOk("no_data_timeframe"); ok {
   167  		o.NoDataTimeframe = datadog.NoDataTimeframe(attr.(int))
   168  	}
   169  	if attr, ok := d.GetOk("renotify_interval"); ok {
   170  		o.SetRenotifyInterval(attr.(int))
   171  	}
   172  	if attr, ok := d.GetOk("notify_audit"); ok {
   173  		o.SetNotifyAudit(attr.(bool))
   174  	}
   175  	if attr, ok := d.GetOk("timeout_h"); ok {
   176  		o.SetTimeoutH(attr.(int))
   177  	}
   178  	if attr, ok := d.GetOk("escalation_message"); ok {
   179  		o.SetEscalationMessage(attr.(string))
   180  	}
   181  	if attr, ok := d.GetOk("locked"); ok {
   182  		o.SetLocked(attr.(bool))
   183  	}
   184  
   185  	m := datadog.Monitor{
   186  		Type:    datadog.String(d.Get("type").(string)),
   187  		Query:   datadog.String(d.Get("query").(string)),
   188  		Name:    datadog.String(d.Get("name").(string)),
   189  		Message: datadog.String(d.Get("message").(string)),
   190  		Options: &o,
   191  	}
   192  
   193  	if attr, ok := d.GetOk("tags"); ok {
   194  		tags := []string{}
   195  		for _, s := range attr.([]interface{}) {
   196  			tags = append(tags, s.(string))
   197  		}
   198  		m.Tags = tags
   199  	}
   200  
   201  	return &m
   202  }
   203  
   204  func resourceDatadogMonitorExists(d *schema.ResourceData, meta interface{}) (b bool, e error) {
   205  	// Exists - This is called to verify a resource still exists. It is called prior to Read,
   206  	// and lowers the burden of Read to be able to assume the resource exists.
   207  	client := meta.(*datadog.Client)
   208  
   209  	i, err := strconv.Atoi(d.Id())
   210  	if err != nil {
   211  		return false, err
   212  	}
   213  
   214  	if _, err = client.GetMonitor(i); err != nil {
   215  		if strings.Contains(err.Error(), "404 Not Found") {
   216  			return false, nil
   217  		}
   218  		return false, err
   219  	}
   220  
   221  	return true, nil
   222  }
   223  
   224  func resourceDatadogMonitorCreate(d *schema.ResourceData, meta interface{}) error {
   225  
   226  	client := meta.(*datadog.Client)
   227  
   228  	m := buildMonitorStruct(d)
   229  	m, err := client.CreateMonitor(m)
   230  	if err != nil {
   231  		return fmt.Errorf("error updating monitor: %s", err.Error())
   232  	}
   233  
   234  	d.SetId(strconv.Itoa(m.GetId()))
   235  
   236  	return nil
   237  }
   238  
   239  func resourceDatadogMonitorRead(d *schema.ResourceData, meta interface{}) error {
   240  	client := meta.(*datadog.Client)
   241  
   242  	i, err := strconv.Atoi(d.Id())
   243  	if err != nil {
   244  		return err
   245  	}
   246  
   247  	m, err := client.GetMonitor(i)
   248  	if err != nil {
   249  		return err
   250  	}
   251  
   252  	thresholds := make(map[string]string)
   253  	for k, v := range map[string]json.Number{
   254  		"ok":       m.Options.Thresholds.GetOk(),
   255  		"warning":  m.Options.Thresholds.GetWarning(),
   256  		"critical": m.Options.Thresholds.GetCritical(),
   257  	} {
   258  		s := v.String()
   259  		if s != "" {
   260  			thresholds[k] = s
   261  		}
   262  	}
   263  
   264  	tags := []string{}
   265  	for _, s := range m.Tags {
   266  		tags = append(tags, s)
   267  	}
   268  
   269  	log.Printf("[DEBUG] monitor: %v", m)
   270  	d.Set("name", m.GetName())
   271  	d.Set("message", m.GetMessage())
   272  	d.Set("query", m.GetQuery())
   273  	d.Set("type", m.GetType())
   274  	d.Set("thresholds", thresholds)
   275  
   276  	d.Set("new_host_delay", m.Options.GetNewHostDelay())
   277  	d.Set("notify_no_data", m.Options.GetNotifyNoData())
   278  	d.Set("no_data_timeframe", m.Options.NoDataTimeframe)
   279  	d.Set("renotify_interval", m.Options.GetRenotifyInterval())
   280  	d.Set("notify_audit", m.Options.GetNotifyAudit())
   281  	d.Set("timeout_h", m.Options.GetTimeoutH())
   282  	d.Set("escalation_message", m.Options.GetEscalationMessage())
   283  	d.Set("silenced", m.Options.Silenced)
   284  	d.Set("include_tags", m.Options.GetIncludeTags())
   285  	d.Set("tags", tags)
   286  	d.Set("require_full_window", m.Options.GetRequireFullWindow()) // TODO Is this one of those options that we neeed to check?
   287  	d.Set("locked", m.Options.GetLocked())
   288  
   289  	return nil
   290  }
   291  
   292  func resourceDatadogMonitorUpdate(d *schema.ResourceData, meta interface{}) error {
   293  	client := meta.(*datadog.Client)
   294  
   295  	m := &datadog.Monitor{}
   296  
   297  	i, err := strconv.Atoi(d.Id())
   298  	if err != nil {
   299  		return err
   300  	}
   301  
   302  	m.Id = datadog.Int(i)
   303  	if attr, ok := d.GetOk("name"); ok {
   304  		m.SetName(attr.(string))
   305  	}
   306  	if attr, ok := d.GetOk("message"); ok {
   307  		m.SetMessage(attr.(string))
   308  	}
   309  	if attr, ok := d.GetOk("query"); ok {
   310  		m.SetQuery(attr.(string))
   311  	}
   312  
   313  	if attr, ok := d.GetOk("tags"); ok {
   314  		s := make([]string, 0)
   315  		for _, v := range attr.([]interface{}) {
   316  			s = append(s, v.(string))
   317  		}
   318  		m.Tags = s
   319  	}
   320  
   321  	o := datadog.Options{
   322  		NotifyNoData:      datadog.Bool(d.Get("notify_no_data").(bool)),
   323  		RequireFullWindow: datadog.Bool(d.Get("require_full_window").(bool)),
   324  		IncludeTags:       datadog.Bool(d.Get("include_tags").(bool)),
   325  	}
   326  	if attr, ok := d.GetOk("thresholds"); ok {
   327  		thresholds := attr.(map[string]interface{})
   328  		o.Thresholds = &datadog.ThresholdCount{} // TODO: This is a little annoying..
   329  		if thresholds["ok"] != nil {
   330  			o.Thresholds.SetOk(json.Number(thresholds["ok"].(string)))
   331  		}
   332  		if thresholds["warning"] != nil {
   333  			o.Thresholds.SetWarning(json.Number(thresholds["warning"].(string)))
   334  		}
   335  		if thresholds["critical"] != nil {
   336  			o.Thresholds.SetCritical(json.Number(thresholds["critical"].(string)))
   337  		}
   338  	}
   339  
   340  	if attr, ok := d.GetOk("new_host_delay"); ok {
   341  		o.SetNewHostDelay(attr.(int))
   342  	}
   343  	if attr, ok := d.GetOk("no_data_timeframe"); ok {
   344  		o.NoDataTimeframe = datadog.NoDataTimeframe(attr.(int))
   345  	}
   346  	if attr, ok := d.GetOk("renotify_interval"); ok {
   347  		o.SetRenotifyInterval(attr.(int))
   348  	}
   349  	if attr, ok := d.GetOk("notify_audit"); ok {
   350  		o.SetNotifyAudit(attr.(bool))
   351  	}
   352  	if attr, ok := d.GetOk("timeout_h"); ok {
   353  		o.SetTimeoutH(attr.(int))
   354  	}
   355  	if attr, ok := d.GetOk("escalation_message"); ok {
   356  		o.SetEscalationMessage(attr.(string))
   357  	}
   358  	if attr, ok := d.GetOk("silenced"); ok {
   359  		// TODO: this is not very defensive, test if we can fail non int input
   360  		s := make(map[string]int)
   361  		for k, v := range attr.(map[string]interface{}) {
   362  			s[k] = v.(int)
   363  		}
   364  		o.Silenced = s
   365  	}
   366  	if attr, ok := d.GetOk("locked"); ok {
   367  		o.SetLocked(attr.(bool))
   368  	}
   369  
   370  	m.Options = &o
   371  
   372  	if err = client.UpdateMonitor(m); err != nil {
   373  		return fmt.Errorf("error updating monitor: %s", err.Error())
   374  	}
   375  
   376  	return resourceDatadogMonitorRead(d, meta)
   377  }
   378  
   379  func resourceDatadogMonitorDelete(d *schema.ResourceData, meta interface{}) error {
   380  	client := meta.(*datadog.Client)
   381  
   382  	i, err := strconv.Atoi(d.Id())
   383  	if err != nil {
   384  		return err
   385  	}
   386  
   387  	if err = client.DeleteMonitor(i); err != nil {
   388  		return err
   389  	}
   390  
   391  	return nil
   392  }
   393  
   394  func resourceDatadogMonitorImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
   395  	if err := resourceDatadogMonitorRead(d, meta); err != nil {
   396  		return nil, err
   397  	}
   398  	return []*schema.ResourceData{d}, nil
   399  }
   400  
   401  // Ignore any diff that results from the mix of ints or floats returned from the
   402  // DataDog API.
   403  func suppressDataDogFloatIntDiff(k, old, new string, d *schema.ResourceData) bool {
   404  	oF, err := strconv.ParseFloat(old, 64)
   405  	if err != nil {
   406  		log.Printf("Error parsing float of old value (%s): %s", old, err)
   407  		return false
   408  	}
   409  
   410  	nF, err := strconv.ParseFloat(new, 64)
   411  	if err != nil {
   412  		log.Printf("Error parsing float of new value (%s): %s", new, err)
   413  		return false
   414  	}
   415  
   416  	// if the float values of these attributes are equivalent, ignore this
   417  	// diff
   418  	if oF == nF {
   419  		return true
   420  	}
   421  	return false
   422  }