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