github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/digitalocean/resource_digitalocean_loadbalancer.go (about) 1 package digitalocean 2 3 import ( 4 "context" 5 "fmt" 6 "log" 7 "strconv" 8 "time" 9 10 "github.com/digitalocean/godo" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceDigitalOceanLoadbalancer() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceDigitalOceanLoadbalancerCreate, 18 Read: resourceDigitalOceanLoadbalancerRead, 19 Update: resourceDigitalOceanLoadbalancerUpdate, 20 Delete: resourceDigitalOceanLoadbalancerDelete, 21 22 Schema: map[string]*schema.Schema{ 23 "name": { 24 Type: schema.TypeString, 25 Required: true, 26 }, 27 28 "region": { 29 Type: schema.TypeString, 30 Required: true, 31 ForceNew: true, 32 }, 33 34 "algorithm": { 35 Type: schema.TypeString, 36 Optional: true, 37 Default: "round_robin", 38 }, 39 40 "forwarding_rule": { 41 Type: schema.TypeList, 42 Required: true, 43 MinItems: 1, 44 Elem: &schema.Resource{ 45 Schema: map[string]*schema.Schema{ 46 "entry_protocol": { 47 Type: schema.TypeString, 48 Required: true, 49 }, 50 "entry_port": { 51 Type: schema.TypeInt, 52 Required: true, 53 }, 54 "target_protocol": { 55 Type: schema.TypeString, 56 Required: true, 57 }, 58 "target_port": { 59 Type: schema.TypeInt, 60 Required: true, 61 }, 62 "certificate_id": { 63 Type: schema.TypeString, 64 Optional: true, 65 }, 66 "tls_passthrough": { 67 Type: schema.TypeBool, 68 Optional: true, 69 Default: false, 70 }, 71 }, 72 }, 73 }, 74 75 "healthcheck": { 76 Type: schema.TypeList, 77 Optional: true, 78 MaxItems: 1, 79 Elem: &schema.Resource{ 80 Schema: map[string]*schema.Schema{ 81 "protocol": { 82 Type: schema.TypeString, 83 Required: true, 84 }, 85 "port": { 86 Type: schema.TypeInt, 87 Required: true, 88 }, 89 "path": { 90 Type: schema.TypeString, 91 Optional: true, 92 }, 93 "check_interval_seconds": { 94 Type: schema.TypeInt, 95 Optional: true, 96 Default: 10, 97 }, 98 "response_timeout_seconds": { 99 Type: schema.TypeInt, 100 Optional: true, 101 Default: 5, 102 }, 103 "unhealthy_threshold": { 104 Type: schema.TypeInt, 105 Optional: true, 106 Default: 3, 107 }, 108 "healthy_threshold": { 109 Type: schema.TypeInt, 110 Optional: true, 111 Default: 5, 112 }, 113 }, 114 }, 115 }, 116 117 "sticky_sessions": { 118 Type: schema.TypeList, 119 Optional: true, 120 Computed: true, //this needs to be computed as the API returns a struct with none as the type 121 MaxItems: 1, 122 Elem: &schema.Resource{ 123 Schema: map[string]*schema.Schema{ 124 "type": { 125 Type: schema.TypeString, 126 Optional: true, 127 Default: "none", 128 }, 129 "cookie_name": { 130 Type: schema.TypeString, 131 Optional: true, 132 }, 133 "cookie_ttl_seconds": { 134 Type: schema.TypeInt, 135 Optional: true, 136 }, 137 }, 138 }, 139 }, 140 141 "droplet_ids": { 142 Type: schema.TypeList, 143 Elem: &schema.Schema{Type: schema.TypeString}, 144 Optional: true, 145 }, 146 147 "droplet_tag": { 148 Type: schema.TypeString, 149 Optional: true, 150 }, 151 152 "redirect_http_to_https": { 153 Type: schema.TypeBool, 154 Optional: true, 155 Default: false, 156 }, 157 158 "ip": { 159 Type: schema.TypeString, 160 Computed: true, 161 }, 162 }, 163 } 164 } 165 166 func buildLoadBalancerRequest(d *schema.ResourceData) (*godo.LoadBalancerRequest, error) { 167 opts := &godo.LoadBalancerRequest{ 168 Name: d.Get("name").(string), 169 Region: d.Get("region").(string), 170 Algorithm: d.Get("algorithm").(string), 171 RedirectHttpToHttps: d.Get("redirect_http_to_https").(bool), 172 ForwardingRules: expandForwardingRules(d.Get("forwarding_rule").([]interface{})), 173 } 174 175 if v, ok := d.GetOk("droplet_ids"); ok { 176 var droplets []int 177 for _, id := range v.([]interface{}) { 178 i, err := strconv.Atoi(id.(string)) 179 if err != nil { 180 return nil, err 181 } 182 droplets = append(droplets, i) 183 } 184 185 opts.DropletIDs = droplets 186 } 187 188 if v, ok := d.GetOk("droplet_tag"); ok { 189 opts.Tag = v.(string) 190 } 191 192 if v, ok := d.GetOk("healthcheck"); ok { 193 opts.HealthCheck = expandHealthCheck(v.([]interface{})) 194 } 195 196 if v, ok := d.GetOk("sticky_sessions"); ok { 197 opts.StickySessions = expandStickySessions(v.([]interface{})) 198 } 199 200 return opts, nil 201 } 202 203 func resourceDigitalOceanLoadbalancerCreate(d *schema.ResourceData, meta interface{}) error { 204 client := meta.(*godo.Client) 205 206 log.Printf("[INFO] Create a Loadbalancer Request") 207 208 lbOpts, err := buildLoadBalancerRequest(d) 209 if err != nil { 210 return err 211 } 212 213 log.Printf("[DEBUG] Loadbalancer Create: %#v", lbOpts) 214 loadbalancer, _, err := client.LoadBalancers.Create(context.Background(), lbOpts) 215 if err != nil { 216 return fmt.Errorf("Error creating Load Balancer: %s", err) 217 } 218 219 d.SetId(loadbalancer.ID) 220 221 log.Printf("[DEBUG] Waiting for Load Balancer (%s) to become active", d.Get("name")) 222 stateConf := &resource.StateChangeConf{ 223 Pending: []string{"new"}, 224 Target: []string{"active"}, 225 Refresh: loadbalancerStateRefreshFunc(client, d.Id()), 226 Timeout: 10 * time.Minute, 227 MinTimeout: 15 * time.Second, 228 } 229 if _, err := stateConf.WaitForState(); err != nil { 230 return fmt.Errorf("Error waiting for Load Balancer (%s) to become active: %s", d.Get("name"), err) 231 } 232 233 return resourceDigitalOceanLoadbalancerRead(d, meta) 234 } 235 236 func resourceDigitalOceanLoadbalancerRead(d *schema.ResourceData, meta interface{}) error { 237 client := meta.(*godo.Client) 238 239 log.Printf("[INFO] Reading the details of the Loadbalancer %s", d.Id()) 240 loadbalancer, resp, err := client.LoadBalancers.Get(context.Background(), d.Id()) 241 if err != nil { 242 if resp != nil && resp.StatusCode == 404 { 243 log.Printf("[WARN] DigitalOcean Load Balancer (%s) not found", d.Id()) 244 d.SetId("") 245 return nil 246 } 247 return fmt.Errorf("Error retrieving Loadbalancer: %s", err) 248 } 249 250 d.Set("name", loadbalancer.Name) 251 d.Set("ip", loadbalancer.IP) 252 d.Set("algorithm", loadbalancer.Algorithm) 253 d.Set("region", loadbalancer.Region.Slug) 254 d.Set("redirect_http_to_https", loadbalancer.RedirectHttpToHttps) 255 d.Set("droplet_ids", flattenDropletIds(loadbalancer.DropletIDs)) 256 d.Set("droplet_tag", loadbalancer.Tag) 257 258 if err := d.Set("sticky_sessions", flattenStickySessions(loadbalancer.StickySessions)); err != nil { 259 return fmt.Errorf("[DEBUG] Error setting Load Balancer sticky_sessions - error: %#v", err) 260 } 261 262 if err := d.Set("healthcheck", flattenHealthChecks(loadbalancer.HealthCheck)); err != nil { 263 return fmt.Errorf("[DEBUG] Error setting Load Balancer healthcheck - error: %#v", err) 264 } 265 266 if err := d.Set("forwarding_rule", flattenForwardingRules(loadbalancer.ForwardingRules)); err != nil { 267 return fmt.Errorf("[DEBUG] Error setting Load Balancer forwarding_rule - error: %#v", err) 268 } 269 270 return nil 271 272 } 273 274 func resourceDigitalOceanLoadbalancerUpdate(d *schema.ResourceData, meta interface{}) error { 275 client := meta.(*godo.Client) 276 277 lbOpts, err := buildLoadBalancerRequest(d) 278 if err != nil { 279 return err 280 } 281 282 log.Printf("[DEBUG] Load Balancer Update: %#v", lbOpts) 283 _, _, err = client.LoadBalancers.Update(context.Background(), d.Id(), lbOpts) 284 if err != nil { 285 return fmt.Errorf("Error updating Load Balancer: %s", err) 286 } 287 288 return resourceDigitalOceanLoadbalancerRead(d, meta) 289 } 290 291 func resourceDigitalOceanLoadbalancerDelete(d *schema.ResourceData, meta interface{}) error { 292 client := meta.(*godo.Client) 293 294 log.Printf("[INFO] Deleting Load Balancer: %s", d.Id()) 295 _, err := client.LoadBalancers.Delete(context.Background(), d.Id()) 296 if err != nil { 297 return fmt.Errorf("Error deleting Load Balancer: %s", err) 298 } 299 300 d.SetId("") 301 return nil 302 303 }