github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/clc/resource_clc_public_ip.go (about) 1 package clc 2 3 import ( 4 "fmt" 5 "log" 6 "strconv" 7 8 clc "github.com/CenturyLinkCloud/clc-sdk" 9 "github.com/CenturyLinkCloud/clc-sdk/server" 10 11 "github.com/hashicorp/terraform/helper/schema" 12 ) 13 14 func resourceCLCPublicIP() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceCLCPublicIPCreate, 17 Read: resourceCLCPublicIPRead, 18 Update: resourceCLCPublicIPUpdate, 19 Delete: resourceCLCPublicIPDelete, 20 Schema: map[string]*schema.Schema{ 21 "server_id": &schema.Schema{ 22 Type: schema.TypeString, 23 Required: true, 24 ForceNew: true, 25 }, 26 "internal_ip_address": &schema.Schema{ 27 Type: schema.TypeString, 28 Optional: true, 29 Computed: true, 30 Default: nil, 31 }, 32 "ports": &schema.Schema{ 33 Type: schema.TypeList, 34 Required: true, 35 Elem: &schema.Schema{Type: schema.TypeMap}, 36 }, 37 "source_restrictions": &schema.Schema{ 38 Type: schema.TypeList, 39 Optional: true, 40 Elem: &schema.Schema{Type: schema.TypeMap}, 41 }, 42 }, 43 } 44 } 45 46 func resourceCLCPublicIPCreate(d *schema.ResourceData, meta interface{}) error { 47 client := meta.(*clc.Client) 48 sid := d.Get("server_id").(string) 49 priv := d.Get("internal_ip_address").(string) 50 ports, sources := parseIPSpec(d) 51 req := server.PublicIP{ 52 Ports: *ports, 53 SourceRestrictions: *sources, 54 } 55 56 // since the API doesn't tell us the public IP it allocated, 57 // track what was added after the call. 58 ips := make(map[string]string) 59 prev, err := client.Server.Get(sid) 60 if err != nil { 61 return fmt.Errorf("Failed finding server %v: %v", sid, err) 62 } 63 for _, i := range prev.Details.IPaddresses { 64 ips[i.Internal] = i.Public 65 } 66 67 if priv != "" { 68 // use existing private ip 69 if _, present := ips[priv]; !present { 70 return fmt.Errorf("Failed finding internal ip to use %v", priv) 71 } 72 req.InternalIP = priv 73 } 74 // execute the request 75 resp, err := client.Server.AddPublicIP(sid, req) 76 if err != nil { 77 return fmt.Errorf("Failed reserving public ip: %v", err) 78 } 79 err = waitStatus(client, resp.ID) 80 if err != nil { 81 return err 82 } 83 84 server, err := client.Server.Get(sid) 85 if err != nil { 86 return fmt.Errorf("Failed refreshing server for public ip: %v", err) 87 } 88 for _, i := range server.Details.IPaddresses { 89 if priv != "" && i.Internal == priv { 90 // bind 91 log.Printf("[DEBUG] Public IP bound on existing internal:%v - %v", i.Internal, i.Public) 92 d.SetId(i.Public) 93 break 94 } else if ips[i.Internal] == "" && i.Public != "" { 95 // allocate 96 log.Printf("[DEBUG] Public IP allocated on new internal:%v - %v", i.Internal, i.Public) 97 d.SetId(i.Public) 98 break 99 } 100 } 101 return resourceCLCPublicIPRead(d, meta) 102 } 103 104 func resourceCLCPublicIPRead(d *schema.ResourceData, meta interface{}) error { 105 client := meta.(*clc.Client) 106 pip := d.Id() 107 s := d.Get("server_id").(string) 108 resp, err := client.Server.GetPublicIP(s, pip) 109 if err != nil { 110 log.Printf("[INFO] Failed finding public ip: %v. Marking destroyed", d.Id()) 111 d.SetId("") 112 return nil 113 } 114 115 d.Set("internal_ip_address", resp.InternalIP) 116 d.Set("ports", resp.Ports) 117 d.Set("source_restrictions", resp.SourceRestrictions) 118 return nil 119 } 120 121 func resourceCLCPublicIPUpdate(d *schema.ResourceData, meta interface{}) error { 122 client := meta.(*clc.Client) 123 ip := d.Id() 124 sid := d.Get("server_id").(string) 125 if d.HasChange("ports") || d.HasChange("source_restrictions") { 126 ports, sources := parseIPSpec(d) 127 req := server.PublicIP{ 128 Ports: *ports, 129 SourceRestrictions: *sources, 130 } 131 resp, err := client.Server.UpdatePublicIP(sid, ip, req) 132 if err != nil { 133 return fmt.Errorf("Failed updating public ip: %v", err) 134 } 135 err = waitStatus(client, resp.ID) 136 if err != nil { 137 return err 138 } 139 log.Printf("[INFO] Successfully updated %v with %v", ip, req) 140 } 141 return nil 142 } 143 144 func resourceCLCPublicIPDelete(d *schema.ResourceData, meta interface{}) error { 145 client := meta.(*clc.Client) 146 s := d.Get("server_id").(string) 147 ip := d.Id() 148 log.Printf("[INFO] Deleting public ip %v", ip) 149 resp, err := client.Server.DeletePublicIP(s, ip) 150 if err != nil { 151 return fmt.Errorf("Failed deleting public ip: %v", err) 152 } 153 err = waitStatus(client, resp.ID) 154 if err != nil { 155 return err 156 } 157 log.Printf("[INFO] Public IP sucessfully deleted: %v", ip) 158 return nil 159 } 160 161 func parseIPSpec(d *schema.ResourceData) (*[]server.Port, *[]server.SourceRestriction) { 162 var ports []server.Port 163 var sources []server.SourceRestriction 164 if v := d.Get("ports"); v != nil { 165 for _, v := range v.([]interface{}) { 166 m := v.(map[string]interface{}) 167 p := server.Port{} 168 port, err := strconv.Atoi(m["port"].(string)) 169 if err != nil { 170 log.Printf("[WARN] Failed parsing port '%v'. skipping", m["port"]) 171 continue 172 } 173 p.Protocol = m["protocol"].(string) 174 p.Port = port 175 through := -1 176 if to := m["port_to"]; to != nil { 177 through, _ = strconv.Atoi(to.(string)) 178 log.Printf("[DEBUG] port range: %v-%v", port, through) 179 p.PortTo = through 180 } 181 ports = append(ports, p) 182 } 183 } 184 if v := d.Get("source_restrictions"); v != nil { 185 for _, v := range v.([]interface{}) { 186 m := v.(map[string]interface{}) 187 r := server.SourceRestriction{} 188 r.CIDR = m["cidr"].(string) 189 sources = append(sources, r) 190 } 191 } 192 return &ports, &sources 193 }