github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/librato/resource_librato_service.go (about)

     1  package librato
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"log"
     7  	"reflect"
     8  	"strconv"
     9  	"time"
    10  
    11  	"github.com/hashicorp/terraform/helper/resource"
    12  	"github.com/hashicorp/terraform/helper/schema"
    13  	"github.com/henrikhodne/go-librato/librato"
    14  )
    15  
    16  func resourceLibratoService() *schema.Resource {
    17  	return &schema.Resource{
    18  		Create: resourceLibratoServiceCreate,
    19  		Read:   resourceLibratoServiceRead,
    20  		Update: resourceLibratoServiceUpdate,
    21  		Delete: resourceLibratoServiceDelete,
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"id": &schema.Schema{
    25  				Type:     schema.TypeInt,
    26  				Computed: true,
    27  			},
    28  			"type": &schema.Schema{
    29  				Type:     schema.TypeString,
    30  				Required: true,
    31  				ForceNew: true,
    32  			},
    33  			"title": &schema.Schema{
    34  				Type:     schema.TypeString,
    35  				Required: true,
    36  			},
    37  			"settings": &schema.Schema{
    38  				Type:      schema.TypeString,
    39  				Required:  true,
    40  				StateFunc: normalizeJson,
    41  			},
    42  		},
    43  	}
    44  }
    45  
    46  // Takes JSON in a string. Decodes JSON into
    47  // settings hash
    48  func resourceLibratoServicesExpandSettings(rawSettings string) (map[string]string, error) {
    49  	var settings map[string]string
    50  
    51  	settings = make(map[string]string)
    52  	err := json.Unmarshal([]byte(rawSettings), &settings)
    53  	if err != nil {
    54  		return nil, fmt.Errorf("Error decoding JSON: %s", err)
    55  	}
    56  
    57  	return settings, err
    58  }
    59  
    60  // Encodes a settings hash into a JSON string
    61  func resourceLibratoServicesFlatten(settings map[string]string) (string, error) {
    62  	byteArray, err := json.Marshal(settings)
    63  	if err != nil {
    64  		return "", fmt.Errorf("Error encoding to JSON: %s", err)
    65  	}
    66  
    67  	return string(byteArray), nil
    68  }
    69  
    70  func normalizeJson(jsonString interface{}) string {
    71  	if jsonString == nil || jsonString == "" {
    72  		return ""
    73  	}
    74  	var j interface{}
    75  	err := json.Unmarshal([]byte(jsonString.(string)), &j)
    76  	if err != nil {
    77  		return fmt.Sprintf("Error parsing JSON: %s", err)
    78  	}
    79  	b, _ := json.Marshal(j)
    80  	return string(b[:])
    81  }
    82  
    83  func resourceLibratoServiceCreate(d *schema.ResourceData, meta interface{}) error {
    84  	client := meta.(*librato.Client)
    85  
    86  	service := new(librato.Service)
    87  	if v, ok := d.GetOk("type"); ok {
    88  		service.Type = librato.String(v.(string))
    89  	}
    90  	if v, ok := d.GetOk("title"); ok {
    91  		service.Title = librato.String(v.(string))
    92  	}
    93  	if v, ok := d.GetOk("settings"); ok {
    94  		res, err := resourceLibratoServicesExpandSettings(normalizeJson(v.(string)))
    95  		if err != nil {
    96  			return fmt.Errorf("Error expanding Librato service settings: %s", err)
    97  		}
    98  		service.Settings = res
    99  	}
   100  
   101  	serviceResult, _, err := client.Services.Create(service)
   102  
   103  	if err != nil {
   104  		return fmt.Errorf("Error creating Librato service: %s", err)
   105  	}
   106  
   107  	resource.Retry(1*time.Minute, func() *resource.RetryError {
   108  		_, _, err := client.Services.Get(*serviceResult.ID)
   109  		if err != nil {
   110  			if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 {
   111  				return resource.RetryableError(err)
   112  			}
   113  			return resource.NonRetryableError(err)
   114  		}
   115  		return nil
   116  	})
   117  
   118  	return resourceLibratoServiceReadResult(d, serviceResult)
   119  }
   120  
   121  func resourceLibratoServiceRead(d *schema.ResourceData, meta interface{}) error {
   122  	client := meta.(*librato.Client)
   123  	id, err := strconv.ParseUint(d.Id(), 10, 0)
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	log.Printf("[INFO] Reading Librato Service: %d", id)
   129  	service, _, err := client.Services.Get(uint(id))
   130  	if err != nil {
   131  		if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 {
   132  			d.SetId("")
   133  			return nil
   134  		}
   135  		return fmt.Errorf("Error reading Librato Service %s: %s", d.Id(), err)
   136  	}
   137  	log.Printf("[INFO] Received Librato Service: %s", service)
   138  
   139  	return resourceLibratoServiceReadResult(d, service)
   140  }
   141  
   142  func resourceLibratoServiceReadResult(d *schema.ResourceData, service *librato.Service) error {
   143  	d.SetId(strconv.FormatUint(uint64(*service.ID), 10))
   144  	d.Set("id", *service.ID)
   145  	d.Set("type", *service.Type)
   146  	d.Set("title", *service.Title)
   147  	settings, _ := resourceLibratoServicesFlatten(service.Settings)
   148  	d.Set("settings", settings)
   149  
   150  	return nil
   151  }
   152  
   153  func resourceLibratoServiceUpdate(d *schema.ResourceData, meta interface{}) error {
   154  	client := meta.(*librato.Client)
   155  
   156  	serviceID, err := strconv.ParseUint(d.Id(), 10, 0)
   157  	if err != nil {
   158  		return err
   159  	}
   160  
   161  	// Just to have whole object for comparison before/after update
   162  	fullService, _, err := client.Services.Get(uint(serviceID))
   163  	if err != nil {
   164  		return err
   165  	}
   166  
   167  	service := new(librato.Service)
   168  	if d.HasChange("type") {
   169  		service.Type = librato.String(d.Get("type").(string))
   170  		fullService.Type = service.Type
   171  	}
   172  	if d.HasChange("title") {
   173  		service.Title = librato.String(d.Get("title").(string))
   174  		fullService.Title = service.Title
   175  	}
   176  	if d.HasChange("settings") {
   177  		res, err := resourceLibratoServicesExpandSettings(normalizeJson(d.Get("settings").(string)))
   178  		if err != nil {
   179  			return fmt.Errorf("Error expanding Librato service settings: %s", err)
   180  		}
   181  		service.Settings = res
   182  		fullService.Settings = res
   183  	}
   184  
   185  	log.Printf("[INFO] Updating Librato Service %d: %s", serviceID, service)
   186  	_, err = client.Services.Edit(uint(serviceID), service)
   187  	if err != nil {
   188  		return fmt.Errorf("Error updating Librato service: %s", err)
   189  	}
   190  	log.Printf("[INFO] Updated Librato Service %d", serviceID)
   191  
   192  	// Wait for propagation since Librato updates are eventually consistent
   193  	wait := resource.StateChangeConf{
   194  		Pending:                   []string{fmt.Sprintf("%t", false)},
   195  		Target:                    []string{fmt.Sprintf("%t", true)},
   196  		Timeout:                   5 * time.Minute,
   197  		MinTimeout:                2 * time.Second,
   198  		ContinuousTargetOccurence: 5,
   199  		Refresh: func() (interface{}, string, error) {
   200  			log.Printf("[DEBUG] Checking if Librato Service %d was updated yet", serviceID)
   201  			changedService, _, err := client.Services.Get(uint(serviceID))
   202  			if err != nil {
   203  				return changedService, "", err
   204  			}
   205  			isEqual := reflect.DeepEqual(*fullService, *changedService)
   206  			log.Printf("[DEBUG] Updated Librato Service %d match: %t", serviceID, isEqual)
   207  			return changedService, fmt.Sprintf("%t", isEqual), nil
   208  		},
   209  	}
   210  
   211  	_, err = wait.WaitForState()
   212  	if err != nil {
   213  		return fmt.Errorf("Failed updating Librato Service %d: %s", serviceID, err)
   214  	}
   215  
   216  	return resourceLibratoServiceRead(d, meta)
   217  }
   218  
   219  func resourceLibratoServiceDelete(d *schema.ResourceData, meta interface{}) error {
   220  	client := meta.(*librato.Client)
   221  	id, err := strconv.ParseUint(d.Id(), 10, 0)
   222  	if err != nil {
   223  		return err
   224  	}
   225  
   226  	log.Printf("[INFO] Deleting Service: %d", id)
   227  	_, err = client.Services.Delete(uint(id))
   228  	if err != nil {
   229  		return fmt.Errorf("Error deleting Service: %s", err)
   230  	}
   231  
   232  	resource.Retry(1*time.Minute, func() *resource.RetryError {
   233  		_, _, err := client.Services.Get(uint(id))
   234  		if err != nil {
   235  			if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 {
   236  				return nil
   237  			}
   238  			return resource.NonRetryableError(err)
   239  		}
   240  		return resource.RetryableError(fmt.Errorf("service still exists"))
   241  	})
   242  
   243  	d.SetId("")
   244  	return nil
   245  }