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  }