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