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  }