provider/digitalocean: Add support for LoadBalancers (#12077)
* provider/digitalocean: Add support for LoadBalancers Fixes: #11945 ``` % make testacc TEST=./builtin/providers/digitalocean TESTARGS='-run=TestAccDigitalOceanLoadbalancer_' 2 ↵ ✹ ✭ ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/02/18 21:49:11 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/digitalocean -v -run=TestAccDigitalOceanLoadbalancer_ -timeout 120m === RUN TestAccDigitalOceanLoadbalancer_Basic --- PASS: TestAccDigitalOceanLoadbalancer_Basic (121.18s) === RUN TestAccDigitalOceanLoadbalancer_Updated --- PASS: TestAccDigitalOceanLoadbalancer_Updated (168.35s) === RUN TestAccDigitalOceanLoadbalancer_dropletTag --- PASS: TestAccDigitalOceanLoadbalancer_dropletTag (131.31s) PASS ok github.com/hashicorp/terraform/builtin/providers/digitalocean 420.851s ``` * provider/digitalocean: Addressing PR feedback from @catsby
This commit is contained in:
parent
9fc577547c
commit
861706921c
|
@ -0,0 +1,145 @@
|
||||||
|
package digitalocean
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/digitalocean/godo"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadbalancerStateRefreshFunc(client *godo.Client, loadbalancerId string) resource.StateRefreshFunc {
|
||||||
|
return func() (interface{}, string, error) {
|
||||||
|
lb, _, err := client.LoadBalancers.Get(loadbalancerId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("Error issuing read request in LoadbalancerStateRefreshFunc to DigitalOcean for Load Balancer '%s': %s", loadbalancerId, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lb, lb.Status, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandStickySessions(config []interface{}) *godo.StickySessions {
|
||||||
|
stickysessionConfig := config[0].(map[string]interface{})
|
||||||
|
|
||||||
|
stickySession := &godo.StickySessions{
|
||||||
|
Type: stickysessionConfig["type"].(string),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := stickysessionConfig["cookie_name"]; ok {
|
||||||
|
stickySession.CookieName = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := stickysessionConfig["cookie_ttl_seconds"]; ok {
|
||||||
|
stickySession.CookieTtlSeconds = v.(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
return stickySession
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandHealthCheck(config []interface{}) *godo.HealthCheck {
|
||||||
|
healthcheckConfig := config[0].(map[string]interface{})
|
||||||
|
|
||||||
|
healthcheck := &godo.HealthCheck{
|
||||||
|
Protocol: healthcheckConfig["protocol"].(string),
|
||||||
|
Port: healthcheckConfig["port"].(int),
|
||||||
|
CheckIntervalSeconds: healthcheckConfig["check_interval_seconds"].(int),
|
||||||
|
ResponseTimeoutSeconds: healthcheckConfig["response_timeout_seconds"].(int),
|
||||||
|
UnhealthyThreshold: healthcheckConfig["unhealthy_threshold"].(int),
|
||||||
|
HealthyThreshold: healthcheckConfig["healthy_threshold"].(int),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := healthcheckConfig["path"]; ok {
|
||||||
|
healthcheck.Path = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return healthcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandForwardingRules(config []interface{}) []godo.ForwardingRule {
|
||||||
|
forwardingRules := make([]godo.ForwardingRule, 0, len(config))
|
||||||
|
|
||||||
|
for _, rawRule := range config {
|
||||||
|
rule := rawRule.(map[string]interface{})
|
||||||
|
|
||||||
|
r := godo.ForwardingRule{
|
||||||
|
EntryPort: rule["entry_port"].(int),
|
||||||
|
EntryProtocol: rule["entry_protocol"].(string),
|
||||||
|
TargetPort: rule["target_port"].(int),
|
||||||
|
TargetProtocol: rule["target_protocol"].(string),
|
||||||
|
TlsPassthrough: rule["tls_passthrough"].(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := rule["certificate_id"]; ok {
|
||||||
|
r.CertificateID = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
forwardingRules = append(forwardingRules, r)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return forwardingRules
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenDropletIds(list []int) []interface{} {
|
||||||
|
vs := make([]interface{}, 0, len(list))
|
||||||
|
for _, v := range list {
|
||||||
|
vs = append(vs, v)
|
||||||
|
}
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenHealthChecks(health *godo.HealthCheck) []map[string]interface{} {
|
||||||
|
result := make([]map[string]interface{}, 0, 1)
|
||||||
|
|
||||||
|
if health != nil {
|
||||||
|
|
||||||
|
r := make(map[string]interface{})
|
||||||
|
r["protocol"] = (*health).Protocol
|
||||||
|
r["port"] = (*health).Port
|
||||||
|
r["path"] = (*health).Path
|
||||||
|
r["check_interval_seconds"] = (*health).CheckIntervalSeconds
|
||||||
|
r["response_timeout_seconds"] = (*health).ResponseTimeoutSeconds
|
||||||
|
r["unhealthy_threshold"] = (*health).UnhealthyThreshold
|
||||||
|
r["healthy_threshold"] = (*health).HealthyThreshold
|
||||||
|
|
||||||
|
result = append(result, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenStickySessions(session *godo.StickySessions) []map[string]interface{} {
|
||||||
|
result := make([]map[string]interface{}, 0, 1)
|
||||||
|
|
||||||
|
if session != nil {
|
||||||
|
|
||||||
|
r := make(map[string]interface{})
|
||||||
|
r["type"] = (*session).Type
|
||||||
|
r["cookie_name"] = (*session).CookieName
|
||||||
|
r["cookie_ttl_seconds"] = (*session).CookieTtlSeconds
|
||||||
|
|
||||||
|
result = append(result, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenForwardingRules(rules []godo.ForwardingRule) []map[string]interface{} {
|
||||||
|
result := make([]map[string]interface{}, 0, 1)
|
||||||
|
|
||||||
|
if rules != nil {
|
||||||
|
for _, rule := range rules {
|
||||||
|
r := make(map[string]interface{})
|
||||||
|
r["entry_protocol"] = rule.EntryProtocol
|
||||||
|
r["entry_port"] = rule.EntryPort
|
||||||
|
r["target_protocol"] = rule.TargetProtocol
|
||||||
|
r["target_port"] = rule.TargetPort
|
||||||
|
r["certificate_id"] = rule.CertificateID
|
||||||
|
r["tls_passthrough"] = rule.TlsPassthrough
|
||||||
|
|
||||||
|
result = append(result, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -18,13 +18,14 @@ func Provider() terraform.ResourceProvider {
|
||||||
},
|
},
|
||||||
|
|
||||||
ResourcesMap: map[string]*schema.Resource{
|
ResourcesMap: map[string]*schema.Resource{
|
||||||
"digitalocean_domain": resourceDigitalOceanDomain(),
|
"digitalocean_domain": resourceDigitalOceanDomain(),
|
||||||
"digitalocean_droplet": resourceDigitalOceanDroplet(),
|
"digitalocean_droplet": resourceDigitalOceanDroplet(),
|
||||||
"digitalocean_floating_ip": resourceDigitalOceanFloatingIp(),
|
"digitalocean_floating_ip": resourceDigitalOceanFloatingIp(),
|
||||||
"digitalocean_record": resourceDigitalOceanRecord(),
|
"digitalocean_loadbalancer": resourceDigitalOceanLoadbalancer(),
|
||||||
"digitalocean_ssh_key": resourceDigitalOceanSSHKey(),
|
"digitalocean_record": resourceDigitalOceanRecord(),
|
||||||
"digitalocean_tag": resourceDigitalOceanTag(),
|
"digitalocean_ssh_key": resourceDigitalOceanSSHKey(),
|
||||||
"digitalocean_volume": resourceDigitalOceanVolume(),
|
"digitalocean_tag": resourceDigitalOceanTag(),
|
||||||
|
"digitalocean_volume": resourceDigitalOceanVolume(),
|
||||||
},
|
},
|
||||||
|
|
||||||
ConfigureFunc: providerConfigure,
|
ConfigureFunc: providerConfigure,
|
||||||
|
|
|
@ -0,0 +1,297 @@
|
||||||
|
package digitalocean
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/digitalocean/godo"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceDigitalOceanLoadbalancer() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceDigitalOceanLoadbalancerCreate,
|
||||||
|
Read: resourceDigitalOceanLoadbalancerRead,
|
||||||
|
Update: resourceDigitalOceanLoadbalancerUpdate,
|
||||||
|
Delete: resourceDigitalOceanLoadbalancerDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"name": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"region": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"algorithm": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "round_robin",
|
||||||
|
},
|
||||||
|
|
||||||
|
"forwarding_rule": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Required: true,
|
||||||
|
MinItems: 1,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"entry_protocol": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"entry_port": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"target_protocol": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"target_port": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"certificate_id": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"tls_passthrough": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"healthcheck": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
MaxItems: 1,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"protocol": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"check_interval_seconds": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Default: 10,
|
||||||
|
},
|
||||||
|
"response_timeout_seconds": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Default: 5,
|
||||||
|
},
|
||||||
|
"unhealthy_threshold": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Default: 3,
|
||||||
|
},
|
||||||
|
"healthy_threshold": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Default: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"sticky_sessions": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true, //this needs to be computed as the API returns a struct with none as the type
|
||||||
|
MaxItems: 1,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"type": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "none",
|
||||||
|
},
|
||||||
|
"cookie_name": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"cookie_ttl_seconds": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"droplet_ids": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"droplet_tag": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"redirect_http_to_https": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ip": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildLoadBalancerRequest(d *schema.ResourceData) (*godo.LoadBalancerRequest, error) {
|
||||||
|
opts := &godo.LoadBalancerRequest{
|
||||||
|
Name: d.Get("name").(string),
|
||||||
|
Region: d.Get("region").(string),
|
||||||
|
Algorithm: d.Get("algorithm").(string),
|
||||||
|
RedirectHttpToHttps: d.Get("redirect_http_to_https").(bool),
|
||||||
|
ForwardingRules: expandForwardingRules(d.Get("forwarding_rule").([]interface{})),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("droplet_ids"); ok {
|
||||||
|
var droplets []int
|
||||||
|
for _, id := range v.([]interface{}) {
|
||||||
|
i, err := strconv.Atoi(id.(string))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
droplets = append(droplets, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.DropletIDs = droplets
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("droplet_tag"); ok {
|
||||||
|
opts.Tag = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("healthcheck"); ok {
|
||||||
|
opts.HealthCheck = expandHealthCheck(v.([]interface{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("sticky_sessions"); ok {
|
||||||
|
opts.StickySessions = expandStickySessions(v.([]interface{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceDigitalOceanLoadbalancerCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*godo.Client)
|
||||||
|
|
||||||
|
log.Printf("[INFO] Create a Loadbalancer Request")
|
||||||
|
|
||||||
|
lbOpts, err := buildLoadBalancerRequest(d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Loadbalancer Create: %#v", lbOpts)
|
||||||
|
loadbalancer, _, err := client.LoadBalancers.Create(lbOpts)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating Load Balancer: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(loadbalancer.ID)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Waiting for Load Balancer (%s) to become active", d.Get("name"))
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"new"},
|
||||||
|
Target: []string{"active"},
|
||||||
|
Refresh: loadbalancerStateRefreshFunc(client, d.Id()),
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
MinTimeout: 15 * time.Second,
|
||||||
|
}
|
||||||
|
if _, err := stateConf.WaitForState(); err != nil {
|
||||||
|
return fmt.Errorf("Error waiting for Load Balancer (%s) to become active: %s", d.Get("name"), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceDigitalOceanLoadbalancerRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceDigitalOceanLoadbalancerRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*godo.Client)
|
||||||
|
|
||||||
|
log.Printf("[INFO] Reading the details of the Loadbalancer %s", d.Id())
|
||||||
|
loadbalancer, _, err := client.LoadBalancers.Get(d.Id())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error retrieving Loadbalancer: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("name", loadbalancer.Name)
|
||||||
|
d.Set("ip", loadbalancer.IP)
|
||||||
|
d.Set("algorithm", loadbalancer.Algorithm)
|
||||||
|
d.Set("region", loadbalancer.Region.Slug)
|
||||||
|
d.Set("redirect_http_to_https", loadbalancer.RedirectHttpToHttps)
|
||||||
|
d.Set("droplet_ids", flattenDropletIds(loadbalancer.DropletIDs))
|
||||||
|
d.Set("droplet_tag", loadbalancer.Tag)
|
||||||
|
|
||||||
|
if err := d.Set("sticky_sessions", flattenStickySessions(loadbalancer.StickySessions)); err != nil {
|
||||||
|
return fmt.Errorf("[DEBUG] Error setting Load Balancer sticky_sessions - error: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Set("healthcheck", flattenHealthChecks(loadbalancer.HealthCheck)); err != nil {
|
||||||
|
return fmt.Errorf("[DEBUG] Error setting Load Balancer healthcheck - error: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Set("forwarding_rule", flattenForwardingRules(loadbalancer.ForwardingRules)); err != nil {
|
||||||
|
return fmt.Errorf("[DEBUG] Error setting Load Balancer forwarding_rule - error: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceDigitalOceanLoadbalancerUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*godo.Client)
|
||||||
|
|
||||||
|
lbOpts, err := buildLoadBalancerRequest(d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Load Balancer Update: %#v", lbOpts)
|
||||||
|
_, _, err = client.LoadBalancers.Update(d.Id(), lbOpts)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error updating Load Balancer: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceDigitalOceanLoadbalancerRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceDigitalOceanLoadbalancerDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*godo.Client)
|
||||||
|
|
||||||
|
log.Printf("[INFO] Deleting Load Balancer: %s", d.Id())
|
||||||
|
_, err := client.LoadBalancers.Delete(d.Id())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error deleting Load Balancer: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,316 @@
|
||||||
|
package digitalocean
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/digitalocean/godo"
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccDigitalOceanLoadbalancer_Basic(t *testing.T) {
|
||||||
|
var loadbalancer godo.LoadBalancer
|
||||||
|
rInt := acctest.RandInt()
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckDigitalOceanLoadbalancerDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccCheckDigitalOceanLoadbalancerConfig_basic(rInt),
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
testAccCheckDigitalOceanLoadbalancerExists("digitalocean_loadbalancer.foobar", &loadbalancer),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "name", fmt.Sprintf("loadbalancer-%d", rInt)),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "region", "nyc3"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.entry_port", "80"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.entry_protocol", "http"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.target_port", "80"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.target_protocol", "http"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "healthcheck.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "healthcheck.0.port", "22"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "healthcheck.0.protocol", "tcp"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "droplet_ids.#", "1"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccDigitalOceanLoadbalancer_Updated(t *testing.T) {
|
||||||
|
var loadbalancer godo.LoadBalancer
|
||||||
|
rInt := acctest.RandInt()
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckDigitalOceanLoadbalancerDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccCheckDigitalOceanLoadbalancerConfig_basic(rInt),
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
testAccCheckDigitalOceanLoadbalancerExists("digitalocean_loadbalancer.foobar", &loadbalancer),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "name", fmt.Sprintf("loadbalancer-%d", rInt)),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "region", "nyc3"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.entry_port", "80"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.entry_protocol", "http"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.target_port", "80"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.target_protocol", "http"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "healthcheck.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "healthcheck.0.port", "22"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "healthcheck.0.protocol", "tcp"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "droplet_ids.#", "1"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Config: testAccCheckDigitalOceanLoadbalancerConfig_updated(rInt),
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
testAccCheckDigitalOceanLoadbalancerExists("digitalocean_loadbalancer.foobar", &loadbalancer),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "name", fmt.Sprintf("loadbalancer-%d", rInt)),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "region", "nyc3"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.entry_port", "81"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.entry_protocol", "http"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.target_port", "81"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.target_protocol", "http"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "healthcheck.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "healthcheck.0.port", "22"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "healthcheck.0.protocol", "tcp"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "droplet_ids.#", "2"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccDigitalOceanLoadbalancer_dropletTag(t *testing.T) {
|
||||||
|
var loadbalancer godo.LoadBalancer
|
||||||
|
rInt := acctest.RandInt()
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckDigitalOceanLoadbalancerDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccCheckDigitalOceanLoadbalancerConfig_dropletTag(rInt),
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
testAccCheckDigitalOceanLoadbalancerExists("digitalocean_loadbalancer.foobar", &loadbalancer),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "name", fmt.Sprintf("loadbalancer-%d", rInt)),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "region", "nyc3"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.entry_port", "80"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.entry_protocol", "http"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.target_port", "80"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "forwarding_rule.0.target_protocol", "http"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "healthcheck.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "healthcheck.0.port", "22"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "healthcheck.0.protocol", "tcp"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"digitalocean_loadbalancer.foobar", "droplet_tag", "sample"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckDigitalOceanLoadbalancerDestroy(s *terraform.State) error {
|
||||||
|
client := testAccProvider.Meta().(*godo.Client)
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "digitalocean_loadbalancer" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := client.LoadBalancers.Get(rs.Primary.ID)
|
||||||
|
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "404") {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error waiting for loadbalancer (%s) to be destroyed: %s",
|
||||||
|
rs.Primary.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckDigitalOceanLoadbalancerExists(n string, loadbalancer *godo.LoadBalancer) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[n]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No Loadbalancer ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := testAccProvider.Meta().(*godo.Client)
|
||||||
|
|
||||||
|
lb, _, err := client.LoadBalancers.Get(rs.Primary.ID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if lb.ID != rs.Primary.ID {
|
||||||
|
return fmt.Errorf("Loabalancer not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
*loadbalancer = *lb
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckDigitalOceanLoadbalancerConfig_basic(rInt int) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "digitalocean_droplet" "foobar" {
|
||||||
|
name = "foo-%d"
|
||||||
|
size = "512mb"
|
||||||
|
image = "centos-7-x64"
|
||||||
|
region = "nyc3"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "digitalocean_loadbalancer" "foobar" {
|
||||||
|
name = "loadbalancer-%d"
|
||||||
|
region = "nyc3"
|
||||||
|
|
||||||
|
forwarding_rule {
|
||||||
|
entry_port = 80
|
||||||
|
entry_protocol = "http"
|
||||||
|
|
||||||
|
target_port = 80
|
||||||
|
target_protocol = "http"
|
||||||
|
}
|
||||||
|
|
||||||
|
healthcheck {
|
||||||
|
port = 22
|
||||||
|
protocol = "tcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
droplet_ids = ["${digitalocean_droplet.foobar.id}"]
|
||||||
|
}`, rInt, rInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckDigitalOceanLoadbalancerConfig_updated(rInt int) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "digitalocean_droplet" "foobar" {
|
||||||
|
name = "foo-%d"
|
||||||
|
size = "512mb"
|
||||||
|
image = "centos-7-x64"
|
||||||
|
region = "nyc3"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "digitalocean_droplet" "foo" {
|
||||||
|
name = "foo-%d"
|
||||||
|
size = "512mb"
|
||||||
|
image = "centos-7-x64"
|
||||||
|
region = "nyc3"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "digitalocean_loadbalancer" "foobar" {
|
||||||
|
name = "loadbalancer-%d"
|
||||||
|
region = "nyc3"
|
||||||
|
|
||||||
|
forwarding_rule {
|
||||||
|
entry_port = 81
|
||||||
|
entry_protocol = "http"
|
||||||
|
|
||||||
|
target_port = 81
|
||||||
|
target_protocol = "http"
|
||||||
|
}
|
||||||
|
|
||||||
|
healthcheck {
|
||||||
|
port = 22
|
||||||
|
protocol = "tcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
droplet_ids = ["${digitalocean_droplet.foobar.id}","${digitalocean_droplet.foo.id}"]
|
||||||
|
}`, rInt, rInt, rInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckDigitalOceanLoadbalancerConfig_dropletTag(rInt int) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "digitalocean_tag" "barbaz" {
|
||||||
|
name = "sample"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "digitalocean_droplet" "foobar" {
|
||||||
|
name = "foo-%d"
|
||||||
|
size = "512mb"
|
||||||
|
image = "centos-7-x64"
|
||||||
|
region = "nyc3"
|
||||||
|
tags = ["${digitalocean_tag.barbaz.id}"]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "digitalocean_loadbalancer" "foobar" {
|
||||||
|
name = "loadbalancer-%d"
|
||||||
|
region = "nyc3"
|
||||||
|
|
||||||
|
forwarding_rule {
|
||||||
|
entry_port = 80
|
||||||
|
entry_protocol = "http"
|
||||||
|
|
||||||
|
target_port = 80
|
||||||
|
target_protocol = "http"
|
||||||
|
}
|
||||||
|
|
||||||
|
healthcheck {
|
||||||
|
port = 22
|
||||||
|
protocol = "tcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
droplet_tag = "${digitalocean_tag.barbaz.name}"
|
||||||
|
|
||||||
|
depends_on = ["digitalocean_droplet.foobar"]
|
||||||
|
}`, rInt, rInt)
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
---
|
||||||
|
layout: "digitalocean"
|
||||||
|
page_title: "DigitalOcean: digitalocean_loadbalancer"
|
||||||
|
sidebar_current: "docs-do-resource-loadbalancer"
|
||||||
|
description: |-
|
||||||
|
Provides a DigitalOcean Load Balancer resource. This can be used to create, modify, and delete Load Balancers.
|
||||||
|
---
|
||||||
|
|
||||||
|
# digitalocean\_loadbalancer
|
||||||
|
|
||||||
|
Provides a DigitalOcean Load Balancer resource. This can be used to create,
|
||||||
|
modify, and delete Load Balancers.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
resource "digitalocean_droplet" "web" {
|
||||||
|
name = "web-1"
|
||||||
|
size = "512mb"
|
||||||
|
image = "centos-7-x64"
|
||||||
|
region = "nyc3"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "digitalocean_loadbalancer" "public" {
|
||||||
|
name = "loadbalancer-1"
|
||||||
|
region = "nyc3"
|
||||||
|
|
||||||
|
forwarding_rule {
|
||||||
|
entry_port = 80
|
||||||
|
entry_protocol = "http"
|
||||||
|
|
||||||
|
target_port = 80
|
||||||
|
target_protocol = "http"
|
||||||
|
}
|
||||||
|
|
||||||
|
healthcheck {
|
||||||
|
port = 22
|
||||||
|
protocol = "tcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
droplet_ids = ["${digitalocean_droplet.web.id}"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `name` - (Required) The Load Balancer name
|
||||||
|
* `region` - (Required) The region to start in
|
||||||
|
* `algorithm` - (Optional) The load balancing algorithm used to determine
|
||||||
|
which backend Droplet will be selected by a client. It must be either `round_robin`
|
||||||
|
or `least_connections`. The default value is `round_robin`.
|
||||||
|
* `forwarding_rule` - (Required) A list of `forwarding_rule` to be assigned to the
|
||||||
|
Load Balancer. The `forwarding_rule` block is documented below.
|
||||||
|
* `healthcheck` - (Optional) A `healthcheck` block to be assigned to the
|
||||||
|
Load Balancer. The `healthcheck` block is documented below. Only 1 healthcheck is allowed.
|
||||||
|
* `sticky_sessions` - (Optional) A `sticky_sessions` block to be assigned to the
|
||||||
|
Load Balancer. The `sticky_sessions` block is documented below. Only 1 sticky_sessions block is allowed.
|
||||||
|
* `redirect_http_to_https` - (Optional) A boolean value indicating whether
|
||||||
|
HTTP requests to the Load Balancer on port 80 will be redirected to HTTPS on port 443.
|
||||||
|
Default value is `false`.
|
||||||
|
* `droplet_ids` (Optional) - A list of the IDs of each droplet to be attached to the Load Balancer.
|
||||||
|
* `droplet_tag` (Optional) - The name of a Droplet tag corresponding to Droplets to be assigned to the Load Balancer.
|
||||||
|
|
||||||
|
`forwarding_rule` supports the following:
|
||||||
|
|
||||||
|
* `entry_protocol` - (Required) The protocol used for traffic to the Load Balancer. The possible values are: `http`, `https`, or `tcp`.
|
||||||
|
* `entry_port` - (Required) An integer representing the port on which the Load Balancer instance will listen.
|
||||||
|
* `target_protocol` - (Required) The protocol used for traffic from the Load Balancer to the backend Droplets. The possible values are: `http`, `https`, or `tcp`.
|
||||||
|
* `target_port` - (Required) An integer representing the port on the backend Droplets to which the Load Balancer will send traffic.
|
||||||
|
* `certificate_id` - (Optional) The ID of the TLS certificate to be used for SSL termination.
|
||||||
|
* `tls_passthrough` - (Optional) A boolean value indicating whether SSL encrypted traffic will be passed through to the backend Droplets. The default value is `false`.
|
||||||
|
|
||||||
|
`sticky_sessions` supports the following:
|
||||||
|
|
||||||
|
* `type` - (Required) An attribute indicating how and if requests from a client will be persistently served by the same backend Droplet. The possible values are `cookies` or `none`. If not specified, the default value is `none`.
|
||||||
|
* `cookie_name` - (Optional) The name to be used for the cookie sent to the client. This attribute is required when using `cookies` for the sticky sessions type.
|
||||||
|
* `cookie_ttl_seconds` - (Optional) The number of seconds until the cookie set by the Load Balancer expires. This attribute is required when using `cookies` for the sticky sessions type.
|
||||||
|
|
||||||
|
|
||||||
|
`healthcheck` supports the following:
|
||||||
|
|
||||||
|
* `protocol` - (Required) The protocol used for health checks sent to the backend Droplets. The possible values are `http` or `tcp`.
|
||||||
|
* `port` - (Optional) An integer representing the port on the backend Droplets on which the health check will attempt a connection.
|
||||||
|
* `path` - (Optional) The path on the backend Droplets to which the Load Balancer instance will send a request.
|
||||||
|
* `check_interval_seconds` - (Optional) The number of seconds between between two consecutive health checks. If not specified, the default value is `10`.
|
||||||
|
* `response_timeout_seconds` - (Optional) The number of seconds the Load Balancer instance will wait for a response until marking a health check as failed. If not specified, the default value is `5`.
|
||||||
|
* `unhealthy_threshold` - (Optional) The number of times a health check must fail for a backend Droplet to be marked "unhealthy" and be removed from the pool. If not specified, the default value is `3`.
|
||||||
|
* `healthy_threshold` - (Optional) The number of times a health check must pass for a backend Droplet to be marked "healthy" and be re-added to the pool. If not specified, the default value is `5`.
|
||||||
|
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `id` - The ID of the Load Balancer
|
||||||
|
* `ip`- The ip of the Load Balancer
|
|
@ -25,6 +25,10 @@
|
||||||
<a href="/docs/providers/do/r/floating_ip.html">digitalocean_floating_ip</a>
|
<a href="/docs/providers/do/r/floating_ip.html">digitalocean_floating_ip</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-do-resource-loadbalancer") %>>
|
||||||
|
<a href="/docs/providers/do/r/loadbalancer.html">digitalocean_loadbalancer</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-do-resource-record") %>>
|
<li<%= sidebar_current("docs-do-resource-record") %>>
|
||||||
<a href="/docs/providers/do/r/record.html">digitalocean_record</a>
|
<a href="/docs/providers/do/r/record.html">digitalocean_record</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
Loading…
Reference in New Issue