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 }