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