Added basic validation with minor optimizations/fixes to the load balancer resource

This commit is contained in:
Tilen Faganel 2018-08-28 15:47:40 +01:00
parent fc729120f9
commit eecd675b76
No known key found for this signature in database
GPG Key ID: 3DDA5ABF228F8E7A
3 changed files with 175 additions and 53 deletions

View File

@ -3,10 +3,10 @@ package digitalocean
import (
"context"
"fmt"
"strconv"
"github.com/digitalocean/godo"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)
func loadbalancerStateRefreshFunc(client *godo.Client, loadbalancerId string) resource.StateRefreshFunc {
@ -82,13 +82,12 @@ func expandForwardingRules(config []interface{}) []godo.ForwardingRule {
return forwardingRules
}
func flattenDropletIds(list []int) []interface{} {
flatList := make([]interface{}, 0, len(list))
func flattenDropletIds(list []int) *schema.Set {
flatSet := schema.NewSet(schema.HashInt, []interface{}{})
for _, v := range list {
vStr := strconv.Itoa(v)
flatList = append(flatList, vStr)
flatSet.Add(v)
}
return flatList
return flatSet
}
func flattenHealthChecks(health *godo.HealthCheck) []map[string]interface{} {

View File

@ -4,12 +4,12 @@ import (
"context"
"fmt"
"log"
"strconv"
"time"
"github.com/digitalocean/godo"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
)
func resourceDigitalOceanLoadbalancer() *schema.Resource {
@ -23,21 +23,26 @@ func resourceDigitalOceanLoadbalancer() *schema.Resource {
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"region": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.NoZeroValues,
},
"algorithm": {
Type: schema.TypeString,
Optional: true,
Default: "round_robin",
ValidateFunc: validation.StringInSlice([]string{
"round_robin",
"least_connections",
}, false),
},
"forwarding_rule": {
@ -49,22 +54,37 @@ func resourceDigitalOceanLoadbalancer() *schema.Resource {
"entry_protocol": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
"http",
"https",
"http2",
"tcp",
}, false),
},
"entry_port": {
Type: schema.TypeInt,
Required: true,
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(1, 65535),
},
"target_protocol": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
"http",
"https",
"http2",
"tcp",
}, false),
},
"target_port": {
Type: schema.TypeInt,
Required: true,
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(1, 65535),
},
"certificate_id": {
Type: schema.TypeString,
Optional: true,
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.NoZeroValues,
},
"tls_passthrough": {
Type: schema.TypeBool,
@ -78,40 +98,51 @@ func resourceDigitalOceanLoadbalancer() *schema.Resource {
"healthcheck": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"protocol": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
"http",
"tcp",
}, false),
},
"port": {
Type: schema.TypeInt,
Required: true,
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(1, 65535),
},
"path": {
Type: schema.TypeString,
Optional: true,
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.NoZeroValues,
},
"check_interval_seconds": {
Type: schema.TypeInt,
Optional: true,
Default: 10,
Type: schema.TypeInt,
Optional: true,
Default: 10,
ValidateFunc: validation.IntBetween(3, 300),
},
"response_timeout_seconds": {
Type: schema.TypeInt,
Optional: true,
Default: 5,
Type: schema.TypeInt,
Optional: true,
Default: 5,
ValidateFunc: validation.IntBetween(3, 300),
},
"unhealthy_threshold": {
Type: schema.TypeInt,
Optional: true,
Default: 3,
Type: schema.TypeInt,
Optional: true,
Default: 3,
ValidateFunc: validation.IntBetween(2, 10),
},
"healthy_threshold": {
Type: schema.TypeInt,
Optional: true,
Default: 5,
Type: schema.TypeInt,
Optional: true,
Default: 5,
ValidateFunc: validation.IntBetween(2, 10),
},
},
},
@ -128,24 +159,31 @@ func resourceDigitalOceanLoadbalancer() *schema.Resource {
Type: schema.TypeString,
Optional: true,
Default: "none",
ValidateFunc: validation.StringInSlice([]string{
"cookies",
"none",
}, false),
},
"cookie_name": {
Type: schema.TypeString,
Optional: true,
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(2, 40),
},
"cookie_ttl_seconds": {
Type: schema.TypeInt,
Optional: true,
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntAtLeast(1),
},
},
},
},
"droplet_ids": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Computed: true,
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeInt},
Optional: true,
Computed: true,
ConflictsWith: []string{"droplet_tag"},
},
"droplet_tag": {
@ -166,6 +204,48 @@ func resourceDigitalOceanLoadbalancer() *schema.Resource {
Computed: true,
},
},
CustomizeDiff: func(diff *schema.ResourceDiff, v interface{}) error {
if _, hasHealthCheck := diff.GetOk("healthcheck"); hasHealthCheck {
healthCheckProtocol := diff.Get("healthcheck.0.protocol").(string)
_, hasPath := diff.GetOk("healthcheck.0.path")
if healthCheckProtocol == "http" {
if !hasPath {
return fmt.Errorf("health check `path` is required for when protocol is `http`")
}
} else {
if hasPath {
return fmt.Errorf("health check `path` is not allowed for when protocol is `tcp`")
}
}
}
if _, hasStickySession := diff.GetOk("sticky_sessions.#"); hasStickySession {
sessionType := diff.Get("sticky_sessions.0.type").(string)
_, hasCookieName := diff.GetOk("sticky_sessions.0.cookie_name")
_, hasTtlSeconds := diff.GetOk("sticky_sessions.0.cookie_ttl_seconds")
if sessionType == "cookies" {
if !hasCookieName {
return fmt.Errorf("sticky sessions `cookie_name` is required for when type is `cookie`")
}
if !hasTtlSeconds {
return fmt.Errorf("sticky sessions `cookie_ttl_seconds` is required for when type is `cookie`")
}
} else {
if hasCookieName {
return fmt.Errorf("sticky sessions `cookie_name` is not allowed for when type is `none`")
}
if hasTtlSeconds {
return fmt.Errorf("sticky sessions `cookie_ttl_seconds` is not allowed for when type is `none`")
}
}
}
return nil
},
}
}
@ -178,23 +258,17 @@ func buildLoadBalancerRequest(d *schema.ResourceData) (*godo.LoadBalancerRequest
ForwardingRules: expandForwardingRules(d.Get("forwarding_rule").([]interface{})),
}
if v, ok := d.GetOk("droplet_ids"); ok {
if v, ok := d.GetOk("droplet_tag"); ok {
opts.Tag = v.(string)
} else 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)
for _, id := range v.(*schema.Set).List() {
droplets = append(droplets, id.(int))
}
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{}))
}
@ -288,6 +362,8 @@ func resourceDigitalOceanLoadbalancerUpdate(d *schema.ResourceData, meta interfa
return err
}
log.Printf("UIIIII: %v", lbOpts)
log.Printf("[DEBUG] Load Balancer Update: %#v", lbOpts)
_, _, err = client.LoadBalancers.Update(context.Background(), d.Id(), lbOpts)
if err != nil {

View File

@ -42,6 +42,53 @@ resource "digitalocean_loadbalancer" "public" {
}
```
When managing certificates attached to the load balancer, make sure to add the `create_before_destroy`
lifecycle property in order to ensure the certificate is correctly updated when changed. The order of
operations will then be: `Create new certificate` -> `Update loadbalancer with new certificate` ->
`Delete old certificate`. When doing so, you must also change the name of the certificate,
as there cannot be multiple certificates with the same name in an account.
```hcl
resource "digitalocean_certificate" "cert" {
name = "cert"
private_key = "${file("key.pem")}"
leaf_certificate = "${file("cert.pem")}"
lifecycle {
create_before_destroy = true
}
}
resource "digitalocean_droplet" "web" {
name = "web-1"
size = "s-1vcpu-1gb"
image = "centos-7-x64"
region = "nyc3"
}
resource "digitalocean_loadbalancer" "public" {
name = "loadbalancer-1"
region = "nyc3"
forwarding_rule {
entry_port = 443
entry_protocol = "https"
target_port = 80
target_protocol = "http"
certificate_id = "${digitalocean_certificate.cert.id}"
}
healthcheck {
port = 22
protocol = "tcp"
}
droplet_ids = ["${digitalocean_droplet.web.id}"]
}
```
## Argument Reference
The following arguments are supported: