371 lines
9.0 KiB
Go
371 lines
9.0 KiB
Go
package oneandone
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/1and1/oneandone-cloudserver-sdk-go"
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
"github.com/hashicorp/terraform/helper/validation"
|
|
"log"
|
|
"strings"
|
|
)
|
|
|
|
func resourceOneandOneLoadbalancer() *schema.Resource {
|
|
return &schema.Resource{
|
|
Create: resourceOneandOneLoadbalancerCreate,
|
|
Read: resourceOneandOneLoadbalancerRead,
|
|
Update: resourceOneandOneLoadbalancerUpdate,
|
|
Delete: resourceOneandOneLoadbalancerDelete,
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"name": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
"description": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
},
|
|
"method": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ValidateFunc: validateMethod,
|
|
},
|
|
"datacenter": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
},
|
|
"persistence": {
|
|
Type: schema.TypeBool,
|
|
Optional: true,
|
|
},
|
|
"persistence_time": {
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
},
|
|
"health_check_test": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
},
|
|
"health_check_interval": {
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
},
|
|
"health_check_path": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
},
|
|
"health_check_path_parser": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
},
|
|
"rules": {
|
|
Type: schema.TypeList,
|
|
Elem: &schema.Resource{
|
|
Schema: map[string]*schema.Schema{
|
|
"protocol": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
"port_balancer": {
|
|
Type: schema.TypeInt,
|
|
Required: true,
|
|
ValidateFunc: validation.IntBetween(1, 65535),
|
|
},
|
|
"port_server": {
|
|
Type: schema.TypeInt,
|
|
Required: true,
|
|
ValidateFunc: validation.IntBetween(1, 65535),
|
|
},
|
|
"source_ip": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
"id": {
|
|
Type: schema.TypeString,
|
|
Computed: true,
|
|
},
|
|
},
|
|
},
|
|
Required: true,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func resourceOneandOneLoadbalancerCreate(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
|
|
req := oneandone.LoadBalancerRequest{
|
|
Name: d.Get("name").(string),
|
|
Rules: getLBRules(d),
|
|
}
|
|
|
|
if raw, ok := d.GetOk("description"); ok {
|
|
req.Description = raw.(string)
|
|
}
|
|
|
|
if raw, ok := d.GetOk("datacenter"); ok {
|
|
dcs, err := config.API.ListDatacenters()
|
|
if err != nil {
|
|
return fmt.Errorf("An error occured while fetching list of datacenters %s", err)
|
|
}
|
|
|
|
decenter := raw.(string)
|
|
for _, dc := range dcs {
|
|
if strings.ToLower(dc.CountryCode) == strings.ToLower(decenter) {
|
|
req.DatacenterId = dc.Id
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if raw, ok := d.GetOk("method"); ok {
|
|
req.Method = raw.(string)
|
|
}
|
|
|
|
if raw, ok := d.GetOk("persistence"); ok {
|
|
req.Persistence = oneandone.Bool2Pointer(raw.(bool))
|
|
}
|
|
if raw, ok := d.GetOk("persistence_time"); ok {
|
|
req.PersistenceTime = oneandone.Int2Pointer(raw.(int))
|
|
}
|
|
|
|
if raw, ok := d.GetOk("health_check_test"); ok {
|
|
req.HealthCheckTest = raw.(string)
|
|
}
|
|
if raw, ok := d.GetOk("health_check_interval"); ok {
|
|
req.HealthCheckInterval = oneandone.Int2Pointer(raw.(int))
|
|
}
|
|
if raw, ok := d.GetOk("health_check_path"); ok {
|
|
req.HealthCheckPath = raw.(string)
|
|
}
|
|
if raw, ok := d.GetOk("health_check_path_parser"); ok {
|
|
req.HealthCheckPathParser = raw.(string)
|
|
}
|
|
|
|
lb_id, lb, err := config.API.CreateLoadBalancer(&req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = config.API.WaitForState(lb, "ACTIVE", 10, config.Retries)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
d.SetId(lb_id)
|
|
|
|
return resourceOneandOneLoadbalancerRead(d, meta)
|
|
}
|
|
|
|
func getLBRules(d *schema.ResourceData) []oneandone.LoadBalancerRule {
|
|
var rules []oneandone.LoadBalancerRule
|
|
|
|
if raw, ok := d.GetOk("rules"); ok {
|
|
rawRules := raw.([]interface{})
|
|
log.Println("[DEBUG] raw rules:", raw)
|
|
for _, raw := range rawRules {
|
|
rl := raw.(map[string]interface{})
|
|
rule := oneandone.LoadBalancerRule{
|
|
Protocol: rl["protocol"].(string),
|
|
}
|
|
|
|
if rl["port_balancer"] != nil {
|
|
rule.PortBalancer = uint16(rl["port_balancer"].(int))
|
|
}
|
|
if rl["port_server"] != nil {
|
|
rule.PortServer = uint16(rl["port_server"].(int))
|
|
}
|
|
|
|
if rl["source_ip"] != nil {
|
|
rule.Source = rl["source_ip"].(string)
|
|
}
|
|
|
|
rules = append(rules, rule)
|
|
}
|
|
}
|
|
return rules
|
|
}
|
|
|
|
func resourceOneandOneLoadbalancerUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
|
|
if d.HasChange("name") || d.HasChange("description") || d.HasChange("method") || d.HasChange("persistence") || d.HasChange("persistence_time") || d.HasChange("health_check_test") || d.HasChange("health_check_interval") {
|
|
lb := oneandone.LoadBalancerRequest{}
|
|
if d.HasChange("name") {
|
|
_, n := d.GetChange("name")
|
|
lb.Name = n.(string)
|
|
}
|
|
if d.HasChange("description") {
|
|
_, n := d.GetChange("description")
|
|
lb.Description = n.(string)
|
|
}
|
|
if d.HasChange("method") {
|
|
_, n := d.GetChange("method")
|
|
lb.Method = (n.(string))
|
|
}
|
|
if d.HasChange("persistence") {
|
|
_, n := d.GetChange("persistence")
|
|
lb.Persistence = oneandone.Bool2Pointer(n.(bool))
|
|
}
|
|
if d.HasChange("persistence_time") {
|
|
_, n := d.GetChange("persistence_time")
|
|
lb.PersistenceTime = oneandone.Int2Pointer(n.(int))
|
|
}
|
|
if d.HasChange("health_check_test") {
|
|
_, n := d.GetChange("health_check_test")
|
|
lb.HealthCheckTest = n.(string)
|
|
}
|
|
if d.HasChange("health_check_path") {
|
|
_, n := d.GetChange("health_check_path")
|
|
lb.HealthCheckPath = n.(string)
|
|
}
|
|
if d.HasChange("health_check_path_parser") {
|
|
_, n := d.GetChange("health_check_path_parser")
|
|
lb.HealthCheckPathParser = n.(string)
|
|
}
|
|
|
|
ss, err := config.API.UpdateLoadBalancer(d.Id(), &lb)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = config.API.WaitForState(ss, "ACTIVE", 10, 30)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
}
|
|
|
|
if d.HasChange("rules") {
|
|
oldR, newR := d.GetChange("rules")
|
|
oldValues := oldR.([]interface{})
|
|
newValues := newR.([]interface{})
|
|
if len(oldValues) > len(newValues) {
|
|
diff := difference(oldValues, newValues)
|
|
for _, old := range diff {
|
|
o := old.(map[string]interface{})
|
|
if o["id"] != nil {
|
|
old_id := o["id"].(string)
|
|
fw, err := config.API.DeleteLoadBalancerRule(d.Id(), old_id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
var rules []oneandone.LoadBalancerRule
|
|
log.Println("[DEBUG] new values:", newValues)
|
|
|
|
for _, raw := range newValues {
|
|
rl := raw.(map[string]interface{})
|
|
log.Println("[DEBUG] rl:", rl)
|
|
|
|
if rl["id"].(string) == "" {
|
|
rule := oneandone.LoadBalancerRule{
|
|
Protocol: rl["protocol"].(string),
|
|
}
|
|
|
|
rule.PortServer = uint16(rl["port_server"].(int))
|
|
rule.PortBalancer = uint16(rl["port_balancer"].(int))
|
|
|
|
rule.Source = rl["source_ip"].(string)
|
|
|
|
log.Println("[DEBUG] adding to list", rl["protocol"], rl["source_ip"], rl["port_balancer"], rl["port_server"])
|
|
log.Println("[DEBUG] adding to list", rule)
|
|
|
|
rules = append(rules, rule)
|
|
}
|
|
}
|
|
|
|
log.Println("[DEBUG] new rules:", rules)
|
|
|
|
if len(rules) > 0 {
|
|
fw, err := config.API.AddLoadBalancerRules(d.Id(), rules)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries)
|
|
}
|
|
}
|
|
}
|
|
|
|
return resourceOneandOneLoadbalancerRead(d, meta)
|
|
}
|
|
|
|
func resourceOneandOneLoadbalancerRead(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
|
|
ss, err := config.API.GetLoadBalancer(d.Id())
|
|
if err != nil {
|
|
if strings.Contains(err.Error(), "404") {
|
|
d.SetId("")
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
d.Set("name", ss.Name)
|
|
d.Set("description", ss.Description)
|
|
d.Set("datacenter", ss.Datacenter.CountryCode)
|
|
d.Set("method", ss.Method)
|
|
d.Set("persistence", ss.Persistence)
|
|
d.Set("persistence_time", ss.PersistenceTime)
|
|
d.Set("health_check_test", ss.HealthCheckTest)
|
|
d.Set("health_check_interval", ss.HealthCheckInterval)
|
|
d.Set("rules", getLoadbalancerRules(ss.Rules))
|
|
|
|
return nil
|
|
}
|
|
|
|
func getLoadbalancerRules(rules []oneandone.LoadBalancerRule) []map[string]interface{} {
|
|
raw := make([]map[string]interface{}, 0, len(rules))
|
|
|
|
for _, rule := range rules {
|
|
|
|
toadd := map[string]interface{}{
|
|
"id": rule.Id,
|
|
"port_balancer": rule.PortBalancer,
|
|
"port_server": rule.PortServer,
|
|
"protocol": rule.Protocol,
|
|
"source_ip": rule.Source,
|
|
}
|
|
|
|
raw = append(raw, toadd)
|
|
}
|
|
|
|
return raw
|
|
|
|
}
|
|
|
|
func resourceOneandOneLoadbalancerDelete(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
|
|
lb, err := config.API.DeleteLoadBalancer(d.Id())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = config.API.WaitUntilDeleted(lb)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateMethod(v interface{}, k string) (ws []string, errors []error) {
|
|
value := v.(string)
|
|
|
|
if value != "ROUND_ROBIN" && value != "LEAST_CONNECTIONS" {
|
|
errors = append(errors, fmt.Errorf("%q value sholud be either 'ROUND_ROBIN' or 'LEAST_CONNECTIONS' not %q", k, value))
|
|
}
|
|
|
|
return
|
|
}
|