github.com/atsaki/terraform@v0.4.3-0.20150919165407-25bba5967654/builtin/providers/google/resource_compute_backend_service.go (about) 1 package google 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "regexp" 8 "time" 9 10 "github.com/hashicorp/terraform/helper/hashcode" 11 "github.com/hashicorp/terraform/helper/schema" 12 "google.golang.org/api/compute/v1" 13 "google.golang.org/api/googleapi" 14 ) 15 16 func resourceComputeBackendService() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceComputeBackendServiceCreate, 19 Read: resourceComputeBackendServiceRead, 20 Update: resourceComputeBackendServiceUpdate, 21 Delete: resourceComputeBackendServiceDelete, 22 23 Schema: map[string]*schema.Schema{ 24 "backend": &schema.Schema{ 25 Type: schema.TypeSet, 26 Elem: &schema.Resource{ 27 Schema: map[string]*schema.Schema{ 28 "balancing_mode": &schema.Schema{ 29 Type: schema.TypeString, 30 Optional: true, 31 Default: "UTILIZATION", 32 }, 33 "capacity_scaler": &schema.Schema{ 34 Type: schema.TypeFloat, 35 Optional: true, 36 Default: 1, 37 }, 38 "description": &schema.Schema{ 39 Type: schema.TypeString, 40 Optional: true, 41 }, 42 "group": &schema.Schema{ 43 Type: schema.TypeString, 44 Optional: true, 45 }, 46 "max_rate": &schema.Schema{ 47 Type: schema.TypeInt, 48 Optional: true, 49 }, 50 "max_rate_per_instance": &schema.Schema{ 51 Type: schema.TypeFloat, 52 Optional: true, 53 }, 54 "max_utilization": &schema.Schema{ 55 Type: schema.TypeFloat, 56 Optional: true, 57 Default: 0.8, 58 }, 59 }, 60 }, 61 Optional: true, 62 Set: resourceGoogleComputeBackendServiceBackendHash, 63 }, 64 65 "description": &schema.Schema{ 66 Type: schema.TypeString, 67 Optional: true, 68 }, 69 70 "health_checks": &schema.Schema{ 71 Type: schema.TypeSet, 72 Elem: &schema.Schema{Type: schema.TypeString}, 73 Required: true, 74 Set: schema.HashString, 75 }, 76 77 "name": &schema.Schema{ 78 Type: schema.TypeString, 79 Required: true, 80 ForceNew: true, 81 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 82 value := v.(string) 83 re := `^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$` 84 if !regexp.MustCompile(re).MatchString(value) { 85 errors = append(errors, fmt.Errorf( 86 "%q (%q) doesn't match regexp %q", k, value, re)) 87 } 88 return 89 }, 90 }, 91 92 "port_name": &schema.Schema{ 93 Type: schema.TypeString, 94 Optional: true, 95 Computed: true, 96 }, 97 98 "protocol": &schema.Schema{ 99 Type: schema.TypeString, 100 Optional: true, 101 Computed: true, 102 }, 103 104 "timeout_sec": &schema.Schema{ 105 Type: schema.TypeInt, 106 Optional: true, 107 Computed: true, 108 }, 109 110 "fingerprint": &schema.Schema{ 111 Type: schema.TypeString, 112 Computed: true, 113 }, 114 115 "self_link": &schema.Schema{ 116 Type: schema.TypeString, 117 Computed: true, 118 }, 119 }, 120 } 121 } 122 123 func resourceComputeBackendServiceCreate(d *schema.ResourceData, meta interface{}) error { 124 config := meta.(*Config) 125 126 hc := d.Get("health_checks").(*schema.Set).List() 127 healthChecks := make([]string, 0, len(hc)) 128 for _, v := range hc { 129 healthChecks = append(healthChecks, v.(string)) 130 } 131 132 service := compute.BackendService{ 133 Name: d.Get("name").(string), 134 HealthChecks: healthChecks, 135 } 136 137 if v, ok := d.GetOk("backend"); ok { 138 service.Backends = expandBackends(v.(*schema.Set).List()) 139 } 140 141 if v, ok := d.GetOk("description"); ok { 142 service.Description = v.(string) 143 } 144 145 if v, ok := d.GetOk("port_name"); ok { 146 service.PortName = v.(string) 147 } 148 149 if v, ok := d.GetOk("protocol"); ok { 150 service.Protocol = v.(string) 151 } 152 153 if v, ok := d.GetOk("timeout_sec"); ok { 154 service.TimeoutSec = int64(v.(int)) 155 } 156 157 log.Printf("[DEBUG] Creating new Backend Service: %#v", service) 158 op, err := config.clientCompute.BackendServices.Insert( 159 config.Project, &service).Do() 160 if err != nil { 161 return fmt.Errorf("Error creating backend service: %s", err) 162 } 163 164 log.Printf("[DEBUG] Waiting for new backend service, operation: %#v", op) 165 166 d.SetId(service.Name) 167 168 // Wait for the operation to complete 169 w := &OperationWaiter{ 170 Service: config.clientCompute, 171 Op: op, 172 Project: config.Project, 173 Region: config.Region, 174 Type: OperationWaitGlobal, 175 } 176 state := w.Conf() 177 state.Timeout = 2 * time.Minute 178 state.MinTimeout = 1 * time.Second 179 opRaw, err := state.WaitForState() 180 if err != nil { 181 return fmt.Errorf("Error waiting for backend service to create: %s", err) 182 } 183 op = opRaw.(*compute.Operation) 184 if op.Error != nil { 185 // The resource didn't actually create 186 d.SetId("") 187 188 // Return the error 189 return OperationError(*op.Error) 190 } 191 192 return resourceComputeBackendServiceRead(d, meta) 193 } 194 195 func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{}) error { 196 config := meta.(*Config) 197 198 service, err := config.clientCompute.BackendServices.Get( 199 config.Project, d.Id()).Do() 200 if err != nil { 201 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 202 // The resource doesn't exist anymore 203 d.SetId("") 204 205 return nil 206 } 207 208 return fmt.Errorf("Error reading service: %s", err) 209 } 210 211 d.Set("description", service.Description) 212 d.Set("port_name", service.PortName) 213 d.Set("protocol", service.Protocol) 214 d.Set("timeout_sec", service.TimeoutSec) 215 d.Set("fingerprint", service.Fingerprint) 216 d.Set("self_link", service.SelfLink) 217 218 d.Set("backend", flattenBackends(service.Backends)) 219 d.Set("health_checks", service.HealthChecks) 220 221 return nil 222 } 223 224 func resourceComputeBackendServiceUpdate(d *schema.ResourceData, meta interface{}) error { 225 config := meta.(*Config) 226 227 hc := d.Get("health_checks").(*schema.Set).List() 228 healthChecks := make([]string, 0, len(hc)) 229 for _, v := range hc { 230 healthChecks = append(healthChecks, v.(string)) 231 } 232 233 service := compute.BackendService{ 234 Name: d.Get("name").(string), 235 Fingerprint: d.Get("fingerprint").(string), 236 HealthChecks: healthChecks, 237 } 238 239 if d.HasChange("backend") { 240 service.Backends = expandBackends(d.Get("backend").(*schema.Set).List()) 241 } 242 if d.HasChange("description") { 243 service.Description = d.Get("description").(string) 244 } 245 if d.HasChange("port_name") { 246 service.PortName = d.Get("port_name").(string) 247 } 248 if d.HasChange("protocol") { 249 service.Protocol = d.Get("protocol").(string) 250 } 251 if d.HasChange("timeout_sec") { 252 service.TimeoutSec = int64(d.Get("timeout_sec").(int)) 253 } 254 255 log.Printf("[DEBUG] Updating existing Backend Service %q: %#v", d.Id(), service) 256 op, err := config.clientCompute.BackendServices.Update( 257 config.Project, d.Id(), &service).Do() 258 if err != nil { 259 return fmt.Errorf("Error updating backend service: %s", err) 260 } 261 262 d.SetId(service.Name) 263 264 // Wait for the operation to complete 265 w := &OperationWaiter{ 266 Service: config.clientCompute, 267 Op: op, 268 Project: config.Project, 269 Region: config.Region, 270 Type: OperationWaitGlobal, 271 } 272 state := w.Conf() 273 state.Timeout = 2 * time.Minute 274 state.MinTimeout = 1 * time.Second 275 opRaw, err := state.WaitForState() 276 if err != nil { 277 return fmt.Errorf("Error waiting for backend service to update: %s", err) 278 } 279 op = opRaw.(*compute.Operation) 280 if op.Error != nil { 281 // Return the error 282 return OperationError(*op.Error) 283 } 284 285 return resourceComputeBackendServiceRead(d, meta) 286 } 287 288 func resourceComputeBackendServiceDelete(d *schema.ResourceData, meta interface{}) error { 289 config := meta.(*Config) 290 291 log.Printf("[DEBUG] Deleting backend service %s", d.Id()) 292 op, err := config.clientCompute.BackendServices.Delete( 293 config.Project, d.Id()).Do() 294 if err != nil { 295 return fmt.Errorf("Error deleting backend service: %s", err) 296 } 297 298 // Wait for the operation to complete 299 w := &OperationWaiter{ 300 Service: config.clientCompute, 301 Op: op, 302 Project: config.Project, 303 Region: config.Region, 304 Type: OperationWaitGlobal, 305 } 306 state := w.Conf() 307 state.Timeout = 2 * time.Minute 308 state.MinTimeout = 1 * time.Second 309 opRaw, err := state.WaitForState() 310 if err != nil { 311 return fmt.Errorf("Error waiting for backend service to delete: %s", err) 312 } 313 op = opRaw.(*compute.Operation) 314 if op.Error != nil { 315 // Return the error 316 return OperationError(*op.Error) 317 } 318 319 d.SetId("") 320 return nil 321 } 322 323 func expandBackends(configured []interface{}) []*compute.Backend { 324 backends := make([]*compute.Backend, 0, len(configured)) 325 326 for _, raw := range configured { 327 data := raw.(map[string]interface{}) 328 329 b := compute.Backend{ 330 Group: data["group"].(string), 331 } 332 333 if v, ok := data["balancing_mode"]; ok { 334 b.BalancingMode = v.(string) 335 } 336 if v, ok := data["capacity_scaler"]; ok { 337 b.CapacityScaler = v.(float64) 338 } 339 if v, ok := data["description"]; ok { 340 b.Description = v.(string) 341 } 342 if v, ok := data["max_rate"]; ok { 343 b.MaxRate = int64(v.(int)) 344 } 345 if v, ok := data["max_rate_per_instance"]; ok { 346 b.MaxRatePerInstance = v.(float64) 347 } 348 if v, ok := data["max_rate_per_instance"]; ok { 349 b.MaxUtilization = v.(float64) 350 } 351 352 backends = append(backends, &b) 353 } 354 355 return backends 356 } 357 358 func flattenBackends(backends []*compute.Backend) []map[string]interface{} { 359 result := make([]map[string]interface{}, 0, len(backends)) 360 361 for _, b := range backends { 362 data := make(map[string]interface{}) 363 364 data["balancing_mode"] = b.BalancingMode 365 data["capacity_scaler"] = b.CapacityScaler 366 data["description"] = b.Description 367 data["group"] = b.Group 368 data["max_rate"] = b.MaxRate 369 data["max_rate_per_instance"] = b.MaxRatePerInstance 370 data["max_utilization"] = b.MaxUtilization 371 372 result = append(result, data) 373 } 374 375 return result 376 } 377 378 func resourceGoogleComputeBackendServiceBackendHash(v interface{}) int { 379 if v == nil { 380 return 0 381 } 382 383 var buf bytes.Buffer 384 m := v.(map[string]interface{}) 385 386 buf.WriteString(fmt.Sprintf("%s-", m["group"].(string))) 387 388 if v, ok := m["balancing_mode"]; ok { 389 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 390 } 391 if v, ok := m["capacity_scaler"]; ok { 392 buf.WriteString(fmt.Sprintf("%f-", v.(float64))) 393 } 394 if v, ok := m["description"]; ok { 395 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 396 } 397 if v, ok := m["max_rate"]; ok { 398 buf.WriteString(fmt.Sprintf("%d-", int64(v.(int)))) 399 } 400 if v, ok := m["max_rate_per_instance"]; ok { 401 buf.WriteString(fmt.Sprintf("%f-", v.(float64))) 402 } 403 if v, ok := m["max_rate_per_instance"]; ok { 404 buf.WriteString(fmt.Sprintf("%f-", v.(float64))) 405 } 406 407 return hashcode.String(buf.String()) 408 }