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