2016-03-23 20:53:50 +01:00
|
|
|
package fastly
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
|
|
gofastly "github.com/sethvargo/go-fastly"
|
|
|
|
)
|
|
|
|
|
|
|
|
var fastlyNoServiceFoundErr = errors.New("No matching Fastly Service found")
|
|
|
|
|
|
|
|
func resourceServiceV1() *schema.Resource {
|
|
|
|
return &schema.Resource{
|
|
|
|
Create: resourceServiceV1Create,
|
|
|
|
Read: resourceServiceV1Read,
|
|
|
|
Update: resourceServiceV1Update,
|
|
|
|
Delete: resourceServiceV1Delete,
|
|
|
|
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"name": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
Description: "Unique name for this Service",
|
|
|
|
},
|
|
|
|
|
|
|
|
// Active Version represents the currently activated version in Fastly. In
|
|
|
|
// Terraform, we abstract this number away from the users and manage
|
|
|
|
// creating and activating. It's used internally, but also exported for
|
|
|
|
// users to see.
|
|
|
|
"active_version": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"domain": &schema.Schema{
|
|
|
|
Type: schema.TypeSet,
|
|
|
|
Required: true,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"name": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
Description: "The domain that this Service will respond to",
|
|
|
|
},
|
|
|
|
|
|
|
|
"comment": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
"default_ttl": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
Default: 3600,
|
|
|
|
Description: "The default Time-to-live (TTL) for the version",
|
|
|
|
},
|
|
|
|
|
|
|
|
"default_host": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Description: "The default hostname for the version",
|
|
|
|
},
|
|
|
|
|
|
|
|
"backend": &schema.Schema{
|
|
|
|
Type: schema.TypeSet,
|
|
|
|
Required: true,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
// required fields
|
|
|
|
"name": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
Description: "A name for this Backend",
|
|
|
|
},
|
|
|
|
"address": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
Description: "An IPv4, hostname, or IPv6 address for the Backend",
|
|
|
|
},
|
|
|
|
// Optional fields, defaults where they exist
|
|
|
|
"auto_loadbalance": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: true,
|
|
|
|
Description: "Should this Backend be load balanced",
|
|
|
|
},
|
|
|
|
"between_bytes_timeout": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
Default: 10000,
|
|
|
|
Description: "How long to wait between bytes in milliseconds",
|
|
|
|
},
|
|
|
|
"connect_timeout": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
Default: 1000,
|
|
|
|
Description: "How long to wait for a timeout in milliseconds",
|
|
|
|
},
|
|
|
|
"error_threshold": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
Default: 0,
|
|
|
|
Description: "Number of errors to allow before the Backend is marked as down",
|
|
|
|
},
|
|
|
|
"first_byte_timeout": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
Default: 15000,
|
|
|
|
Description: "How long to wait for the first bytes in milliseconds",
|
|
|
|
},
|
|
|
|
"max_conn": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
Default: 200,
|
|
|
|
Description: "Maximum number of connections for this Backend",
|
|
|
|
},
|
|
|
|
"port": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
Default: 80,
|
|
|
|
Description: "The port number Backend responds on. Default 80",
|
|
|
|
},
|
|
|
|
"ssl_check_cert": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: true,
|
|
|
|
Description: "Be strict on checking SSL certs",
|
|
|
|
},
|
|
|
|
// UseSSL is something we want to support in the future, but
|
|
|
|
// requires SSL setup we don't yet have
|
|
|
|
// TODO: Provide all SSL fields from https://docs.fastly.com/api/config#backend
|
|
|
|
// "use_ssl": &schema.Schema{
|
|
|
|
// Type: schema.TypeBool,
|
|
|
|
// Optional: true,
|
|
|
|
// Default: false,
|
|
|
|
// Description: "Whether or not to use SSL to reach the Backend",
|
|
|
|
// },
|
|
|
|
"weight": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
Default: 100,
|
|
|
|
Description: "How long to wait for the first bytes in milliseconds",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
"force_destroy": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceServiceV1Create(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
conn := meta.(*FastlyClient).conn
|
|
|
|
service, err := conn.CreateService(&gofastly.CreateServiceInput{
|
|
|
|
Name: d.Get("name").(string),
|
|
|
|
Comment: "Managed by Terraform",
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
d.SetId(service.ID)
|
|
|
|
return resourceServiceV1Update(d, meta)
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceServiceV1Update(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
conn := meta.(*FastlyClient).conn
|
|
|
|
|
|
|
|
// Update Name. No new verions is required for this
|
|
|
|
if d.HasChange("name") {
|
|
|
|
_, err := conn.UpdateService(&gofastly.UpdateServiceInput{
|
|
|
|
ID: d.Id(),
|
|
|
|
Name: d.Get("name").(string),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Once activated, Versions are locked and become immutable. This is true for
|
|
|
|
// versions that are no longer active. For Domains, Backends, DefaultHost and
|
|
|
|
// DefaultTTL, a new Version must be created first, and updates posted to that
|
|
|
|
// Version. Loop these attributes and determine if we need to create a new version first
|
|
|
|
var needsChange bool
|
|
|
|
for _, v := range []string{"domain", "backend", "default_host", "default_ttl"} {
|
|
|
|
if d.HasChange(v) {
|
|
|
|
needsChange = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if needsChange {
|
|
|
|
latestVersion := d.Get("active_version").(string)
|
|
|
|
if latestVersion == "" {
|
|
|
|
// If the service was just created, there is an empty Version 1 available
|
|
|
|
// that is unlocked and can be updated
|
|
|
|
latestVersion = "1"
|
|
|
|
} else {
|
|
|
|
// Clone the latest version, giving us an unlocked version we can modify
|
|
|
|
log.Printf("[DEBUG] Creating clone of version (%s) for updates", latestVersion)
|
|
|
|
newVersion, err := conn.CloneVersion(&gofastly.CloneVersionInput{
|
|
|
|
Service: d.Id(),
|
|
|
|
Version: latestVersion,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// The new version number is named "Number", but it's actually a string
|
|
|
|
latestVersion = newVersion.Number
|
|
|
|
|
|
|
|
// New versions are not immediately found in the API, or are not
|
|
|
|
// immediately mutable, so we need to sleep a few and let Fastly ready
|
|
|
|
// itself. Typically, 7 seconds is enough
|
2016-03-24 16:27:20 +01:00
|
|
|
log.Printf("[DEBUG] Sleeping 7 seconds to allow Fastly Version to be available")
|
2016-03-23 20:53:50 +01:00
|
|
|
time.Sleep(7 * time.Second)
|
|
|
|
}
|
|
|
|
|
|
|
|
// update general settings
|
|
|
|
if d.HasChange("default_host") || d.HasChange("default_ttl") {
|
|
|
|
opts := gofastly.UpdateSettingsInput{
|
|
|
|
Service: d.Id(),
|
|
|
|
Version: latestVersion,
|
|
|
|
// default_ttl has the same default value of 3600 that is provided by
|
|
|
|
// the Fastly API, so it's safe to include here
|
|
|
|
DefaultTTL: uint(d.Get("default_ttl").(int)),
|
|
|
|
}
|
|
|
|
|
|
|
|
if attr, ok := d.GetOk("default_host"); ok {
|
|
|
|
opts.DefaultHost = attr.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[DEBUG] Update Settings opts: %#v", opts)
|
|
|
|
_, err := conn.UpdateSettings(&opts)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find differences in domains
|
|
|
|
if d.HasChange("domain") {
|
|
|
|
// Note: we don't utilize the PUT endpoint to update a Domain, we simply
|
|
|
|
// destroy it and create a new one. This is how Terraform works with nested
|
|
|
|
// sub resources, we only get the full diff not a partial set item diff.
|
|
|
|
// Because this is done on a new version of the configuration, this is
|
|
|
|
// considered safe
|
|
|
|
od, nd := d.GetChange("domain")
|
|
|
|
if od == nil {
|
|
|
|
od = new(schema.Set)
|
|
|
|
}
|
|
|
|
if nd == nil {
|
|
|
|
nd = new(schema.Set)
|
|
|
|
}
|
|
|
|
|
|
|
|
ods := od.(*schema.Set)
|
|
|
|
nds := nd.(*schema.Set)
|
|
|
|
|
|
|
|
remove := ods.Difference(nds).List()
|
|
|
|
add := nds.Difference(ods).List()
|
|
|
|
|
|
|
|
// Delete removed domains
|
|
|
|
for _, dRaw := range remove {
|
|
|
|
df := dRaw.(map[string]interface{})
|
|
|
|
opts := gofastly.DeleteDomainInput{
|
|
|
|
Service: d.Id(),
|
|
|
|
Version: latestVersion,
|
|
|
|
Name: df["name"].(string),
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[DEBUG] Fastly Domain Removal opts: %#v", opts)
|
|
|
|
err := conn.DeleteDomain(&opts)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// POST new Domains
|
|
|
|
for _, dRaw := range add {
|
|
|
|
df := dRaw.(map[string]interface{})
|
|
|
|
opts := gofastly.CreateDomainInput{
|
|
|
|
Service: d.Id(),
|
|
|
|
Version: latestVersion,
|
|
|
|
Name: df["name"].(string),
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := df["comment"]; ok {
|
|
|
|
opts.Comment = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[DEBUG] Fastly Domain Addition opts: %#v", opts)
|
|
|
|
_, err := conn.CreateDomain(&opts)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// find difference in backends
|
|
|
|
if d.HasChange("backend") {
|
|
|
|
// POST new Backends
|
|
|
|
// Note: we don't utilize the PUT endpoint to update a Backend, we simply
|
|
|
|
// destroy it and create a new one. This is how Terraform works with nested
|
|
|
|
// sub resources, we only get the full diff not a partial set item diff.
|
|
|
|
// Because this is done on a new version of the configuration, this is
|
|
|
|
// considered safe
|
|
|
|
ob, nb := d.GetChange("backend")
|
|
|
|
if ob == nil {
|
|
|
|
ob = new(schema.Set)
|
|
|
|
}
|
|
|
|
if nb == nil {
|
|
|
|
nb = new(schema.Set)
|
|
|
|
}
|
|
|
|
|
|
|
|
obs := ob.(*schema.Set)
|
|
|
|
nbs := nb.(*schema.Set)
|
|
|
|
removeBackends := obs.Difference(nbs).List()
|
|
|
|
addBackends := nbs.Difference(obs).List()
|
|
|
|
|
|
|
|
// DELETE old Backends
|
|
|
|
for _, bRaw := range removeBackends {
|
|
|
|
bf := bRaw.(map[string]interface{})
|
|
|
|
opts := gofastly.DeleteBackendInput{
|
|
|
|
Service: d.Id(),
|
|
|
|
Version: latestVersion,
|
|
|
|
Name: bf["name"].(string),
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[DEBUG] Fastly Backend Removal opts: %#v", opts)
|
|
|
|
err := conn.DeleteBackend(&opts)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, dRaw := range addBackends {
|
|
|
|
df := dRaw.(map[string]interface{})
|
|
|
|
opts := gofastly.CreateBackendInput{
|
|
|
|
Service: d.Id(),
|
|
|
|
Version: latestVersion,
|
|
|
|
Name: df["name"].(string),
|
|
|
|
Address: df["address"].(string),
|
|
|
|
AutoLoadbalance: df["auto_loadbalance"].(bool),
|
|
|
|
SSLCheckCert: df["ssl_check_cert"].(bool),
|
|
|
|
Port: uint(df["port"].(int)),
|
|
|
|
BetweenBytesTimeout: uint(df["between_bytes_timeout"].(int)),
|
|
|
|
ConnectTimeout: uint(df["connect_timeout"].(int)),
|
|
|
|
ErrorThreshold: uint(df["error_threshold"].(int)),
|
|
|
|
FirstByteTimeout: uint(df["first_byte_timeout"].(int)),
|
|
|
|
MaxConn: uint(df["max_conn"].(int)),
|
|
|
|
Weight: uint(df["weight"].(int)),
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[DEBUG] Create Backend Opts: %#v", opts)
|
|
|
|
_, err := conn.CreateBackend(&opts)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// validate version
|
|
|
|
log.Printf("[DEBUG] Validating Fastly Service (%s), Version (%s)", d.Id(), latestVersion)
|
|
|
|
valid, msg, err := conn.ValidateVersion(&gofastly.ValidateVersionInput{
|
|
|
|
Service: d.Id(),
|
|
|
|
Version: latestVersion,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("[ERR] Error checking validation: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !valid {
|
2016-03-24 16:27:20 +01:00
|
|
|
return fmt.Errorf("[ERR] Invalid configuration for Fastly Service (%s): %s", d.Id(), msg)
|
2016-03-23 20:53:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[DEBUG] Activating Fastly Service (%s), Version (%s)", d.Id(), latestVersion)
|
|
|
|
_, err = conn.ActivateVersion(&gofastly.ActivateVersionInput{
|
|
|
|
Service: d.Id(),
|
|
|
|
Version: latestVersion,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("[ERR] Error activating version (%s): %s", latestVersion, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only if the version is valid and activated do we set the active_version.
|
|
|
|
// This prevents us from getting stuck in cloning an invalid version
|
|
|
|
d.Set("active_version", latestVersion)
|
|
|
|
}
|
|
|
|
|
|
|
|
return resourceServiceV1Read(d, meta)
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceServiceV1Read(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
conn := meta.(*FastlyClient).conn
|
|
|
|
|
|
|
|
// Find the Service. Discard the service because we need the ServiceDetails,
|
|
|
|
// not just a Service record
|
|
|
|
_, err := findService(d.Id(), meta)
|
|
|
|
if err != nil {
|
|
|
|
switch err {
|
|
|
|
case fastlyNoServiceFoundErr:
|
|
|
|
log.Printf("[WARN] %s for ID (%s)", err, d.Id())
|
|
|
|
d.SetId("")
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s, err := conn.GetServiceDetails(&gofastly.GetServiceInput{
|
|
|
|
ID: d.Id(),
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
d.Set("name", s.Name)
|
|
|
|
d.Set("active_version", s.ActiveVersion.Number)
|
|
|
|
|
|
|
|
// If CreateService succeeds, but initial updates to the Service fail, we'll
|
|
|
|
// have an empty ActiveService version (no version is active, so we can't
|
|
|
|
// query for information on it)
|
|
|
|
if s.ActiveVersion.Number != "" {
|
|
|
|
settingsOpts := gofastly.GetSettingsInput{
|
|
|
|
Service: d.Id(),
|
|
|
|
Version: s.ActiveVersion.Number,
|
|
|
|
}
|
|
|
|
if settings, err := conn.GetSettings(&settingsOpts); err == nil {
|
|
|
|
d.Set("default_host", settings.DefaultHost)
|
|
|
|
d.Set("default_ttl", settings.DefaultTTL)
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("[ERR] Error looking up Version settings for (%s), version (%s): %s", d.Id(), s.ActiveVersion.Number, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: update go-fastly to support an ActiveVersion struct, which contains
|
|
|
|
// domain and backend info in the response. Here we do 2 additional queries
|
|
|
|
// to find out that info
|
|
|
|
domainList, err := conn.ListDomains(&gofastly.ListDomainsInput{
|
|
|
|
Service: d.Id(),
|
|
|
|
Version: s.ActiveVersion.Number,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("[ERR] Error looking up Domains for (%s), version (%s): %s", d.Id(), s.ActiveVersion.Number, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Refresh Domains
|
|
|
|
dl := flattenDomains(domainList)
|
|
|
|
|
|
|
|
if err := d.Set("domain", dl); err != nil {
|
|
|
|
log.Printf("[WARN] Error setting Domains for (%s): %s", d.Id(), err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Refresh Backends
|
|
|
|
backendList, err := conn.ListBackends(&gofastly.ListBackendsInput{
|
|
|
|
Service: d.Id(),
|
|
|
|
Version: s.ActiveVersion.Number,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("[ERR] Error looking up Backends for (%s), version (%s): %s", d.Id(), s.ActiveVersion.Number, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
bl := flattenBackends(backendList)
|
|
|
|
|
|
|
|
if err := d.Set("backend", bl); err != nil {
|
|
|
|
log.Printf("[WARN] Error setting Backends for (%s): %s", d.Id(), err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log.Printf("[DEBUG] Active Version for Service (%s) is empty, no state to refresh", d.Id())
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceServiceV1Delete(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
conn := meta.(*FastlyClient).conn
|
|
|
|
|
|
|
|
// Fastly will fail to delete any service with an Active Version.
|
|
|
|
// If `force_destroy` is given, we deactivate the active version and then send
|
|
|
|
// the DELETE call
|
|
|
|
if d.Get("force_destroy").(bool) {
|
|
|
|
s, err := conn.GetServiceDetails(&gofastly.GetServiceInput{
|
|
|
|
ID: d.Id(),
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if s.ActiveVersion.Number != "" {
|
|
|
|
_, err := conn.DeactivateVersion(&gofastly.DeactivateVersionInput{
|
|
|
|
Service: d.Id(),
|
|
|
|
Version: s.ActiveVersion.Number,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err := conn.DeleteService(&gofastly.DeleteServiceInput{
|
|
|
|
ID: d.Id(),
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = findService(d.Id(), meta)
|
|
|
|
if err != nil {
|
|
|
|
switch err {
|
|
|
|
// we expect no records to be found here
|
|
|
|
case fastlyNoServiceFoundErr:
|
|
|
|
d.SetId("")
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// findService above returned something and nil error, but shouldn't have
|
|
|
|
return fmt.Errorf("[WARN] Tried deleting Service (%s), but was still found", d.Id())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func flattenDomains(list []*gofastly.Domain) []map[string]interface{} {
|
|
|
|
dl := make([]map[string]interface{}, 0, len(list))
|
|
|
|
|
|
|
|
for _, d := range list {
|
|
|
|
dl = append(dl, map[string]interface{}{
|
|
|
|
"name": d.Name,
|
|
|
|
"comment": d.Comment,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return dl
|
|
|
|
}
|
|
|
|
|
|
|
|
func flattenBackends(backendList []*gofastly.Backend) []map[string]interface{} {
|
|
|
|
var bl []map[string]interface{}
|
|
|
|
for _, b := range backendList {
|
|
|
|
// Convert Backend to a map for saving to state.
|
|
|
|
nb := map[string]interface{}{
|
|
|
|
"name": b.Name,
|
|
|
|
"address": b.Address,
|
|
|
|
"auto_loadbalance": b.AutoLoadbalance,
|
|
|
|
"between_bytes_timeout": int(b.BetweenBytesTimeout),
|
|
|
|
"connect_timeout": int(b.ConnectTimeout),
|
|
|
|
"error_threshold": int(b.ErrorThreshold),
|
|
|
|
"first_byte_timeout": int(b.FirstByteTimeout),
|
|
|
|
"max_conn": int(b.MaxConn),
|
|
|
|
"port": int(b.Port),
|
|
|
|
"ssl_check_cert": b.SSLCheckCert,
|
|
|
|
"weight": int(b.Weight),
|
|
|
|
}
|
|
|
|
|
|
|
|
bl = append(bl, nb)
|
|
|
|
}
|
|
|
|
return bl
|
|
|
|
}
|
|
|
|
|
|
|
|
// findService finds a Fastly Service via the ListServices endpoint, returning
|
|
|
|
// the Service if found.
|
|
|
|
//
|
|
|
|
// Fastly API does not include any "deleted_at" type parameter to indicate
|
|
|
|
// that a Service has been deleted. GET requests to a deleted Service will
|
|
|
|
// return 200 OK and have the full output of the Service for an unknown time
|
|
|
|
// (days, in my testing). In order to determine if a Service is deleted, we
|
|
|
|
// need to hit /service and loop the returned Services, searching for the one
|
|
|
|
// in question. This endpoint only returns active or "alive" services. If the
|
|
|
|
// Service is not included, then it's "gone"
|
|
|
|
//
|
|
|
|
// Returns a fastlyNoServiceFoundErr error if the Service is not found in the
|
|
|
|
// ListServices response.
|
|
|
|
func findService(id string, meta interface{}) (*gofastly.Service, error) {
|
|
|
|
conn := meta.(*FastlyClient).conn
|
|
|
|
|
|
|
|
l, err := conn.ListServices(&gofastly.ListServicesInput{})
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("[WARN] Error listing servcies when deleting Fastly Service (%s): %s", id, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, s := range l {
|
|
|
|
if s.ID == id {
|
|
|
|
log.Printf("[DEBUG] Found Service (%s)", id)
|
|
|
|
return s, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fastlyNoServiceFoundErr
|
|
|
|
}
|