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