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 }