github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/oneandone/resource_oneandone_loadbalancer.go (about)

     1  package oneandone
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/1and1/oneandone-cloudserver-sdk-go"
     6  	"github.com/hashicorp/terraform/helper/schema"
     7  	"github.com/hashicorp/terraform/helper/validation"
     8  	"log"
     9  	"strings"
    10  )
    11  
    12  func resourceOneandOneLoadbalancer() *schema.Resource {
    13  	return &schema.Resource{
    14  		Create: resourceOneandOneLoadbalancerCreate,
    15  		Read:   resourceOneandOneLoadbalancerRead,
    16  		Update: resourceOneandOneLoadbalancerUpdate,
    17  		Delete: resourceOneandOneLoadbalancerDelete,
    18  		Schema: map[string]*schema.Schema{
    19  
    20  			"name": {
    21  				Type:     schema.TypeString,
    22  				Required: true,
    23  			},
    24  			"description": {
    25  				Type:     schema.TypeString,
    26  				Optional: true,
    27  			},
    28  			"method": {
    29  				Type:         schema.TypeString,
    30  				Required:     true,
    31  				ValidateFunc: validateMethod,
    32  			},
    33  			"datacenter": {
    34  				Type:     schema.TypeString,
    35  				Optional: true,
    36  			},
    37  			"persistence": {
    38  				Type:     schema.TypeBool,
    39  				Optional: true,
    40  			},
    41  			"persistence_time": {
    42  				Type:     schema.TypeInt,
    43  				Optional: true,
    44  			},
    45  			"health_check_test": {
    46  				Type:     schema.TypeString,
    47  				Optional: true,
    48  			},
    49  			"health_check_interval": {
    50  				Type:     schema.TypeInt,
    51  				Optional: true,
    52  			},
    53  			"health_check_path": {
    54  				Type:     schema.TypeString,
    55  				Optional: true,
    56  			},
    57  			"health_check_path_parser": {
    58  				Type:     schema.TypeString,
    59  				Optional: true,
    60  			},
    61  			"rules": {
    62  				Type: schema.TypeList,
    63  				Elem: &schema.Resource{
    64  					Schema: map[string]*schema.Schema{
    65  						"protocol": {
    66  							Type:     schema.TypeString,
    67  							Required: true,
    68  						},
    69  						"port_balancer": {
    70  							Type:         schema.TypeInt,
    71  							Required:     true,
    72  							ValidateFunc: validation.IntBetween(1, 65535),
    73  						},
    74  						"port_server": {
    75  							Type:         schema.TypeInt,
    76  							Required:     true,
    77  							ValidateFunc: validation.IntBetween(1, 65535),
    78  						},
    79  						"source_ip": {
    80  							Type:     schema.TypeString,
    81  							Required: true,
    82  						},
    83  						"id": {
    84  							Type:     schema.TypeString,
    85  							Computed: true,
    86  						},
    87  					},
    88  				},
    89  				Required: true,
    90  			},
    91  		},
    92  	}
    93  }
    94  
    95  func resourceOneandOneLoadbalancerCreate(d *schema.ResourceData, meta interface{}) error {
    96  	config := meta.(*Config)
    97  
    98  	req := oneandone.LoadBalancerRequest{
    99  		Name:  d.Get("name").(string),
   100  		Rules: getLBRules(d),
   101  	}
   102  
   103  	if raw, ok := d.GetOk("description"); ok {
   104  		req.Description = raw.(string)
   105  	}
   106  
   107  	if raw, ok := d.GetOk("datacenter"); ok {
   108  		dcs, err := config.API.ListDatacenters()
   109  		if err != nil {
   110  			return fmt.Errorf("An error occured while fetching list of datacenters %s", err)
   111  		}
   112  
   113  		decenter := raw.(string)
   114  		for _, dc := range dcs {
   115  			if strings.ToLower(dc.CountryCode) == strings.ToLower(decenter) {
   116  				req.DatacenterId = dc.Id
   117  				break
   118  			}
   119  		}
   120  	}
   121  
   122  	if raw, ok := d.GetOk("method"); ok {
   123  		req.Method = raw.(string)
   124  	}
   125  
   126  	if raw, ok := d.GetOk("persistence"); ok {
   127  		req.Persistence = oneandone.Bool2Pointer(raw.(bool))
   128  	}
   129  	if raw, ok := d.GetOk("persistence_time"); ok {
   130  		req.PersistenceTime = oneandone.Int2Pointer(raw.(int))
   131  	}
   132  
   133  	if raw, ok := d.GetOk("health_check_test"); ok {
   134  		req.HealthCheckTest = raw.(string)
   135  	}
   136  	if raw, ok := d.GetOk("health_check_interval"); ok {
   137  		req.HealthCheckInterval = oneandone.Int2Pointer(raw.(int))
   138  	}
   139  	if raw, ok := d.GetOk("health_check_path"); ok {
   140  		req.HealthCheckPath = raw.(string)
   141  	}
   142  	if raw, ok := d.GetOk("health_check_path_parser"); ok {
   143  		req.HealthCheckPathParser = raw.(string)
   144  	}
   145  
   146  	lb_id, lb, err := config.API.CreateLoadBalancer(&req)
   147  	if err != nil {
   148  		return err
   149  	}
   150  
   151  	err = config.API.WaitForState(lb, "ACTIVE", 10, config.Retries)
   152  	if err != nil {
   153  		return err
   154  	}
   155  
   156  	d.SetId(lb_id)
   157  
   158  	return resourceOneandOneLoadbalancerRead(d, meta)
   159  }
   160  
   161  func getLBRules(d *schema.ResourceData) []oneandone.LoadBalancerRule {
   162  	var rules []oneandone.LoadBalancerRule
   163  
   164  	if raw, ok := d.GetOk("rules"); ok {
   165  		rawRules := raw.([]interface{})
   166  		log.Println("[DEBUG] raw rules:", raw)
   167  		for _, raw := range rawRules {
   168  			rl := raw.(map[string]interface{})
   169  			rule := oneandone.LoadBalancerRule{
   170  				Protocol: rl["protocol"].(string),
   171  			}
   172  
   173  			if rl["port_balancer"] != nil {
   174  				rule.PortBalancer = uint16(rl["port_balancer"].(int))
   175  			}
   176  			if rl["port_server"] != nil {
   177  				rule.PortServer = uint16(rl["port_server"].(int))
   178  			}
   179  
   180  			if rl["source_ip"] != nil {
   181  				rule.Source = rl["source_ip"].(string)
   182  			}
   183  
   184  			rules = append(rules, rule)
   185  		}
   186  	}
   187  	return rules
   188  }
   189  
   190  func resourceOneandOneLoadbalancerUpdate(d *schema.ResourceData, meta interface{}) error {
   191  	config := meta.(*Config)
   192  
   193  	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") {
   194  		lb := oneandone.LoadBalancerRequest{}
   195  		if d.HasChange("name") {
   196  			_, n := d.GetChange("name")
   197  			lb.Name = n.(string)
   198  		}
   199  		if d.HasChange("description") {
   200  			_, n := d.GetChange("description")
   201  			lb.Description = n.(string)
   202  		}
   203  		if d.HasChange("method") {
   204  			_, n := d.GetChange("method")
   205  			lb.Method = (n.(string))
   206  		}
   207  		if d.HasChange("persistence") {
   208  			_, n := d.GetChange("persistence")
   209  			lb.Persistence = oneandone.Bool2Pointer(n.(bool))
   210  		}
   211  		if d.HasChange("persistence_time") {
   212  			_, n := d.GetChange("persistence_time")
   213  			lb.PersistenceTime = oneandone.Int2Pointer(n.(int))
   214  		}
   215  		if d.HasChange("health_check_test") {
   216  			_, n := d.GetChange("health_check_test")
   217  			lb.HealthCheckTest = n.(string)
   218  		}
   219  		if d.HasChange("health_check_path") {
   220  			_, n := d.GetChange("health_check_path")
   221  			lb.HealthCheckPath = n.(string)
   222  		}
   223  		if d.HasChange("health_check_path_parser") {
   224  			_, n := d.GetChange("health_check_path_parser")
   225  			lb.HealthCheckPathParser = n.(string)
   226  		}
   227  
   228  		ss, err := config.API.UpdateLoadBalancer(d.Id(), &lb)
   229  
   230  		if err != nil {
   231  			return err
   232  		}
   233  		err = config.API.WaitForState(ss, "ACTIVE", 10, 30)
   234  		if err != nil {
   235  			return err
   236  		}
   237  
   238  	}
   239  
   240  	if d.HasChange("rules") {
   241  		oldR, newR := d.GetChange("rules")
   242  		oldValues := oldR.([]interface{})
   243  		newValues := newR.([]interface{})
   244  		if len(oldValues) > len(newValues) {
   245  			diff := difference(oldValues, newValues)
   246  			for _, old := range diff {
   247  				o := old.(map[string]interface{})
   248  				if o["id"] != nil {
   249  					old_id := o["id"].(string)
   250  					fw, err := config.API.DeleteLoadBalancerRule(d.Id(), old_id)
   251  					if err != nil {
   252  						return err
   253  					}
   254  
   255  					err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries)
   256  					if err != nil {
   257  						return err
   258  					}
   259  				}
   260  			}
   261  		} else {
   262  			var rules []oneandone.LoadBalancerRule
   263  			log.Println("[DEBUG] new values:", newValues)
   264  
   265  			for _, raw := range newValues {
   266  				rl := raw.(map[string]interface{})
   267  				log.Println("[DEBUG] rl:", rl)
   268  
   269  				if rl["id"].(string) == "" {
   270  					rule := oneandone.LoadBalancerRule{
   271  						Protocol: rl["protocol"].(string),
   272  					}
   273  
   274  					rule.PortServer = uint16(rl["port_server"].(int))
   275  					rule.PortBalancer = uint16(rl["port_balancer"].(int))
   276  
   277  					rule.Source = rl["source_ip"].(string)
   278  
   279  					log.Println("[DEBUG] adding to list", rl["protocol"], rl["source_ip"], rl["port_balancer"], rl["port_server"])
   280  					log.Println("[DEBUG] adding to list", rule)
   281  
   282  					rules = append(rules, rule)
   283  				}
   284  			}
   285  
   286  			log.Println("[DEBUG] new rules:", rules)
   287  
   288  			if len(rules) > 0 {
   289  				fw, err := config.API.AddLoadBalancerRules(d.Id(), rules)
   290  				if err != nil {
   291  					return err
   292  				}
   293  
   294  				err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries)
   295  			}
   296  		}
   297  	}
   298  
   299  	return resourceOneandOneLoadbalancerRead(d, meta)
   300  }
   301  
   302  func resourceOneandOneLoadbalancerRead(d *schema.ResourceData, meta interface{}) error {
   303  	config := meta.(*Config)
   304  
   305  	ss, err := config.API.GetLoadBalancer(d.Id())
   306  	if err != nil {
   307  		if strings.Contains(err.Error(), "404") {
   308  			d.SetId("")
   309  			return nil
   310  		}
   311  		return err
   312  	}
   313  
   314  	d.Set("name", ss.Name)
   315  	d.Set("description", ss.Description)
   316  	d.Set("datacenter", ss.Datacenter.CountryCode)
   317  	d.Set("method", ss.Method)
   318  	d.Set("persistence", ss.Persistence)
   319  	d.Set("persistence_time", ss.PersistenceTime)
   320  	d.Set("health_check_test", ss.HealthCheckTest)
   321  	d.Set("health_check_interval", ss.HealthCheckInterval)
   322  	d.Set("rules", getLoadbalancerRules(ss.Rules))
   323  
   324  	return nil
   325  }
   326  
   327  func getLoadbalancerRules(rules []oneandone.LoadBalancerRule) []map[string]interface{} {
   328  	raw := make([]map[string]interface{}, 0, len(rules))
   329  
   330  	for _, rule := range rules {
   331  
   332  		toadd := map[string]interface{}{
   333  			"id":            rule.Id,
   334  			"port_balancer": rule.PortBalancer,
   335  			"port_server":   rule.PortServer,
   336  			"protocol":      rule.Protocol,
   337  			"source_ip":     rule.Source,
   338  		}
   339  
   340  		raw = append(raw, toadd)
   341  	}
   342  
   343  	return raw
   344  
   345  }
   346  
   347  func resourceOneandOneLoadbalancerDelete(d *schema.ResourceData, meta interface{}) error {
   348  	config := meta.(*Config)
   349  
   350  	lb, err := config.API.DeleteLoadBalancer(d.Id())
   351  	if err != nil {
   352  		return err
   353  	}
   354  	err = config.API.WaitUntilDeleted(lb)
   355  	if err != nil {
   356  		return err
   357  	}
   358  
   359  	return nil
   360  }
   361  
   362  func validateMethod(v interface{}, k string) (ws []string, errors []error) {
   363  	value := v.(string)
   364  
   365  	if value != "ROUND_ROBIN" && value != "LEAST_CONNECTIONS" {
   366  		errors = append(errors, fmt.Errorf("%q value sholud be either 'ROUND_ROBIN' or 'LEAST_CONNECTIONS' not %q", k, value))
   367  	}
   368  
   369  	return
   370  }