Skip to content

Commit

Permalink
Merge branch 'ewilde-spectrum' of github.com:ewilde/terraform-provide…
Browse files Browse the repository at this point in the history
…r-cloudflare into ewilde-spectrum
  • Loading branch information
ewilde committed Nov 18, 2018
2 parents bfa4322 + b31d1fe commit e8a9b45
Showing 1 changed file with 352 additions and 0 deletions.
352 changes: 352 additions & 0 deletions cloudflare/resource_cloudflare_spectrum_app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
package cloudflare

import (
"fmt"
"log"
"strings"

"time"

"github.com/cloudflare/cloudflare-go"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/pkg/errors"
)

func resourceCloudflareSpectrumApp() *schema.Resource {
return &schema.Resource{
Create: resourceCloudflareSpectrumAppCreate,
Read: resourceCloudflareSpectrumAppRead,
Update: resourceCloudflareSpectrumAppUpdate,
Delete: resourceCloudflareSpectrumAppDelete,
Importer: &schema.ResourceImporter{
State: resourceCloudflareSpectrumAppImport,
},

SchemaVersion: 0,
Schema: map[string]*schema.Schema{
"protocol": {
Type: schema.TypeString,
Required: true,
},

"dns": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"type": {
Type: schema.TypeString,
Required: true,
},
"name": {
Type: schema.TypeString,
Required: true,
},
},
},
},

"origin_direct": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},



"origin_dns": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
},
},
},

"tls": {
Type: schema.TypeString,
Computed: true,
ValidateFunc: validation.StringInSlice([]string{
cloudflare.,
ecs.ScopeTask,
}, false),
},

"created_on": {
Type: schema.TypeString,
Computed: true,
},

"modified_on": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

var popPoolElem = &schema.Resource{
Schema: map[string]*schema.Schema{
"pop": {
Type: schema.TypeString,
Required: true,
// let the api handle validating pops
},

"pool_ids": {
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringLenBetween(1, 32),
},
},
},
}

var regionPoolElem = &schema.Resource{
Schema: map[string]*schema.Schema{
"region": {
Type: schema.TypeString,
Required: true,
// let the api handle validating regions
},

"pool_ids": {
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringLenBetween(1, 32),
},
},
},
}

var localPoolElems = map[string]*schema.Resource{
"pop": popPoolElem,
"region": regionPoolElem,
}

func resourceCloudflareSpectrumAppCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*cloudflare.API)

newSpectrumApp := cloudflare.SpectrumApp{
Name: d.Get("name").(string),
FallbackPool: d.Get("fallback_pool_id").(string),
DefaultPools: expandInterfaceToStringList(d.Get("default_pool_ids")),
Proxied: d.Get("proxied").(bool),
TTL: d.Get("ttl").(int),
SteeringPolicy: d.Get("steering_policy").(string),
Persistence: d.Get("session_affinity").(string),
}

if description, ok := d.GetOk("description"); ok {
newSpectrumApp.Description = description.(string)
}

if ttl, ok := d.GetOk("ttl"); ok {
newSpectrumApp.TTL = ttl.(int)
}

if regionPools, ok := d.GetOk("region_pools"); ok {
expandedRegionPools, err := expandGeoPools(regionPools, "region")
if err != nil {
return err
}
newSpectrumApp.RegionPools = expandedRegionPools
}

if popPools, ok := d.GetOk("pop_pools"); ok {
expandedPopPools, err := expandGeoPools(popPools, "pop")
if err != nil {
return err
}
newSpectrumApp.PopPools = expandedPopPools
}

zoneName := d.Get("zone").(string)
zoneID, err := client.ZoneIDByName(zoneName)
if err != nil {
return fmt.Errorf("error finding zone %q: %s", zoneName, err)
}
d.Set("zone_id", zoneID)

log.Printf("[INFO] Creating Cloudflare Load Balancer from struct: %+v", newSpectrumApp)

r, err := client.CreateSpectrumApp(zoneID, newSpectrumApp)
if err != nil {
return errors.Wrap(err, "error creating load balancer for zone")
}

if r.ID == "" {
return fmt.Errorf("failed to find id in Create response; resource was empty")
}

d.SetId(r.ID)

log.Printf("[INFO] Cloudflare Load Balancer ID: %s", d.Id())

return resourceCloudflareSpectrumAppRead(d, meta)
}

func resourceCloudflareSpectrumAppUpdate(d *schema.ResourceData, meta interface{}) error {
// since api only supports replace, update looks a lot like create...
client := meta.(*cloudflare.API)
zoneID := d.Get("zone_id").(string)

loadBalancer := cloudflare.SpectrumApp{
ID: d.Id(),
Name: d.Get("name").(string),
FallbackPool: d.Get("fallback_pool_id").(string),
DefaultPools: expandInterfaceToStringList(d.Get("default_pool_ids")),
Proxied: d.Get("proxied").(bool),
TTL: d.Get("ttl").(int),
SteeringPolicy: d.Get("steering_policy").(string),
Persistence: d.Get("session_affinity").(string),
}

if description, ok := d.GetOk("description"); ok {
loadBalancer.Description = description.(string)
}

if regionPools, ok := d.GetOk("region_pools"); ok {
expandedRegionPools, err := expandGeoPools(regionPools, "region")
if err != nil {
return err
}
loadBalancer.RegionPools = expandedRegionPools
}

if popPools, ok := d.GetOk("pop_pools"); ok {
expandedPopPools, err := expandGeoPools(popPools, "pop")
if err != nil {
return err
}
loadBalancer.PopPools = expandedPopPools
}

log.Printf("[INFO] Updating Cloudflare Load Balancer from struct: %+v", loadBalancer)

_, err := client.ModifySpectrumApp(zoneID, loadBalancer)
if err != nil {
return errors.Wrap(err, "error creating load balancer for zone")
}

return resourceCloudflareSpectrumAppRead(d, meta)
}

func expandGeoPools(pool interface{}, geoType string) (map[string][]string, error) {
cfg := pool.(*schema.Set).List()
expanded := make(map[string][]string)
for _, v := range cfg {
locationConfig := v.(map[string]interface{})
// lists are of type interface{} by default
location := locationConfig[geoType].(string)
if _, present := expanded[location]; !present {
expanded[location] = expandInterfaceToStringList(locationConfig["pool_ids"])
} else {
return nil, fmt.Errorf("duplicate entry specified for %s pool in location %q. each location must only be specified once", geoType, location)
}
}
return expanded, nil
}

func resourceCloudflareSpectrumAppRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*cloudflare.API)
zoneID := d.Get("zone_id").(string)
loadBalancerID := d.Id()

loadBalancer, err := client.SpectrumAppDetails(zoneID, loadBalancerID)
if err != nil {
if strings.Contains(err.Error(), "HTTP status 404") {
log.Printf("[INFO] Load balancer %s in zone %s not found", loadBalancerID, zoneID)
d.SetId("")
return nil
}
return errors.Wrap(err,
fmt.Sprintf("Error reading load balancer resource from API for resource %s in zone %s", zoneID, loadBalancerID))
}

d.Set("name", loadBalancer.Name)
d.Set("fallback_pool_id", loadBalancer.FallbackPool)
d.Set("proxied", loadBalancer.Proxied)
d.Set("description", loadBalancer.Description)
d.Set("ttl", loadBalancer.TTL)
d.Set("steering_policy", loadBalancer.SteeringPolicy)
d.Set("created_on", loadBalancer.CreatedOn.Format(time.RFC3339Nano))
d.Set("modified_on", loadBalancer.ModifiedOn.Format(time.RFC3339Nano))

if err := d.Set("default_pool_ids", loadBalancer.DefaultPools); err != nil {
log.Printf("[WARN] Error setting default_pool_ids on load balancer %q: %s", d.Id(), err)
}

if err := d.Set("pop_pools", flattenGeoPools(loadBalancer.PopPools, "pop")); err != nil {
log.Printf("[WARN] Error setting pop_pools on load balancer %q: %s", d.Id(), err)
}

if err := d.Set("region_pools", flattenGeoPools(loadBalancer.RegionPools, "region")); err != nil {
log.Printf("[WARN] Error setting region_pools on load balancer %q: %s", d.Id(), err)
}

return nil
}

func flattenGeoPools(pools map[string][]string, geoType string) *schema.Set {
flattened := make([]interface{}, 0)
for k, v := range pools {
geoConf := map[string]interface{}{
geoType: k,
"pool_ids": flattenStringList(v),
}
flattened = append(flattened, geoConf)
}
return schema.NewSet(schema.HashResource(localPoolElems[geoType]), flattened)
}

func resourceCloudflareSpectrumAppDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*cloudflare.API)
zoneID := d.Get("zone_id").(string)
loadBalancerID := d.Id()

log.Printf("[INFO] Deleting Cloudflare Load Balancer: %s in zone: %s", loadBalancerID, zoneID)

err := client.DeleteSpectrumApp(zoneID, loadBalancerID)
if err != nil {
return fmt.Errorf("error deleting Cloudflare Load Balancer: %s", err)
}

return nil
}

func resourceCloudflareSpectrumAppImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
client := meta.(*cloudflare.API)

// split the id so we can lookup
idAttr := strings.SplitN(d.Id(), "/", 2)
var zoneName string
var loadBalancerID string
if len(idAttr) == 2 {
zoneName = idAttr[0]
loadBalancerID = idAttr[1]
} else {
return nil, fmt.Errorf("invalid id (\"%s\") specified, should be in format \"zoneName/loadBalancerID\"", d.Id())
}
zoneID, err := client.ZoneIDByName(zoneName)

if err != nil {
return nil, fmt.Errorf("error finding zoneName %q: %s", zoneName, err)
}

d.Set("zone", zoneName)
d.Set("zone_id", zoneID)
d.SetId(loadBalancerID)
return []*schema.ResourceData{d}, nil
}

0 comments on commit e8a9b45

Please sign in to comment.