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