github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/digitalocean/resource_digitalocean_loadbalancer.go (about)

     1  package digitalocean
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/digitalocean/godo"
    11  	"github.com/hashicorp/terraform/helper/resource"
    12  	"github.com/hashicorp/terraform/helper/schema"
    13  )
    14  
    15  func resourceDigitalOceanLoadbalancer() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceDigitalOceanLoadbalancerCreate,
    18  		Read:   resourceDigitalOceanLoadbalancerRead,
    19  		Update: resourceDigitalOceanLoadbalancerUpdate,
    20  		Delete: resourceDigitalOceanLoadbalancerDelete,
    21  
    22  		Schema: map[string]*schema.Schema{
    23  			"name": {
    24  				Type:     schema.TypeString,
    25  				Required: true,
    26  			},
    27  
    28  			"region": {
    29  				Type:     schema.TypeString,
    30  				Required: true,
    31  				ForceNew: true,
    32  			},
    33  
    34  			"algorithm": {
    35  				Type:     schema.TypeString,
    36  				Optional: true,
    37  				Default:  "round_robin",
    38  			},
    39  
    40  			"forwarding_rule": {
    41  				Type:     schema.TypeList,
    42  				Required: true,
    43  				MinItems: 1,
    44  				Elem: &schema.Resource{
    45  					Schema: map[string]*schema.Schema{
    46  						"entry_protocol": {
    47  							Type:     schema.TypeString,
    48  							Required: true,
    49  						},
    50  						"entry_port": {
    51  							Type:     schema.TypeInt,
    52  							Required: true,
    53  						},
    54  						"target_protocol": {
    55  							Type:     schema.TypeString,
    56  							Required: true,
    57  						},
    58  						"target_port": {
    59  							Type:     schema.TypeInt,
    60  							Required: true,
    61  						},
    62  						"certificate_id": {
    63  							Type:     schema.TypeString,
    64  							Optional: true,
    65  						},
    66  						"tls_passthrough": {
    67  							Type:     schema.TypeBool,
    68  							Optional: true,
    69  							Default:  false,
    70  						},
    71  					},
    72  				},
    73  			},
    74  
    75  			"healthcheck": {
    76  				Type:     schema.TypeList,
    77  				Optional: true,
    78  				MaxItems: 1,
    79  				Elem: &schema.Resource{
    80  					Schema: map[string]*schema.Schema{
    81  						"protocol": {
    82  							Type:     schema.TypeString,
    83  							Required: true,
    84  						},
    85  						"port": {
    86  							Type:     schema.TypeInt,
    87  							Required: true,
    88  						},
    89  						"path": {
    90  							Type:     schema.TypeString,
    91  							Optional: true,
    92  						},
    93  						"check_interval_seconds": {
    94  							Type:     schema.TypeInt,
    95  							Optional: true,
    96  							Default:  10,
    97  						},
    98  						"response_timeout_seconds": {
    99  							Type:     schema.TypeInt,
   100  							Optional: true,
   101  							Default:  5,
   102  						},
   103  						"unhealthy_threshold": {
   104  							Type:     schema.TypeInt,
   105  							Optional: true,
   106  							Default:  3,
   107  						},
   108  						"healthy_threshold": {
   109  							Type:     schema.TypeInt,
   110  							Optional: true,
   111  							Default:  5,
   112  						},
   113  					},
   114  				},
   115  			},
   116  
   117  			"sticky_sessions": {
   118  				Type:     schema.TypeList,
   119  				Optional: true,
   120  				Computed: true, //this needs to be computed as the API returns a struct with none as the type
   121  				MaxItems: 1,
   122  				Elem: &schema.Resource{
   123  					Schema: map[string]*schema.Schema{
   124  						"type": {
   125  							Type:     schema.TypeString,
   126  							Optional: true,
   127  							Default:  "none",
   128  						},
   129  						"cookie_name": {
   130  							Type:     schema.TypeString,
   131  							Optional: true,
   132  						},
   133  						"cookie_ttl_seconds": {
   134  							Type:     schema.TypeInt,
   135  							Optional: true,
   136  						},
   137  					},
   138  				},
   139  			},
   140  
   141  			"droplet_ids": {
   142  				Type:     schema.TypeList,
   143  				Elem:     &schema.Schema{Type: schema.TypeString},
   144  				Optional: true,
   145  			},
   146  
   147  			"droplet_tag": {
   148  				Type:     schema.TypeString,
   149  				Optional: true,
   150  			},
   151  
   152  			"redirect_http_to_https": {
   153  				Type:     schema.TypeBool,
   154  				Optional: true,
   155  				Default:  false,
   156  			},
   157  
   158  			"ip": {
   159  				Type:     schema.TypeString,
   160  				Computed: true,
   161  			},
   162  		},
   163  	}
   164  }
   165  
   166  func buildLoadBalancerRequest(d *schema.ResourceData) (*godo.LoadBalancerRequest, error) {
   167  	opts := &godo.LoadBalancerRequest{
   168  		Name:                d.Get("name").(string),
   169  		Region:              d.Get("region").(string),
   170  		Algorithm:           d.Get("algorithm").(string),
   171  		RedirectHttpToHttps: d.Get("redirect_http_to_https").(bool),
   172  		ForwardingRules:     expandForwardingRules(d.Get("forwarding_rule").([]interface{})),
   173  	}
   174  
   175  	if v, ok := d.GetOk("droplet_ids"); ok {
   176  		var droplets []int
   177  		for _, id := range v.([]interface{}) {
   178  			i, err := strconv.Atoi(id.(string))
   179  			if err != nil {
   180  				return nil, err
   181  			}
   182  			droplets = append(droplets, i)
   183  		}
   184  
   185  		opts.DropletIDs = droplets
   186  	}
   187  
   188  	if v, ok := d.GetOk("droplet_tag"); ok {
   189  		opts.Tag = v.(string)
   190  	}
   191  
   192  	if v, ok := d.GetOk("healthcheck"); ok {
   193  		opts.HealthCheck = expandHealthCheck(v.([]interface{}))
   194  	}
   195  
   196  	if v, ok := d.GetOk("sticky_sessions"); ok {
   197  		opts.StickySessions = expandStickySessions(v.([]interface{}))
   198  	}
   199  
   200  	return opts, nil
   201  }
   202  
   203  func resourceDigitalOceanLoadbalancerCreate(d *schema.ResourceData, meta interface{}) error {
   204  	client := meta.(*godo.Client)
   205  
   206  	log.Printf("[INFO] Create a Loadbalancer Request")
   207  
   208  	lbOpts, err := buildLoadBalancerRequest(d)
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	log.Printf("[DEBUG] Loadbalancer Create: %#v", lbOpts)
   214  	loadbalancer, _, err := client.LoadBalancers.Create(context.Background(), lbOpts)
   215  	if err != nil {
   216  		return fmt.Errorf("Error creating Load Balancer: %s", err)
   217  	}
   218  
   219  	d.SetId(loadbalancer.ID)
   220  
   221  	log.Printf("[DEBUG] Waiting for Load Balancer (%s) to become active", d.Get("name"))
   222  	stateConf := &resource.StateChangeConf{
   223  		Pending:    []string{"new"},
   224  		Target:     []string{"active"},
   225  		Refresh:    loadbalancerStateRefreshFunc(client, d.Id()),
   226  		Timeout:    10 * time.Minute,
   227  		MinTimeout: 15 * time.Second,
   228  	}
   229  	if _, err := stateConf.WaitForState(); err != nil {
   230  		return fmt.Errorf("Error waiting for Load Balancer (%s) to become active: %s", d.Get("name"), err)
   231  	}
   232  
   233  	return resourceDigitalOceanLoadbalancerRead(d, meta)
   234  }
   235  
   236  func resourceDigitalOceanLoadbalancerRead(d *schema.ResourceData, meta interface{}) error {
   237  	client := meta.(*godo.Client)
   238  
   239  	log.Printf("[INFO] Reading the details of the Loadbalancer %s", d.Id())
   240  	loadbalancer, resp, err := client.LoadBalancers.Get(context.Background(), d.Id())
   241  	if err != nil {
   242  		if resp != nil && resp.StatusCode == 404 {
   243  			log.Printf("[WARN] DigitalOcean Load Balancer (%s) not found", d.Id())
   244  			d.SetId("")
   245  			return nil
   246  		}
   247  		return fmt.Errorf("Error retrieving Loadbalancer: %s", err)
   248  	}
   249  
   250  	d.Set("name", loadbalancer.Name)
   251  	d.Set("ip", loadbalancer.IP)
   252  	d.Set("algorithm", loadbalancer.Algorithm)
   253  	d.Set("region", loadbalancer.Region.Slug)
   254  	d.Set("redirect_http_to_https", loadbalancer.RedirectHttpToHttps)
   255  	d.Set("droplet_ids", flattenDropletIds(loadbalancer.DropletIDs))
   256  	d.Set("droplet_tag", loadbalancer.Tag)
   257  
   258  	if err := d.Set("sticky_sessions", flattenStickySessions(loadbalancer.StickySessions)); err != nil {
   259  		return fmt.Errorf("[DEBUG] Error setting Load Balancer sticky_sessions - error: %#v", err)
   260  	}
   261  
   262  	if err := d.Set("healthcheck", flattenHealthChecks(loadbalancer.HealthCheck)); err != nil {
   263  		return fmt.Errorf("[DEBUG] Error setting Load Balancer healthcheck - error: %#v", err)
   264  	}
   265  
   266  	if err := d.Set("forwarding_rule", flattenForwardingRules(loadbalancer.ForwardingRules)); err != nil {
   267  		return fmt.Errorf("[DEBUG] Error setting Load Balancer forwarding_rule - error: %#v", err)
   268  	}
   269  
   270  	return nil
   271  
   272  }
   273  
   274  func resourceDigitalOceanLoadbalancerUpdate(d *schema.ResourceData, meta interface{}) error {
   275  	client := meta.(*godo.Client)
   276  
   277  	lbOpts, err := buildLoadBalancerRequest(d)
   278  	if err != nil {
   279  		return err
   280  	}
   281  
   282  	log.Printf("[DEBUG] Load Balancer Update: %#v", lbOpts)
   283  	_, _, err = client.LoadBalancers.Update(context.Background(), d.Id(), lbOpts)
   284  	if err != nil {
   285  		return fmt.Errorf("Error updating Load Balancer: %s", err)
   286  	}
   287  
   288  	return resourceDigitalOceanLoadbalancerRead(d, meta)
   289  }
   290  
   291  func resourceDigitalOceanLoadbalancerDelete(d *schema.ResourceData, meta interface{}) error {
   292  	client := meta.(*godo.Client)
   293  
   294  	log.Printf("[INFO] Deleting Load Balancer: %s", d.Id())
   295  	_, err := client.LoadBalancers.Delete(context.Background(), d.Id())
   296  	if err != nil {
   297  		return fmt.Errorf("Error deleting Load Balancer: %s", err)
   298  	}
   299  
   300  	d.SetId("")
   301  	return nil
   302  
   303  }