194 lines
5.1 KiB
Go
194 lines
5.1 KiB
Go
package clc
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strconv"
|
|
|
|
clc "github.com/CenturyLinkCloud/clc-sdk"
|
|
"github.com/CenturyLinkCloud/clc-sdk/server"
|
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
)
|
|
|
|
func resourceCLCPublicIP() *schema.Resource {
|
|
return &schema.Resource{
|
|
Create: resourceCLCPublicIPCreate,
|
|
Read: resourceCLCPublicIPRead,
|
|
Update: resourceCLCPublicIPUpdate,
|
|
Delete: resourceCLCPublicIPDelete,
|
|
Schema: map[string]*schema.Schema{
|
|
"server_id": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
},
|
|
"internal_ip_address": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
Default: nil,
|
|
},
|
|
"ports": &schema.Schema{
|
|
Type: schema.TypeList,
|
|
Required: true,
|
|
Elem: &schema.Schema{Type: schema.TypeMap},
|
|
},
|
|
"source_restrictions": &schema.Schema{
|
|
Type: schema.TypeList,
|
|
Optional: true,
|
|
Elem: &schema.Schema{Type: schema.TypeMap},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func resourceCLCPublicIPCreate(d *schema.ResourceData, meta interface{}) error {
|
|
client := meta.(*clc.Client)
|
|
sid := d.Get("server_id").(string)
|
|
priv := d.Get("internal_ip_address").(string)
|
|
ports, sources := parseIPSpec(d)
|
|
req := server.PublicIP{
|
|
Ports: *ports,
|
|
SourceRestrictions: *sources,
|
|
}
|
|
|
|
// since the API doesn't tell us the public IP it allocated,
|
|
// track what was added after the call.
|
|
ips := make(map[string]string)
|
|
prev, err := client.Server.Get(sid)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed finding server %v: %v", sid, err)
|
|
}
|
|
for _, i := range prev.Details.IPaddresses {
|
|
ips[i.Internal] = i.Public
|
|
}
|
|
|
|
if priv != "" {
|
|
// use existing private ip
|
|
if _, present := ips[priv]; !present {
|
|
return fmt.Errorf("Failed finding internal ip to use %v", priv)
|
|
}
|
|
req.InternalIP = priv
|
|
}
|
|
// execute the request
|
|
resp, err := client.Server.AddPublicIP(sid, req)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed reserving public ip: %v", err)
|
|
}
|
|
err = waitStatus(client, resp.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
server, err := client.Server.Get(sid)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed refreshing server for public ip: %v", err)
|
|
}
|
|
for _, i := range server.Details.IPaddresses {
|
|
if priv != "" && i.Internal == priv {
|
|
// bind
|
|
log.Printf("[DEBUG] Public IP bound on existing internal:%v - %v", i.Internal, i.Public)
|
|
d.SetId(i.Public)
|
|
break
|
|
} else if ips[i.Internal] == "" && i.Public != "" {
|
|
// allocate
|
|
log.Printf("[DEBUG] Public IP allocated on new internal:%v - %v", i.Internal, i.Public)
|
|
d.SetId(i.Public)
|
|
break
|
|
}
|
|
}
|
|
return resourceCLCPublicIPRead(d, meta)
|
|
}
|
|
|
|
func resourceCLCPublicIPRead(d *schema.ResourceData, meta interface{}) error {
|
|
client := meta.(*clc.Client)
|
|
pip := d.Id()
|
|
s := d.Get("server_id").(string)
|
|
resp, err := client.Server.GetPublicIP(s, pip)
|
|
if err != nil {
|
|
log.Printf("[INFO] Failed finding public ip: %v. Marking destroyed", d.Id())
|
|
d.SetId("")
|
|
return nil
|
|
}
|
|
|
|
d.Set("internal_ip_address", resp.InternalIP)
|
|
d.Set("ports", resp.Ports)
|
|
d.Set("source_restrictions", resp.SourceRestrictions)
|
|
return nil
|
|
}
|
|
|
|
func resourceCLCPublicIPUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
client := meta.(*clc.Client)
|
|
ip := d.Id()
|
|
sid := d.Get("server_id").(string)
|
|
if d.HasChange("ports") || d.HasChange("source_restrictions") {
|
|
ports, sources := parseIPSpec(d)
|
|
req := server.PublicIP{
|
|
Ports: *ports,
|
|
SourceRestrictions: *sources,
|
|
}
|
|
resp, err := client.Server.UpdatePublicIP(sid, ip, req)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed updating public ip: %v", err)
|
|
}
|
|
err = waitStatus(client, resp.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Printf("[INFO] Successfully updated %v with %v", ip, req)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func resourceCLCPublicIPDelete(d *schema.ResourceData, meta interface{}) error {
|
|
client := meta.(*clc.Client)
|
|
s := d.Get("server_id").(string)
|
|
ip := d.Id()
|
|
log.Printf("[INFO] Deleting public ip %v", ip)
|
|
resp, err := client.Server.DeletePublicIP(s, ip)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed deleting public ip: %v", err)
|
|
}
|
|
err = waitStatus(client, resp.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Printf("[INFO] Public IP sucessfully deleted: %v", ip)
|
|
return nil
|
|
}
|
|
|
|
func parseIPSpec(d *schema.ResourceData) (*[]server.Port, *[]server.SourceRestriction) {
|
|
var ports []server.Port
|
|
var sources []server.SourceRestriction
|
|
if v := d.Get("ports"); v != nil {
|
|
for _, v := range v.([]interface{}) {
|
|
m := v.(map[string]interface{})
|
|
p := server.Port{}
|
|
port, err := strconv.Atoi(m["port"].(string))
|
|
if err != nil {
|
|
log.Printf("[WARN] Failed parsing port '%v'. skipping", m["port"])
|
|
continue
|
|
}
|
|
p.Protocol = m["protocol"].(string)
|
|
p.Port = port
|
|
through := -1
|
|
if to := m["port_to"]; to != nil {
|
|
through, _ = strconv.Atoi(to.(string))
|
|
log.Printf("[DEBUG] port range: %v-%v", port, through)
|
|
p.PortTo = through
|
|
}
|
|
ports = append(ports, p)
|
|
}
|
|
}
|
|
if v := d.Get("source_restrictions"); v != nil {
|
|
for _, v := range v.([]interface{}) {
|
|
m := v.(map[string]interface{})
|
|
r := server.SourceRestriction{}
|
|
r.CIDR = m["cidr"].(string)
|
|
sources = append(sources, r)
|
|
}
|
|
}
|
|
return &ports, &sources
|
|
}
|