github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/fastly/resource_fastly_service_v1.go (about)

     1  package fastly
     2  
     3  import (
     4  	"crypto/sha1"
     5  	"encoding/hex"
     6  	"errors"
     7  	"fmt"
     8  	"log"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/hashicorp/terraform/helper/schema"
    13  	gofastly "github.com/sethvargo/go-fastly"
    14  )
    15  
    16  var fastlyNoServiceFoundErr = errors.New("No matching Fastly Service found")
    17  
    18  func resourceServiceV1() *schema.Resource {
    19  	return &schema.Resource{
    20  		Create: resourceServiceV1Create,
    21  		Read:   resourceServiceV1Read,
    22  		Update: resourceServiceV1Update,
    23  		Delete: resourceServiceV1Delete,
    24  		Importer: &schema.ResourceImporter{
    25  			State: schema.ImportStatePassthrough,
    26  		},
    27  
    28  		Schema: map[string]*schema.Schema{
    29  			"name": {
    30  				Type:        schema.TypeString,
    31  				Required:    true,
    32  				Description: "Unique name for this Service",
    33  			},
    34  
    35  			// Active Version represents the currently activated version in Fastly. In
    36  			// Terraform, we abstract this number away from the users and manage
    37  			// creating and activating. It's used internally, but also exported for
    38  			// users to see.
    39  			"active_version": {
    40  				Type:     schema.TypeInt,
    41  				Computed: true,
    42  			},
    43  
    44  			"domain": {
    45  				Type:     schema.TypeSet,
    46  				Required: true,
    47  				Elem: &schema.Resource{
    48  					Schema: map[string]*schema.Schema{
    49  						"name": {
    50  							Type:        schema.TypeString,
    51  							Required:    true,
    52  							Description: "The domain that this Service will respond to",
    53  						},
    54  
    55  						"comment": {
    56  							Type:     schema.TypeString,
    57  							Optional: true,
    58  						},
    59  					},
    60  				},
    61  			},
    62  
    63  			"condition": {
    64  				Type:     schema.TypeSet,
    65  				Optional: true,
    66  				Elem: &schema.Resource{
    67  					Schema: map[string]*schema.Schema{
    68  						"name": {
    69  							Type:     schema.TypeString,
    70  							Required: true,
    71  						},
    72  						"statement": {
    73  							Type:        schema.TypeString,
    74  							Required:    true,
    75  							Description: "The statement used to determine if the condition is met",
    76  							StateFunc: func(v interface{}) string {
    77  								value := v.(string)
    78  								// Trim newlines and spaces, to match Fastly API
    79  								return strings.TrimSpace(value)
    80  							},
    81  						},
    82  						"priority": {
    83  							Type:        schema.TypeInt,
    84  							Required:    true,
    85  							Description: "A number used to determine the order in which multiple conditions execute. Lower numbers execute first",
    86  						},
    87  						"type": {
    88  							Type:        schema.TypeString,
    89  							Required:    true,
    90  							Description: "Type of the condition, either `REQUEST`, `RESPONSE`, or `CACHE`",
    91  						},
    92  					},
    93  				},
    94  			},
    95  
    96  			"default_ttl": {
    97  				Type:        schema.TypeInt,
    98  				Optional:    true,
    99  				Default:     3600,
   100  				Description: "The default Time-to-live (TTL) for the version",
   101  			},
   102  
   103  			"default_host": {
   104  				Type:        schema.TypeString,
   105  				Optional:    true,
   106  				Computed:    true,
   107  				Description: "The default hostname for the version",
   108  			},
   109  
   110  			"healthcheck": {
   111  				Type:     schema.TypeSet,
   112  				Optional: true,
   113  				Elem: &schema.Resource{
   114  					Schema: map[string]*schema.Schema{
   115  						// required fields
   116  						"name": {
   117  							Type:        schema.TypeString,
   118  							Required:    true,
   119  							Description: "A name to refer to this healthcheck",
   120  						},
   121  						"host": {
   122  							Type:        schema.TypeString,
   123  							Required:    true,
   124  							Description: "Which host to check",
   125  						},
   126  						"path": {
   127  							Type:        schema.TypeString,
   128  							Required:    true,
   129  							Description: "The path to check",
   130  						},
   131  						// optional fields
   132  						"check_interval": {
   133  							Type:        schema.TypeInt,
   134  							Optional:    true,
   135  							Default:     5000,
   136  							Description: "How often to run the healthcheck in milliseconds",
   137  						},
   138  						"expected_response": {
   139  							Type:        schema.TypeInt,
   140  							Optional:    true,
   141  							Default:     200,
   142  							Description: "The status code expected from the host",
   143  						},
   144  						"http_version": {
   145  							Type:        schema.TypeString,
   146  							Optional:    true,
   147  							Default:     "1.1",
   148  							Description: "Whether to use version 1.0 or 1.1 HTTP",
   149  						},
   150  						"initial": {
   151  							Type:        schema.TypeInt,
   152  							Optional:    true,
   153  							Default:     2,
   154  							Description: "When loading a config, the initial number of probes to be seen as OK",
   155  						},
   156  						"method": {
   157  							Type:        schema.TypeString,
   158  							Optional:    true,
   159  							Default:     "HEAD",
   160  							Description: "Which HTTP method to use",
   161  						},
   162  						"threshold": {
   163  							Type:        schema.TypeInt,
   164  							Optional:    true,
   165  							Default:     3,
   166  							Description: "How many healthchecks must succeed to be considered healthy",
   167  						},
   168  						"timeout": {
   169  							Type:        schema.TypeInt,
   170  							Optional:    true,
   171  							Default:     500,
   172  							Description: "Timeout in milliseconds",
   173  						},
   174  						"window": {
   175  							Type:        schema.TypeInt,
   176  							Optional:    true,
   177  							Default:     5,
   178  							Description: "The number of most recent healthcheck queries to keep for this healthcheck",
   179  						},
   180  					},
   181  				},
   182  			},
   183  
   184  			"backend": {
   185  				Type:     schema.TypeSet,
   186  				Optional: true,
   187  				Elem: &schema.Resource{
   188  					Schema: map[string]*schema.Schema{
   189  						// required fields
   190  						"name": {
   191  							Type:        schema.TypeString,
   192  							Required:    true,
   193  							Description: "A name for this Backend",
   194  						},
   195  						"address": {
   196  							Type:        schema.TypeString,
   197  							Required:    true,
   198  							Description: "An IPv4, hostname, or IPv6 address for the Backend",
   199  						},
   200  						// Optional fields, defaults where they exist
   201  						"auto_loadbalance": {
   202  							Type:        schema.TypeBool,
   203  							Optional:    true,
   204  							Default:     true,
   205  							Description: "Should this Backend be load balanced",
   206  						},
   207  						"between_bytes_timeout": {
   208  							Type:        schema.TypeInt,
   209  							Optional:    true,
   210  							Default:     10000,
   211  							Description: "How long to wait between bytes in milliseconds",
   212  						},
   213  						"connect_timeout": {
   214  							Type:        schema.TypeInt,
   215  							Optional:    true,
   216  							Default:     1000,
   217  							Description: "How long to wait for a timeout in milliseconds",
   218  						},
   219  						"error_threshold": {
   220  							Type:        schema.TypeInt,
   221  							Optional:    true,
   222  							Default:     0,
   223  							Description: "Number of errors to allow before the Backend is marked as down",
   224  						},
   225  						"first_byte_timeout": {
   226  							Type:        schema.TypeInt,
   227  							Optional:    true,
   228  							Default:     15000,
   229  							Description: "How long to wait for the first bytes in milliseconds",
   230  						},
   231  						"healthcheck": {
   232  							Type:        schema.TypeString,
   233  							Optional:    true,
   234  							Default:     "",
   235  							Description: "The healthcheck name that should be used for this Backend",
   236  						},
   237  						"max_conn": {
   238  							Type:        schema.TypeInt,
   239  							Optional:    true,
   240  							Default:     200,
   241  							Description: "Maximum number of connections for this Backend",
   242  						},
   243  						"port": {
   244  							Type:        schema.TypeInt,
   245  							Optional:    true,
   246  							Default:     80,
   247  							Description: "The port number Backend responds on. Default 80",
   248  						},
   249  						"request_condition": {
   250  							Type:        schema.TypeString,
   251  							Optional:    true,
   252  							Default:     "",
   253  							Description: "Name of a condition, which if met, will select this backend during a request.",
   254  						},
   255  						"shield": {
   256  							Type:        schema.TypeString,
   257  							Optional:    true,
   258  							Default:     "",
   259  							Description: "The POP of the shield designated to reduce inbound load.",
   260  						},
   261  						"ssl_check_cert": {
   262  							Type:        schema.TypeBool,
   263  							Optional:    true,
   264  							Default:     true,
   265  							Description: "Be strict on checking SSL certs",
   266  						},
   267  						"ssl_hostname": {
   268  							Type:        schema.TypeString,
   269  							Optional:    true,
   270  							Default:     "",
   271  							Description: "SSL certificate hostname",
   272  							Deprecated:  "Use ssl_cert_hostname and ssl_sni_hostname instead.",
   273  						},
   274  						"ssl_cert_hostname": {
   275  							Type:        schema.TypeString,
   276  							Optional:    true,
   277  							Default:     "",
   278  							Description: "SSL certificate hostname for cert verification",
   279  						},
   280  						"ssl_sni_hostname": {
   281  							Type:        schema.TypeString,
   282  							Optional:    true,
   283  							Default:     "",
   284  							Description: "SSL certificate hostname for SNI verification",
   285  						},
   286  						// UseSSL is something we want to support in the future, but
   287  						// requires SSL setup we don't yet have
   288  						// TODO: Provide all SSL fields from https://docs.fastly.com/api/config#backend
   289  						// "use_ssl": &schema.Schema{
   290  						// 	Type:        schema.TypeBool,
   291  						// 	Optional:    true,
   292  						// 	Default:     false,
   293  						// 	Description: "Whether or not to use SSL to reach the Backend",
   294  						// },
   295  						"weight": {
   296  							Type:        schema.TypeInt,
   297  							Optional:    true,
   298  							Default:     100,
   299  							Description: "The portion of traffic to send to a specific origins. Each origin receives weight/total of the traffic.",
   300  						},
   301  					},
   302  				},
   303  			},
   304  
   305  			"force_destroy": {
   306  				Type:     schema.TypeBool,
   307  				Optional: true,
   308  			},
   309  
   310  			"cache_setting": {
   311  				Type:     schema.TypeSet,
   312  				Optional: true,
   313  				Elem: &schema.Resource{
   314  					Schema: map[string]*schema.Schema{
   315  						// required fields
   316  						"name": {
   317  							Type:        schema.TypeString,
   318  							Required:    true,
   319  							Description: "A name to refer to this Cache Setting",
   320  						},
   321  						"action": {
   322  							Type:        schema.TypeString,
   323  							Optional:    true,
   324  							Description: "Action to take",
   325  						},
   326  						// optional
   327  						"cache_condition": {
   328  							Type:        schema.TypeString,
   329  							Optional:    true,
   330  							Default:     "",
   331  							Description: "Name of a condition to check if this Cache Setting applies",
   332  						},
   333  						"stale_ttl": {
   334  							Type:        schema.TypeInt,
   335  							Optional:    true,
   336  							Description: "Max 'Time To Live' for stale (unreachable) objects.",
   337  							Default:     300,
   338  						},
   339  						"ttl": {
   340  							Type:        schema.TypeInt,
   341  							Optional:    true,
   342  							Description: "The 'Time To Live' for the object",
   343  						},
   344  					},
   345  				},
   346  			},
   347  
   348  			"gzip": {
   349  				Type:     schema.TypeSet,
   350  				Optional: true,
   351  				Elem: &schema.Resource{
   352  					Schema: map[string]*schema.Schema{
   353  						// required fields
   354  						"name": {
   355  							Type:        schema.TypeString,
   356  							Required:    true,
   357  							Description: "A name to refer to this gzip condition",
   358  						},
   359  						// optional fields
   360  						"content_types": {
   361  							Type:        schema.TypeSet,
   362  							Optional:    true,
   363  							Description: "Content types to apply automatic gzip to",
   364  							Elem:        &schema.Schema{Type: schema.TypeString},
   365  						},
   366  						"extensions": {
   367  							Type:        schema.TypeSet,
   368  							Optional:    true,
   369  							Description: "File extensions to apply automatic gzip to. Do not include '.'",
   370  							Elem:        &schema.Schema{Type: schema.TypeString},
   371  						},
   372  						"cache_condition": {
   373  							Type:        schema.TypeString,
   374  							Optional:    true,
   375  							Default:     "",
   376  							Description: "Name of a condition controlling when this gzip configuration applies.",
   377  						},
   378  					},
   379  				},
   380  			},
   381  
   382  			"header": {
   383  				Type:     schema.TypeSet,
   384  				Optional: true,
   385  				Elem: &schema.Resource{
   386  					Schema: map[string]*schema.Schema{
   387  						// required fields
   388  						"name": {
   389  							Type:        schema.TypeString,
   390  							Required:    true,
   391  							Description: "A name to refer to this Header object",
   392  						},
   393  						"action": {
   394  							Type:        schema.TypeString,
   395  							Required:    true,
   396  							Description: "One of set, append, delete, regex, or regex_repeat",
   397  							ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
   398  								var found bool
   399  								for _, t := range []string{"set", "append", "delete", "regex", "regex_repeat"} {
   400  									if v.(string) == t {
   401  										found = true
   402  									}
   403  								}
   404  								if !found {
   405  									es = append(es, fmt.Errorf(
   406  										"Fastly Header action is case sensitive and must be one of 'set', 'append', 'delete', 'regex', or 'regex_repeat'; found: %s", v.(string)))
   407  								}
   408  								return
   409  							},
   410  						},
   411  						"type": {
   412  							Type:        schema.TypeString,
   413  							Required:    true,
   414  							Description: "Type to manipulate: request, fetch, cache, response",
   415  							ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
   416  								var found bool
   417  								for _, t := range []string{"request", "fetch", "cache", "response"} {
   418  									if v.(string) == t {
   419  										found = true
   420  									}
   421  								}
   422  								if !found {
   423  									es = append(es, fmt.Errorf(
   424  										"Fastly Header type is case sensitive and must be one of 'request', 'fetch', 'cache', or 'response'; found: %s", v.(string)))
   425  								}
   426  								return
   427  							},
   428  						},
   429  						"destination": {
   430  							Type:        schema.TypeString,
   431  							Required:    true,
   432  							Description: "Header this affects",
   433  						},
   434  						// Optional fields, defaults where they exist
   435  						"ignore_if_set": {
   436  							Type:        schema.TypeBool,
   437  							Optional:    true,
   438  							Default:     false,
   439  							Description: "Don't add the header if it is already. (Only applies to 'set' action.). Default `false`",
   440  						},
   441  						"source": {
   442  							Type:        schema.TypeString,
   443  							Optional:    true,
   444  							Computed:    true,
   445  							Description: "Variable to be used as a source for the header content (Does not apply to 'delete' action.)",
   446  						},
   447  						"regex": {
   448  							Type:        schema.TypeString,
   449  							Optional:    true,
   450  							Computed:    true,
   451  							Description: "Regular expression to use (Only applies to 'regex' and 'regex_repeat' actions.)",
   452  						},
   453  						"substitution": {
   454  							Type:        schema.TypeString,
   455  							Optional:    true,
   456  							Computed:    true,
   457  							Description: "Value to substitute in place of regular expression. (Only applies to 'regex' and 'regex_repeat'.)",
   458  						},
   459  						"priority": {
   460  							Type:        schema.TypeInt,
   461  							Optional:    true,
   462  							Default:     100,
   463  							Description: "Lower priorities execute first. (Default: 100.)",
   464  						},
   465  						"request_condition": {
   466  							Type:        schema.TypeString,
   467  							Optional:    true,
   468  							Default:     "",
   469  							Description: "Optional name of a request condition to apply.",
   470  						},
   471  						"cache_condition": {
   472  							Type:        schema.TypeString,
   473  							Optional:    true,
   474  							Default:     "",
   475  							Description: "Optional name of a cache condition to apply.",
   476  						},
   477  						"response_condition": {
   478  							Type:        schema.TypeString,
   479  							Optional:    true,
   480  							Default:     "",
   481  							Description: "Optional name of a response condition to apply.",
   482  						},
   483  					},
   484  				},
   485  			},
   486  
   487  			"s3logging": {
   488  				Type:     schema.TypeSet,
   489  				Optional: true,
   490  				Elem: &schema.Resource{
   491  					Schema: map[string]*schema.Schema{
   492  						// Required fields
   493  						"name": {
   494  							Type:        schema.TypeString,
   495  							Required:    true,
   496  							Description: "Unique name to refer to this logging setup",
   497  						},
   498  						"bucket_name": {
   499  							Type:        schema.TypeString,
   500  							Required:    true,
   501  							Description: "S3 Bucket name to store logs in",
   502  						},
   503  						"s3_access_key": {
   504  							Type:        schema.TypeString,
   505  							Optional:    true,
   506  							DefaultFunc: schema.EnvDefaultFunc("FASTLY_S3_ACCESS_KEY", ""),
   507  							Description: "AWS Access Key",
   508  							Sensitive:   true,
   509  						},
   510  						"s3_secret_key": {
   511  							Type:        schema.TypeString,
   512  							Optional:    true,
   513  							DefaultFunc: schema.EnvDefaultFunc("FASTLY_S3_SECRET_KEY", ""),
   514  							Description: "AWS Secret Key",
   515  							Sensitive:   true,
   516  						},
   517  						// Optional fields
   518  						"path": {
   519  							Type:        schema.TypeString,
   520  							Optional:    true,
   521  							Description: "Path to store the files. Must end with a trailing slash",
   522  						},
   523  						"domain": {
   524  							Type:        schema.TypeString,
   525  							Optional:    true,
   526  							Description: "Bucket endpoint",
   527  						},
   528  						"gzip_level": {
   529  							Type:        schema.TypeInt,
   530  							Optional:    true,
   531  							Default:     0,
   532  							Description: "Gzip Compression level",
   533  						},
   534  						"period": {
   535  							Type:        schema.TypeInt,
   536  							Optional:    true,
   537  							Default:     3600,
   538  							Description: "How frequently the logs should be transferred, in seconds (Default 3600)",
   539  						},
   540  						"format": {
   541  							Type:        schema.TypeString,
   542  							Optional:    true,
   543  							Default:     "%h %l %u %t %r %>s",
   544  							Description: "Apache-style string or VCL variables to use for log formatting",
   545  						},
   546  						"format_version": {
   547  							Type:         schema.TypeInt,
   548  							Optional:     true,
   549  							Default:      1,
   550  							Description:  "The version of the custom logging format used for the configured endpoint. Can be either 1 or 2. (Default: 1)",
   551  							ValidateFunc: validateLoggingFormatVersion,
   552  						},
   553  						"timestamp_format": {
   554  							Type:        schema.TypeString,
   555  							Optional:    true,
   556  							Default:     "%Y-%m-%dT%H:%M:%S.000",
   557  							Description: "specified timestamp formatting (default `%Y-%m-%dT%H:%M:%S.000`)",
   558  						},
   559  						"response_condition": {
   560  							Type:        schema.TypeString,
   561  							Optional:    true,
   562  							Default:     "",
   563  							Description: "Name of a condition to apply this logging.",
   564  						},
   565  					},
   566  				},
   567  			},
   568  
   569  			"papertrail": {
   570  				Type:     schema.TypeSet,
   571  				Optional: true,
   572  				Elem: &schema.Resource{
   573  					Schema: map[string]*schema.Schema{
   574  						// Required fields
   575  						"name": {
   576  							Type:        schema.TypeString,
   577  							Required:    true,
   578  							Description: "Unique name to refer to this logging setup",
   579  						},
   580  						"address": {
   581  							Type:        schema.TypeString,
   582  							Required:    true,
   583  							Description: "The address of the papertrail service",
   584  						},
   585  						"port": {
   586  							Type:        schema.TypeInt,
   587  							Required:    true,
   588  							Description: "The port of the papertrail service",
   589  						},
   590  						// Optional fields
   591  						"format": {
   592  							Type:        schema.TypeString,
   593  							Optional:    true,
   594  							Default:     "%h %l %u %t %r %>s",
   595  							Description: "Apache-style string or VCL variables to use for log formatting",
   596  						},
   597  						"response_condition": {
   598  							Type:        schema.TypeString,
   599  							Optional:    true,
   600  							Default:     "",
   601  							Description: "Name of a condition to apply this logging",
   602  						},
   603  					},
   604  				},
   605  			},
   606  			"sumologic": {
   607  				Type:     schema.TypeSet,
   608  				Optional: true,
   609  				Elem: &schema.Resource{
   610  					Schema: map[string]*schema.Schema{
   611  						// Required fields
   612  						"name": {
   613  							Type:        schema.TypeString,
   614  							Required:    true,
   615  							Description: "Unique name to refer to this logging setup",
   616  						},
   617  						"url": {
   618  							Type:        schema.TypeString,
   619  							Required:    true,
   620  							Description: "The URL to POST to.",
   621  						},
   622  						// Optional fields
   623  						"format": {
   624  							Type:        schema.TypeString,
   625  							Optional:    true,
   626  							Default:     "%h %l %u %t %r %>s",
   627  							Description: "Apache-style string or VCL variables to use for log formatting",
   628  						},
   629  						"format_version": {
   630  							Type:         schema.TypeInt,
   631  							Optional:     true,
   632  							Default:      1,
   633  							Description:  "The version of the custom logging format used for the configured endpoint. Can be either 1 or 2. (Default: 1)",
   634  							ValidateFunc: validateLoggingFormatVersion,
   635  						},
   636  						"response_condition": {
   637  							Type:        schema.TypeString,
   638  							Optional:    true,
   639  							Default:     "",
   640  							Description: "Name of a condition to apply this logging.",
   641  						},
   642  						"message_type": {
   643  							Type:         schema.TypeString,
   644  							Optional:     true,
   645  							Default:      "classic",
   646  							Description:  "How the message should be formatted.",
   647  							ValidateFunc: validateLoggingMessageType,
   648  						},
   649  					},
   650  				},
   651  			},
   652  
   653  			"gcslogging": {
   654  				Type:     schema.TypeSet,
   655  				Optional: true,
   656  				Elem: &schema.Resource{
   657  					Schema: map[string]*schema.Schema{
   658  						// Required fields
   659  						"name": {
   660  							Type:        schema.TypeString,
   661  							Required:    true,
   662  							Description: "Unique name to refer to this logging setup",
   663  						},
   664  						"email": {
   665  							Type:        schema.TypeString,
   666  							Required:    true,
   667  							Description: "The email address associated with the target GCS bucket on your account.",
   668  						},
   669  						"bucket_name": {
   670  							Type:        schema.TypeString,
   671  							Required:    true,
   672  							Description: "The name of the bucket in which to store the logs.",
   673  						},
   674  						"secret_key": {
   675  							Type:        schema.TypeString,
   676  							Required:    true,
   677  							Description: "The secret key associated with the target gcs bucket on your account.",
   678  							Sensitive:   true,
   679  						},
   680  						// Optional fields
   681  						"path": {
   682  							Type:        schema.TypeString,
   683  							Optional:    true,
   684  							Description: "Path to store the files. Must end with a trailing slash",
   685  						},
   686  						"gzip_level": {
   687  							Type:        schema.TypeInt,
   688  							Optional:    true,
   689  							Default:     0,
   690  							Description: "Gzip Compression level",
   691  						},
   692  						"period": {
   693  							Type:        schema.TypeInt,
   694  							Optional:    true,
   695  							Default:     3600,
   696  							Description: "How frequently the logs should be transferred, in seconds (Default 3600)",
   697  						},
   698  						"format": {
   699  							Type:        schema.TypeString,
   700  							Optional:    true,
   701  							Default:     "%h %l %u %t %r %>s",
   702  							Description: "Apache-style string or VCL variables to use for log formatting",
   703  						},
   704  						"timestamp_format": {
   705  							Type:        schema.TypeString,
   706  							Optional:    true,
   707  							Default:     "%Y-%m-%dT%H:%M:%S.000",
   708  							Description: "specified timestamp formatting (default `%Y-%m-%dT%H:%M:%S.000`)",
   709  						},
   710  						"response_condition": {
   711  							Type:        schema.TypeString,
   712  							Optional:    true,
   713  							Default:     "",
   714  							Description: "Name of a condition to apply this logging.",
   715  						},
   716  					},
   717  				},
   718  			},
   719  
   720  			"response_object": {
   721  				Type:     schema.TypeSet,
   722  				Optional: true,
   723  				Elem: &schema.Resource{
   724  					Schema: map[string]*schema.Schema{
   725  						// Required
   726  						"name": {
   727  							Type:        schema.TypeString,
   728  							Required:    true,
   729  							Description: "Unique name to refer to this request object",
   730  						},
   731  						// Optional fields
   732  						"status": {
   733  							Type:        schema.TypeInt,
   734  							Optional:    true,
   735  							Default:     200,
   736  							Description: "The HTTP Status Code of the object",
   737  						},
   738  						"response": {
   739  							Type:        schema.TypeString,
   740  							Optional:    true,
   741  							Default:     "OK",
   742  							Description: "The HTTP Response of the object",
   743  						},
   744  						"content": {
   745  							Type:        schema.TypeString,
   746  							Optional:    true,
   747  							Default:     "",
   748  							Description: "The content to deliver for the response object",
   749  						},
   750  						"content_type": {
   751  							Type:        schema.TypeString,
   752  							Optional:    true,
   753  							Default:     "",
   754  							Description: "The MIME type of the content",
   755  						},
   756  						"request_condition": {
   757  							Type:        schema.TypeString,
   758  							Optional:    true,
   759  							Default:     "",
   760  							Description: "Name of the condition to be checked during the request phase to see if the object should be delivered",
   761  						},
   762  						"cache_condition": {
   763  							Type:        schema.TypeString,
   764  							Optional:    true,
   765  							Default:     "",
   766  							Description: "Name of the condition checked after we have retrieved an object. If the condition passes then deliver this Request Object instead.",
   767  						},
   768  					},
   769  				},
   770  			},
   771  
   772  			"request_setting": {
   773  				Type:     schema.TypeSet,
   774  				Optional: true,
   775  				Elem: &schema.Resource{
   776  					Schema: map[string]*schema.Schema{
   777  						// Required fields
   778  						"name": {
   779  							Type:        schema.TypeString,
   780  							Required:    true,
   781  							Description: "Unique name to refer to this Request Setting",
   782  						},
   783  						// Optional fields
   784  						"request_condition": {
   785  							Type:        schema.TypeString,
   786  							Optional:    true,
   787  							Default:     "",
   788  							Description: "Name of a request condition to apply. If there is no condition this setting will always be applied.",
   789  						},
   790  						"max_stale_age": {
   791  							Type:        schema.TypeInt,
   792  							Optional:    true,
   793  							Default:     60,
   794  							Description: "How old an object is allowed to be, in seconds. Default `60`",
   795  						},
   796  						"force_miss": {
   797  							Type:        schema.TypeBool,
   798  							Optional:    true,
   799  							Description: "Force a cache miss for the request",
   800  						},
   801  						"force_ssl": {
   802  							Type:        schema.TypeBool,
   803  							Optional:    true,
   804  							Description: "Forces the request use SSL",
   805  						},
   806  						"action": {
   807  							Type:        schema.TypeString,
   808  							Optional:    true,
   809  							Description: "Allows you to terminate request handling and immediately perform an action",
   810  						},
   811  						"bypass_busy_wait": {
   812  							Type:        schema.TypeBool,
   813  							Optional:    true,
   814  							Description: "Disable collapsed forwarding",
   815  						},
   816  						"hash_keys": {
   817  							Type:        schema.TypeString,
   818  							Optional:    true,
   819  							Description: "Comma separated list of varnish request object fields that should be in the hash key",
   820  						},
   821  						"xff": {
   822  							Type:        schema.TypeString,
   823  							Optional:    true,
   824  							Default:     "append",
   825  							Description: "X-Forwarded-For options",
   826  						},
   827  						"timer_support": {
   828  							Type:        schema.TypeBool,
   829  							Optional:    true,
   830  							Description: "Injects the X-Timer info into the request",
   831  						},
   832  						"geo_headers": {
   833  							Type:        schema.TypeBool,
   834  							Optional:    true,
   835  							Description: "Inject Fastly-Geo-Country, Fastly-Geo-City, and Fastly-Geo-Region",
   836  						},
   837  						"default_host": {
   838  							Type:        schema.TypeString,
   839  							Optional:    true,
   840  							Description: "the host header",
   841  						},
   842  					},
   843  				},
   844  			},
   845  			"vcl": {
   846  				Type:     schema.TypeSet,
   847  				Optional: true,
   848  				Elem: &schema.Resource{
   849  					Schema: map[string]*schema.Schema{
   850  						"name": {
   851  							Type:        schema.TypeString,
   852  							Required:    true,
   853  							Description: "A name to refer to this VCL configuration",
   854  						},
   855  						"content": {
   856  							Type:        schema.TypeString,
   857  							Required:    true,
   858  							Description: "The contents of this VCL configuration",
   859  							StateFunc: func(v interface{}) string {
   860  								switch v.(type) {
   861  								case string:
   862  									hash := sha1.Sum([]byte(v.(string)))
   863  									return hex.EncodeToString(hash[:])
   864  								default:
   865  									return ""
   866  								}
   867  							},
   868  						},
   869  						"main": {
   870  							Type:        schema.TypeBool,
   871  							Optional:    true,
   872  							Default:     false,
   873  							Description: "Should this VCL configuration be the main configuration",
   874  						},
   875  					},
   876  				},
   877  			},
   878  		},
   879  	}
   880  }
   881  
   882  func resourceServiceV1Create(d *schema.ResourceData, meta interface{}) error {
   883  	if err := validateVCLs(d); err != nil {
   884  		return err
   885  	}
   886  
   887  	conn := meta.(*FastlyClient).conn
   888  	service, err := conn.CreateService(&gofastly.CreateServiceInput{
   889  		Name:    d.Get("name").(string),
   890  		Comment: "Managed by Terraform",
   891  	})
   892  
   893  	if err != nil {
   894  		return err
   895  	}
   896  
   897  	d.SetId(service.ID)
   898  	return resourceServiceV1Update(d, meta)
   899  }
   900  
   901  func resourceServiceV1Update(d *schema.ResourceData, meta interface{}) error {
   902  	if err := validateVCLs(d); err != nil {
   903  		return err
   904  	}
   905  
   906  	conn := meta.(*FastlyClient).conn
   907  
   908  	// Update Name. No new verions is required for this
   909  	if d.HasChange("name") {
   910  		_, err := conn.UpdateService(&gofastly.UpdateServiceInput{
   911  			ID:   d.Id(),
   912  			Name: d.Get("name").(string),
   913  		})
   914  		if err != nil {
   915  			return err
   916  		}
   917  	}
   918  
   919  	// Once activated, Versions are locked and become immutable. This is true for
   920  	// versions that are no longer active. For Domains, Backends, DefaultHost and
   921  	// DefaultTTL, a new Version must be created first, and updates posted to that
   922  	// Version. Loop these attributes and determine if we need to create a new version first
   923  	var needsChange bool
   924  	for _, v := range []string{
   925  		"domain",
   926  		"backend",
   927  		"default_host",
   928  		"default_ttl",
   929  		"header",
   930  		"gzip",
   931  		"healthcheck",
   932  		"s3logging",
   933  		"papertrail",
   934  		"response_object",
   935  		"condition",
   936  		"request_setting",
   937  		"cache_setting",
   938  		"vcl",
   939  	} {
   940  		if d.HasChange(v) {
   941  			needsChange = true
   942  		}
   943  	}
   944  
   945  	if needsChange {
   946  		latestVersion := d.Get("active_version").(int)
   947  		if latestVersion == 0 {
   948  			// If the service was just created, there is an empty Version 1 available
   949  			// that is unlocked and can be updated
   950  			latestVersion = 1
   951  		} else {
   952  			// Clone the latest version, giving us an unlocked version we can modify
   953  			log.Printf("[DEBUG] Creating clone of version (%d) for updates", latestVersion)
   954  			newVersion, err := conn.CloneVersion(&gofastly.CloneVersionInput{
   955  				Service: d.Id(),
   956  				Version: latestVersion,
   957  			})
   958  			if err != nil {
   959  				return err
   960  			}
   961  
   962  			// The new version number is named "Number", but it's actually a string
   963  			latestVersion = newVersion.Number
   964  
   965  			// New versions are not immediately found in the API, or are not
   966  			// immediately mutable, so we need to sleep a few and let Fastly ready
   967  			// itself. Typically, 7 seconds is enough
   968  			log.Print("[DEBUG] Sleeping 7 seconds to allow Fastly Version to be available")
   969  			time.Sleep(7 * time.Second)
   970  		}
   971  
   972  		// update general settings
   973  		if d.HasChange("default_host") || d.HasChange("default_ttl") {
   974  			opts := gofastly.UpdateSettingsInput{
   975  				Service: d.Id(),
   976  				Version: latestVersion,
   977  				// default_ttl has the same default value of 3600 that is provided by
   978  				// the Fastly API, so it's safe to include here
   979  				DefaultTTL: uint(d.Get("default_ttl").(int)),
   980  			}
   981  
   982  			if attr, ok := d.GetOk("default_host"); ok {
   983  				opts.DefaultHost = attr.(string)
   984  			}
   985  
   986  			log.Printf("[DEBUG] Update Settings opts: %#v", opts)
   987  			_, err := conn.UpdateSettings(&opts)
   988  			if err != nil {
   989  				return err
   990  			}
   991  		}
   992  
   993  		// Conditions need to be updated first, as they can be referenced by other
   994  		// configuraiton objects (Backends, Request Headers, etc)
   995  
   996  		// Find difference in Conditions
   997  		if d.HasChange("condition") {
   998  			// Note: we don't utilize the PUT endpoint to update these objects, we simply
   999  			// destroy any that have changed, and create new ones with the updated
  1000  			// values. This is how Terraform works with nested sub resources, we only
  1001  			// get the full diff not a partial set item diff. Because this is done
  1002  			// on a new version of the Fastly Service configuration, this is considered safe
  1003  
  1004  			oc, nc := d.GetChange("condition")
  1005  			if oc == nil {
  1006  				oc = new(schema.Set)
  1007  			}
  1008  			if nc == nil {
  1009  				nc = new(schema.Set)
  1010  			}
  1011  
  1012  			ocs := oc.(*schema.Set)
  1013  			ncs := nc.(*schema.Set)
  1014  			removeConditions := ocs.Difference(ncs).List()
  1015  			addConditions := ncs.Difference(ocs).List()
  1016  
  1017  			// DELETE old Conditions
  1018  			for _, cRaw := range removeConditions {
  1019  				cf := cRaw.(map[string]interface{})
  1020  				opts := gofastly.DeleteConditionInput{
  1021  					Service: d.Id(),
  1022  					Version: latestVersion,
  1023  					Name:    cf["name"].(string),
  1024  				}
  1025  
  1026  				log.Printf("[DEBUG] Fastly Conditions Removal opts: %#v", opts)
  1027  				err := conn.DeleteCondition(&opts)
  1028  				if err != nil {
  1029  					return err
  1030  				}
  1031  			}
  1032  
  1033  			// POST new Conditions
  1034  			for _, cRaw := range addConditions {
  1035  				cf := cRaw.(map[string]interface{})
  1036  				opts := gofastly.CreateConditionInput{
  1037  					Service: d.Id(),
  1038  					Version: latestVersion,
  1039  					Name:    cf["name"].(string),
  1040  					Type:    cf["type"].(string),
  1041  					// need to trim leading/tailing spaces, incase the config has HEREDOC
  1042  					// formatting and contains a trailing new line
  1043  					Statement: strings.TrimSpace(cf["statement"].(string)),
  1044  					Priority:  cf["priority"].(int),
  1045  				}
  1046  
  1047  				log.Printf("[DEBUG] Create Conditions Opts: %#v", opts)
  1048  				_, err := conn.CreateCondition(&opts)
  1049  				if err != nil {
  1050  					return err
  1051  				}
  1052  			}
  1053  		}
  1054  
  1055  		// Find differences in domains
  1056  		if d.HasChange("domain") {
  1057  			od, nd := d.GetChange("domain")
  1058  			if od == nil {
  1059  				od = new(schema.Set)
  1060  			}
  1061  			if nd == nil {
  1062  				nd = new(schema.Set)
  1063  			}
  1064  
  1065  			ods := od.(*schema.Set)
  1066  			nds := nd.(*schema.Set)
  1067  
  1068  			remove := ods.Difference(nds).List()
  1069  			add := nds.Difference(ods).List()
  1070  
  1071  			// Delete removed domains
  1072  			for _, dRaw := range remove {
  1073  				df := dRaw.(map[string]interface{})
  1074  				opts := gofastly.DeleteDomainInput{
  1075  					Service: d.Id(),
  1076  					Version: latestVersion,
  1077  					Name:    df["name"].(string),
  1078  				}
  1079  
  1080  				log.Printf("[DEBUG] Fastly Domain removal opts: %#v", opts)
  1081  				err := conn.DeleteDomain(&opts)
  1082  				if err != nil {
  1083  					return err
  1084  				}
  1085  			}
  1086  
  1087  			// POST new Domains
  1088  			for _, dRaw := range add {
  1089  				df := dRaw.(map[string]interface{})
  1090  				opts := gofastly.CreateDomainInput{
  1091  					Service: d.Id(),
  1092  					Version: latestVersion,
  1093  					Name:    df["name"].(string),
  1094  				}
  1095  
  1096  				if v, ok := df["comment"]; ok {
  1097  					opts.Comment = v.(string)
  1098  				}
  1099  
  1100  				log.Printf("[DEBUG] Fastly Domain Addition opts: %#v", opts)
  1101  				_, err := conn.CreateDomain(&opts)
  1102  				if err != nil {
  1103  					return err
  1104  				}
  1105  			}
  1106  		}
  1107  
  1108  		// Healthchecks need to be updated BEFORE backends
  1109  		if d.HasChange("healthcheck") {
  1110  			oh, nh := d.GetChange("healthcheck")
  1111  			if oh == nil {
  1112  				oh = new(schema.Set)
  1113  			}
  1114  			if nh == nil {
  1115  				nh = new(schema.Set)
  1116  			}
  1117  
  1118  			ohs := oh.(*schema.Set)
  1119  			nhs := nh.(*schema.Set)
  1120  			removeHealthCheck := ohs.Difference(nhs).List()
  1121  			addHealthCheck := nhs.Difference(ohs).List()
  1122  
  1123  			// DELETE old healthcheck configurations
  1124  			for _, hRaw := range removeHealthCheck {
  1125  				hf := hRaw.(map[string]interface{})
  1126  				opts := gofastly.DeleteHealthCheckInput{
  1127  					Service: d.Id(),
  1128  					Version: latestVersion,
  1129  					Name:    hf["name"].(string),
  1130  				}
  1131  
  1132  				log.Printf("[DEBUG] Fastly Healthcheck removal opts: %#v", opts)
  1133  				err := conn.DeleteHealthCheck(&opts)
  1134  				if err != nil {
  1135  					return err
  1136  				}
  1137  			}
  1138  
  1139  			// POST new/updated Healthcheck
  1140  			for _, hRaw := range addHealthCheck {
  1141  				hf := hRaw.(map[string]interface{})
  1142  
  1143  				opts := gofastly.CreateHealthCheckInput{
  1144  					Service:          d.Id(),
  1145  					Version:          latestVersion,
  1146  					Name:             hf["name"].(string),
  1147  					Host:             hf["host"].(string),
  1148  					Path:             hf["path"].(string),
  1149  					CheckInterval:    uint(hf["check_interval"].(int)),
  1150  					ExpectedResponse: uint(hf["expected_response"].(int)),
  1151  					HTTPVersion:      hf["http_version"].(string),
  1152  					Initial:          uint(hf["initial"].(int)),
  1153  					Method:           hf["method"].(string),
  1154  					Threshold:        uint(hf["threshold"].(int)),
  1155  					Timeout:          uint(hf["timeout"].(int)),
  1156  					Window:           uint(hf["window"].(int)),
  1157  				}
  1158  
  1159  				log.Printf("[DEBUG] Create Healthcheck Opts: %#v", opts)
  1160  				_, err := conn.CreateHealthCheck(&opts)
  1161  				if err != nil {
  1162  					return err
  1163  				}
  1164  			}
  1165  		}
  1166  
  1167  		// find difference in backends
  1168  		if d.HasChange("backend") {
  1169  			ob, nb := d.GetChange("backend")
  1170  			if ob == nil {
  1171  				ob = new(schema.Set)
  1172  			}
  1173  			if nb == nil {
  1174  				nb = new(schema.Set)
  1175  			}
  1176  
  1177  			obs := ob.(*schema.Set)
  1178  			nbs := nb.(*schema.Set)
  1179  			removeBackends := obs.Difference(nbs).List()
  1180  			addBackends := nbs.Difference(obs).List()
  1181  
  1182  			// DELETE old Backends
  1183  			for _, bRaw := range removeBackends {
  1184  				bf := bRaw.(map[string]interface{})
  1185  				opts := gofastly.DeleteBackendInput{
  1186  					Service: d.Id(),
  1187  					Version: latestVersion,
  1188  					Name:    bf["name"].(string),
  1189  				}
  1190  
  1191  				log.Printf("[DEBUG] Fastly Backend removal opts: %#v", opts)
  1192  				err := conn.DeleteBackend(&opts)
  1193  				if err != nil {
  1194  					return err
  1195  				}
  1196  			}
  1197  
  1198  			// Find and post new Backends
  1199  			for _, dRaw := range addBackends {
  1200  				df := dRaw.(map[string]interface{})
  1201  				opts := gofastly.CreateBackendInput{
  1202  					Service:             d.Id(),
  1203  					Version:             latestVersion,
  1204  					Name:                df["name"].(string),
  1205  					Address:             df["address"].(string),
  1206  					AutoLoadbalance:     gofastly.CBool(df["auto_loadbalance"].(bool)),
  1207  					SSLCheckCert:        gofastly.CBool(df["ssl_check_cert"].(bool)),
  1208  					SSLHostname:         df["ssl_hostname"].(string),
  1209  					SSLCertHostname:     df["ssl_cert_hostname"].(string),
  1210  					SSLSNIHostname:      df["ssl_sni_hostname"].(string),
  1211  					Shield:              df["shield"].(string),
  1212  					Port:                uint(df["port"].(int)),
  1213  					BetweenBytesTimeout: uint(df["between_bytes_timeout"].(int)),
  1214  					ConnectTimeout:      uint(df["connect_timeout"].(int)),
  1215  					ErrorThreshold:      uint(df["error_threshold"].(int)),
  1216  					FirstByteTimeout:    uint(df["first_byte_timeout"].(int)),
  1217  					MaxConn:             uint(df["max_conn"].(int)),
  1218  					Weight:              uint(df["weight"].(int)),
  1219  					RequestCondition:    df["request_condition"].(string),
  1220  					HealthCheck:         df["healthcheck"].(string),
  1221  				}
  1222  
  1223  				log.Printf("[DEBUG] Create Backend Opts: %#v", opts)
  1224  				_, err := conn.CreateBackend(&opts)
  1225  				if err != nil {
  1226  					return err
  1227  				}
  1228  			}
  1229  		}
  1230  
  1231  		if d.HasChange("header") {
  1232  			oh, nh := d.GetChange("header")
  1233  			if oh == nil {
  1234  				oh = new(schema.Set)
  1235  			}
  1236  			if nh == nil {
  1237  				nh = new(schema.Set)
  1238  			}
  1239  
  1240  			ohs := oh.(*schema.Set)
  1241  			nhs := nh.(*schema.Set)
  1242  
  1243  			remove := ohs.Difference(nhs).List()
  1244  			add := nhs.Difference(ohs).List()
  1245  
  1246  			// Delete removed headers
  1247  			for _, dRaw := range remove {
  1248  				df := dRaw.(map[string]interface{})
  1249  				opts := gofastly.DeleteHeaderInput{
  1250  					Service: d.Id(),
  1251  					Version: latestVersion,
  1252  					Name:    df["name"].(string),
  1253  				}
  1254  
  1255  				log.Printf("[DEBUG] Fastly Header removal opts: %#v", opts)
  1256  				err := conn.DeleteHeader(&opts)
  1257  				if err != nil {
  1258  					return err
  1259  				}
  1260  			}
  1261  
  1262  			// POST new Headers
  1263  			for _, dRaw := range add {
  1264  				opts, err := buildHeader(dRaw.(map[string]interface{}))
  1265  				if err != nil {
  1266  					log.Printf("[DEBUG] Error building Header: %s", err)
  1267  					return err
  1268  				}
  1269  				opts.Service = d.Id()
  1270  				opts.Version = latestVersion
  1271  
  1272  				log.Printf("[DEBUG] Fastly Header Addition opts: %#v", opts)
  1273  				_, err = conn.CreateHeader(opts)
  1274  				if err != nil {
  1275  					return err
  1276  				}
  1277  			}
  1278  		}
  1279  
  1280  		// Find differences in Gzips
  1281  		if d.HasChange("gzip") {
  1282  			og, ng := d.GetChange("gzip")
  1283  			if og == nil {
  1284  				og = new(schema.Set)
  1285  			}
  1286  			if ng == nil {
  1287  				ng = new(schema.Set)
  1288  			}
  1289  
  1290  			ogs := og.(*schema.Set)
  1291  			ngs := ng.(*schema.Set)
  1292  
  1293  			remove := ogs.Difference(ngs).List()
  1294  			add := ngs.Difference(ogs).List()
  1295  
  1296  			// Delete removed gzip rules
  1297  			for _, dRaw := range remove {
  1298  				df := dRaw.(map[string]interface{})
  1299  				opts := gofastly.DeleteGzipInput{
  1300  					Service: d.Id(),
  1301  					Version: latestVersion,
  1302  					Name:    df["name"].(string),
  1303  				}
  1304  
  1305  				log.Printf("[DEBUG] Fastly Gzip removal opts: %#v", opts)
  1306  				err := conn.DeleteGzip(&opts)
  1307  				if err != nil {
  1308  					return err
  1309  				}
  1310  			}
  1311  
  1312  			// POST new Gzips
  1313  			for _, dRaw := range add {
  1314  				df := dRaw.(map[string]interface{})
  1315  				opts := gofastly.CreateGzipInput{
  1316  					Service:        d.Id(),
  1317  					Version:        latestVersion,
  1318  					Name:           df["name"].(string),
  1319  					CacheCondition: df["cache_condition"].(string),
  1320  				}
  1321  
  1322  				if v, ok := df["content_types"]; ok {
  1323  					if len(v.(*schema.Set).List()) > 0 {
  1324  						var cl []string
  1325  						for _, c := range v.(*schema.Set).List() {
  1326  							cl = append(cl, c.(string))
  1327  						}
  1328  						opts.ContentTypes = strings.Join(cl, " ")
  1329  					}
  1330  				}
  1331  
  1332  				if v, ok := df["extensions"]; ok {
  1333  					if len(v.(*schema.Set).List()) > 0 {
  1334  						var el []string
  1335  						for _, e := range v.(*schema.Set).List() {
  1336  							el = append(el, e.(string))
  1337  						}
  1338  						opts.Extensions = strings.Join(el, " ")
  1339  					}
  1340  				}
  1341  
  1342  				log.Printf("[DEBUG] Fastly Gzip Addition opts: %#v", opts)
  1343  				_, err := conn.CreateGzip(&opts)
  1344  				if err != nil {
  1345  					return err
  1346  				}
  1347  			}
  1348  		}
  1349  
  1350  		// find difference in s3logging
  1351  		if d.HasChange("s3logging") {
  1352  			os, ns := d.GetChange("s3logging")
  1353  			if os == nil {
  1354  				os = new(schema.Set)
  1355  			}
  1356  			if ns == nil {
  1357  				ns = new(schema.Set)
  1358  			}
  1359  
  1360  			oss := os.(*schema.Set)
  1361  			nss := ns.(*schema.Set)
  1362  			removeS3Logging := oss.Difference(nss).List()
  1363  			addS3Logging := nss.Difference(oss).List()
  1364  
  1365  			// DELETE old S3 Log configurations
  1366  			for _, sRaw := range removeS3Logging {
  1367  				sf := sRaw.(map[string]interface{})
  1368  				opts := gofastly.DeleteS3Input{
  1369  					Service: d.Id(),
  1370  					Version: latestVersion,
  1371  					Name:    sf["name"].(string),
  1372  				}
  1373  
  1374  				log.Printf("[DEBUG] Fastly S3 Logging removal opts: %#v", opts)
  1375  				err := conn.DeleteS3(&opts)
  1376  				if err != nil {
  1377  					return err
  1378  				}
  1379  			}
  1380  
  1381  			// POST new/updated S3 Logging
  1382  			for _, sRaw := range addS3Logging {
  1383  				sf := sRaw.(map[string]interface{})
  1384  
  1385  				// Fastly API will not error if these are omitted, so we throw an error
  1386  				// if any of these are empty
  1387  				for _, sk := range []string{"s3_access_key", "s3_secret_key"} {
  1388  					if sf[sk].(string) == "" {
  1389  						return fmt.Errorf("[ERR] No %s found for S3 Log stream setup for Service (%s)", sk, d.Id())
  1390  					}
  1391  				}
  1392  
  1393  				opts := gofastly.CreateS3Input{
  1394  					Service:           d.Id(),
  1395  					Version:           latestVersion,
  1396  					Name:              sf["name"].(string),
  1397  					BucketName:        sf["bucket_name"].(string),
  1398  					AccessKey:         sf["s3_access_key"].(string),
  1399  					SecretKey:         sf["s3_secret_key"].(string),
  1400  					Period:            uint(sf["period"].(int)),
  1401  					GzipLevel:         uint(sf["gzip_level"].(int)),
  1402  					Domain:            sf["domain"].(string),
  1403  					Path:              sf["path"].(string),
  1404  					Format:            sf["format"].(string),
  1405  					FormatVersion:     uint(sf["format_version"].(int)),
  1406  					TimestampFormat:   sf["timestamp_format"].(string),
  1407  					ResponseCondition: sf["response_condition"].(string),
  1408  				}
  1409  
  1410  				log.Printf("[DEBUG] Create S3 Logging Opts: %#v", opts)
  1411  				_, err := conn.CreateS3(&opts)
  1412  				if err != nil {
  1413  					return err
  1414  				}
  1415  			}
  1416  		}
  1417  
  1418  		// find difference in Papertrail
  1419  		if d.HasChange("papertrail") {
  1420  			os, ns := d.GetChange("papertrail")
  1421  			if os == nil {
  1422  				os = new(schema.Set)
  1423  			}
  1424  			if ns == nil {
  1425  				ns = new(schema.Set)
  1426  			}
  1427  
  1428  			oss := os.(*schema.Set)
  1429  			nss := ns.(*schema.Set)
  1430  			removePapertrail := oss.Difference(nss).List()
  1431  			addPapertrail := nss.Difference(oss).List()
  1432  
  1433  			// DELETE old papertrail configurations
  1434  			for _, pRaw := range removePapertrail {
  1435  				pf := pRaw.(map[string]interface{})
  1436  				opts := gofastly.DeletePapertrailInput{
  1437  					Service: d.Id(),
  1438  					Version: latestVersion,
  1439  					Name:    pf["name"].(string),
  1440  				}
  1441  
  1442  				log.Printf("[DEBUG] Fastly Papertrail removal opts: %#v", opts)
  1443  				err := conn.DeletePapertrail(&opts)
  1444  				if err != nil {
  1445  					return err
  1446  				}
  1447  			}
  1448  
  1449  			// POST new/updated Papertrail
  1450  			for _, pRaw := range addPapertrail {
  1451  				pf := pRaw.(map[string]interface{})
  1452  
  1453  				opts := gofastly.CreatePapertrailInput{
  1454  					Service:           d.Id(),
  1455  					Version:           latestVersion,
  1456  					Name:              pf["name"].(string),
  1457  					Address:           pf["address"].(string),
  1458  					Port:              uint(pf["port"].(int)),
  1459  					Format:            pf["format"].(string),
  1460  					ResponseCondition: pf["response_condition"].(string),
  1461  				}
  1462  
  1463  				log.Printf("[DEBUG] Create Papertrail Opts: %#v", opts)
  1464  				_, err := conn.CreatePapertrail(&opts)
  1465  				if err != nil {
  1466  					return err
  1467  				}
  1468  			}
  1469  		}
  1470  
  1471  		// find difference in Sumologic
  1472  		if d.HasChange("sumologic") {
  1473  			os, ns := d.GetChange("sumologic")
  1474  			if os == nil {
  1475  				os = new(schema.Set)
  1476  			}
  1477  			if ns == nil {
  1478  				ns = new(schema.Set)
  1479  			}
  1480  
  1481  			oss := os.(*schema.Set)
  1482  			nss := ns.(*schema.Set)
  1483  			removeSumologic := oss.Difference(nss).List()
  1484  			addSumologic := nss.Difference(oss).List()
  1485  
  1486  			// DELETE old sumologic configurations
  1487  			for _, pRaw := range removeSumologic {
  1488  				sf := pRaw.(map[string]interface{})
  1489  				opts := gofastly.DeleteSumologicInput{
  1490  					Service: d.Id(),
  1491  					Version: latestVersion,
  1492  					Name:    sf["name"].(string),
  1493  				}
  1494  
  1495  				log.Printf("[DEBUG] Fastly Sumologic removal opts: %#v", opts)
  1496  				err := conn.DeleteSumologic(&opts)
  1497  				if err != nil {
  1498  					return err
  1499  				}
  1500  			}
  1501  
  1502  			// POST new/updated Sumologic
  1503  			for _, pRaw := range addSumologic {
  1504  				sf := pRaw.(map[string]interface{})
  1505  				opts := gofastly.CreateSumologicInput{
  1506  					Service:           d.Id(),
  1507  					Version:           latestVersion,
  1508  					Name:              sf["name"].(string),
  1509  					URL:               sf["url"].(string),
  1510  					Format:            sf["format"].(string),
  1511  					FormatVersion:     sf["format_version"].(int),
  1512  					ResponseCondition: sf["response_condition"].(string),
  1513  					MessageType:       sf["message_type"].(string),
  1514  				}
  1515  
  1516  				log.Printf("[DEBUG] Create Sumologic Opts: %#v", opts)
  1517  				_, err := conn.CreateSumologic(&opts)
  1518  				if err != nil {
  1519  					return err
  1520  				}
  1521  			}
  1522  		}
  1523  
  1524  		// find difference in gcslogging
  1525  		if d.HasChange("gcslogging") {
  1526  			os, ns := d.GetChange("gcslogging")
  1527  			if os == nil {
  1528  				os = new(schema.Set)
  1529  			}
  1530  			if ns == nil {
  1531  				ns = new(schema.Set)
  1532  			}
  1533  
  1534  			oss := os.(*schema.Set)
  1535  			nss := ns.(*schema.Set)
  1536  			removeGcslogging := oss.Difference(nss).List()
  1537  			addGcslogging := nss.Difference(oss).List()
  1538  
  1539  			// DELETE old gcslogging configurations
  1540  			for _, pRaw := range removeGcslogging {
  1541  				sf := pRaw.(map[string]interface{})
  1542  				opts := gofastly.DeleteGCSInput{
  1543  					Service: d.Id(),
  1544  					Version: latestVersion,
  1545  					Name:    sf["name"].(string),
  1546  				}
  1547  
  1548  				log.Printf("[DEBUG] Fastly gcslogging removal opts: %#v", opts)
  1549  				err := conn.DeleteGCS(&opts)
  1550  				if err != nil {
  1551  					return err
  1552  				}
  1553  			}
  1554  
  1555  			// POST new/updated gcslogging
  1556  			for _, pRaw := range addGcslogging {
  1557  				sf := pRaw.(map[string]interface{})
  1558  				opts := gofastly.CreateGCSInput{
  1559  					Service:           d.Id(),
  1560  					Version:           latestVersion,
  1561  					Name:              sf["name"].(string),
  1562  					User:              sf["email"].(string),
  1563  					Bucket:            sf["bucket_name"].(string),
  1564  					SecretKey:         sf["secret_key"].(string),
  1565  					Format:            sf["format"].(string),
  1566  					ResponseCondition: sf["response_condition"].(string),
  1567  				}
  1568  
  1569  				log.Printf("[DEBUG] Create GCS Opts: %#v", opts)
  1570  				_, err := conn.CreateGCS(&opts)
  1571  				if err != nil {
  1572  					return err
  1573  				}
  1574  			}
  1575  		}
  1576  
  1577  		// find difference in Response Object
  1578  		if d.HasChange("response_object") {
  1579  			or, nr := d.GetChange("response_object")
  1580  			if or == nil {
  1581  				or = new(schema.Set)
  1582  			}
  1583  			if nr == nil {
  1584  				nr = new(schema.Set)
  1585  			}
  1586  
  1587  			ors := or.(*schema.Set)
  1588  			nrs := nr.(*schema.Set)
  1589  			removeResponseObject := ors.Difference(nrs).List()
  1590  			addResponseObject := nrs.Difference(ors).List()
  1591  
  1592  			// DELETE old response object configurations
  1593  			for _, rRaw := range removeResponseObject {
  1594  				rf := rRaw.(map[string]interface{})
  1595  				opts := gofastly.DeleteResponseObjectInput{
  1596  					Service: d.Id(),
  1597  					Version: latestVersion,
  1598  					Name:    rf["name"].(string),
  1599  				}
  1600  
  1601  				log.Printf("[DEBUG] Fastly Response Object removal opts: %#v", opts)
  1602  				err := conn.DeleteResponseObject(&opts)
  1603  				if err != nil {
  1604  					return err
  1605  				}
  1606  			}
  1607  
  1608  			// POST new/updated Response Object
  1609  			for _, rRaw := range addResponseObject {
  1610  				rf := rRaw.(map[string]interface{})
  1611  
  1612  				opts := gofastly.CreateResponseObjectInput{
  1613  					Service:          d.Id(),
  1614  					Version:          latestVersion,
  1615  					Name:             rf["name"].(string),
  1616  					Status:           uint(rf["status"].(int)),
  1617  					Response:         rf["response"].(string),
  1618  					Content:          rf["content"].(string),
  1619  					ContentType:      rf["content_type"].(string),
  1620  					RequestCondition: rf["request_condition"].(string),
  1621  					CacheCondition:   rf["cache_condition"].(string),
  1622  				}
  1623  
  1624  				log.Printf("[DEBUG] Create Response Object Opts: %#v", opts)
  1625  				_, err := conn.CreateResponseObject(&opts)
  1626  				if err != nil {
  1627  					return err
  1628  				}
  1629  			}
  1630  		}
  1631  
  1632  		// find difference in request settings
  1633  		if d.HasChange("request_setting") {
  1634  			os, ns := d.GetChange("request_setting")
  1635  			if os == nil {
  1636  				os = new(schema.Set)
  1637  			}
  1638  			if ns == nil {
  1639  				ns = new(schema.Set)
  1640  			}
  1641  
  1642  			ors := os.(*schema.Set)
  1643  			nrs := ns.(*schema.Set)
  1644  			removeRequestSettings := ors.Difference(nrs).List()
  1645  			addRequestSettings := nrs.Difference(ors).List()
  1646  
  1647  			// DELETE old Request Settings configurations
  1648  			for _, sRaw := range removeRequestSettings {
  1649  				sf := sRaw.(map[string]interface{})
  1650  				opts := gofastly.DeleteRequestSettingInput{
  1651  					Service: d.Id(),
  1652  					Version: latestVersion,
  1653  					Name:    sf["name"].(string),
  1654  				}
  1655  
  1656  				log.Printf("[DEBUG] Fastly Request Setting removal opts: %#v", opts)
  1657  				err := conn.DeleteRequestSetting(&opts)
  1658  				if err != nil {
  1659  					return err
  1660  				}
  1661  			}
  1662  
  1663  			// POST new/updated Request Setting
  1664  			for _, sRaw := range addRequestSettings {
  1665  				opts, err := buildRequestSetting(sRaw.(map[string]interface{}))
  1666  				if err != nil {
  1667  					log.Printf("[DEBUG] Error building Requset Setting: %s", err)
  1668  					return err
  1669  				}
  1670  				opts.Service = d.Id()
  1671  				opts.Version = latestVersion
  1672  
  1673  				log.Printf("[DEBUG] Create Request Setting Opts: %#v", opts)
  1674  				_, err = conn.CreateRequestSetting(opts)
  1675  				if err != nil {
  1676  					return err
  1677  				}
  1678  			}
  1679  		}
  1680  
  1681  		// Find differences in VCLs
  1682  		if d.HasChange("vcl") {
  1683  			// Note: as above with Gzip and S3 logging, we don't utilize the PUT
  1684  			// endpoint to update a VCL, we simply destroy it and create a new one.
  1685  			oldVCLVal, newVCLVal := d.GetChange("vcl")
  1686  			if oldVCLVal == nil {
  1687  				oldVCLVal = new(schema.Set)
  1688  			}
  1689  			if newVCLVal == nil {
  1690  				newVCLVal = new(schema.Set)
  1691  			}
  1692  
  1693  			oldVCLSet := oldVCLVal.(*schema.Set)
  1694  			newVCLSet := newVCLVal.(*schema.Set)
  1695  
  1696  			remove := oldVCLSet.Difference(newVCLSet).List()
  1697  			add := newVCLSet.Difference(oldVCLSet).List()
  1698  
  1699  			// Delete removed VCL configurations
  1700  			for _, dRaw := range remove {
  1701  				df := dRaw.(map[string]interface{})
  1702  				opts := gofastly.DeleteVCLInput{
  1703  					Service: d.Id(),
  1704  					Version: latestVersion,
  1705  					Name:    df["name"].(string),
  1706  				}
  1707  
  1708  				log.Printf("[DEBUG] Fastly VCL Removal opts: %#v", opts)
  1709  				err := conn.DeleteVCL(&opts)
  1710  				if err != nil {
  1711  					return err
  1712  				}
  1713  			}
  1714  			// POST new VCL configurations
  1715  			for _, dRaw := range add {
  1716  				df := dRaw.(map[string]interface{})
  1717  				opts := gofastly.CreateVCLInput{
  1718  					Service: d.Id(),
  1719  					Version: latestVersion,
  1720  					Name:    df["name"].(string),
  1721  					Content: df["content"].(string),
  1722  				}
  1723  
  1724  				log.Printf("[DEBUG] Fastly VCL Addition opts: %#v", opts)
  1725  				_, err := conn.CreateVCL(&opts)
  1726  				if err != nil {
  1727  					return err
  1728  				}
  1729  
  1730  				// if this new VCL is the main
  1731  				if df["main"].(bool) {
  1732  					opts := gofastly.ActivateVCLInput{
  1733  						Service: d.Id(),
  1734  						Version: latestVersion,
  1735  						Name:    df["name"].(string),
  1736  					}
  1737  					log.Printf("[DEBUG] Fastly VCL activation opts: %#v", opts)
  1738  					_, err := conn.ActivateVCL(&opts)
  1739  					if err != nil {
  1740  						return err
  1741  					}
  1742  
  1743  				}
  1744  			}
  1745  		}
  1746  
  1747  		// Find differences in Cache Settings
  1748  		if d.HasChange("cache_setting") {
  1749  			oc, nc := d.GetChange("cache_setting")
  1750  			if oc == nil {
  1751  				oc = new(schema.Set)
  1752  			}
  1753  			if nc == nil {
  1754  				nc = new(schema.Set)
  1755  			}
  1756  
  1757  			ocs := oc.(*schema.Set)
  1758  			ncs := nc.(*schema.Set)
  1759  
  1760  			remove := ocs.Difference(ncs).List()
  1761  			add := ncs.Difference(ocs).List()
  1762  
  1763  			// Delete removed Cache Settings
  1764  			for _, dRaw := range remove {
  1765  				df := dRaw.(map[string]interface{})
  1766  				opts := gofastly.DeleteCacheSettingInput{
  1767  					Service: d.Id(),
  1768  					Version: latestVersion,
  1769  					Name:    df["name"].(string),
  1770  				}
  1771  
  1772  				log.Printf("[DEBUG] Fastly Cache Settings removal opts: %#v", opts)
  1773  				err := conn.DeleteCacheSetting(&opts)
  1774  				if err != nil {
  1775  					return err
  1776  				}
  1777  			}
  1778  
  1779  			// POST new Cache Settings
  1780  			for _, dRaw := range add {
  1781  				opts, err := buildCacheSetting(dRaw.(map[string]interface{}))
  1782  				if err != nil {
  1783  					log.Printf("[DEBUG] Error building Cache Setting: %s", err)
  1784  					return err
  1785  				}
  1786  				opts.Service = d.Id()
  1787  				opts.Version = latestVersion
  1788  
  1789  				log.Printf("[DEBUG] Fastly Cache Settings Addition opts: %#v", opts)
  1790  				_, err = conn.CreateCacheSetting(opts)
  1791  				if err != nil {
  1792  					return err
  1793  				}
  1794  			}
  1795  		}
  1796  
  1797  		// validate version
  1798  		log.Printf("[DEBUG] Validating Fastly Service (%s), Version (%v)", d.Id(), latestVersion)
  1799  		valid, msg, err := conn.ValidateVersion(&gofastly.ValidateVersionInput{
  1800  			Service: d.Id(),
  1801  			Version: latestVersion,
  1802  		})
  1803  
  1804  		if err != nil {
  1805  			return fmt.Errorf("[ERR] Error checking validation: %s", err)
  1806  		}
  1807  
  1808  		if !valid {
  1809  			return fmt.Errorf("[ERR] Invalid configuration for Fastly Service (%s): %s", d.Id(), msg)
  1810  		}
  1811  
  1812  		log.Printf("[DEBUG] Activating Fastly Service (%s), Version (%v)", d.Id(), latestVersion)
  1813  		_, err = conn.ActivateVersion(&gofastly.ActivateVersionInput{
  1814  			Service: d.Id(),
  1815  			Version: latestVersion,
  1816  		})
  1817  		if err != nil {
  1818  			return fmt.Errorf("[ERR] Error activating version (%d): %s", latestVersion, err)
  1819  		}
  1820  
  1821  		// Only if the version is valid and activated do we set the active_version.
  1822  		// This prevents us from getting stuck in cloning an invalid version
  1823  		d.Set("active_version", latestVersion)
  1824  	}
  1825  
  1826  	return resourceServiceV1Read(d, meta)
  1827  }
  1828  
  1829  func resourceServiceV1Read(d *schema.ResourceData, meta interface{}) error {
  1830  	conn := meta.(*FastlyClient).conn
  1831  
  1832  	// Find the Service. Discard the service because we need the ServiceDetails,
  1833  	// not just a Service record
  1834  	_, err := findService(d.Id(), meta)
  1835  	if err != nil {
  1836  		switch err {
  1837  		case fastlyNoServiceFoundErr:
  1838  			log.Printf("[WARN] %s for ID (%s)", err, d.Id())
  1839  			d.SetId("")
  1840  			return nil
  1841  		default:
  1842  			return err
  1843  		}
  1844  	}
  1845  
  1846  	s, err := conn.GetServiceDetails(&gofastly.GetServiceInput{
  1847  		ID: d.Id(),
  1848  	})
  1849  
  1850  	if err != nil {
  1851  		return err
  1852  	}
  1853  
  1854  	d.Set("name", s.Name)
  1855  	d.Set("active_version", s.ActiveVersion.Number)
  1856  
  1857  	// If CreateService succeeds, but initial updates to the Service fail, we'll
  1858  	// have an empty ActiveService version (no version is active, so we can't
  1859  	// query for information on it)
  1860  	if s.ActiveVersion.Number != 0 {
  1861  		settingsOpts := gofastly.GetSettingsInput{
  1862  			Service: d.Id(),
  1863  			Version: s.ActiveVersion.Number,
  1864  		}
  1865  		if settings, err := conn.GetSettings(&settingsOpts); err == nil {
  1866  			d.Set("default_host", settings.DefaultHost)
  1867  			d.Set("default_ttl", settings.DefaultTTL)
  1868  		} else {
  1869  			return fmt.Errorf("[ERR] Error looking up Version settings for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  1870  		}
  1871  
  1872  		// TODO: update go-fastly to support an ActiveVersion struct, which contains
  1873  		// domain and backend info in the response. Here we do 2 additional queries
  1874  		// to find out that info
  1875  		log.Printf("[DEBUG] Refreshing Domains for (%s)", d.Id())
  1876  		domainList, err := conn.ListDomains(&gofastly.ListDomainsInput{
  1877  			Service: d.Id(),
  1878  			Version: s.ActiveVersion.Number,
  1879  		})
  1880  
  1881  		if err != nil {
  1882  			return fmt.Errorf("[ERR] Error looking up Domains for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  1883  		}
  1884  
  1885  		// Refresh Domains
  1886  		dl := flattenDomains(domainList)
  1887  
  1888  		if err := d.Set("domain", dl); err != nil {
  1889  			log.Printf("[WARN] Error setting Domains for (%s): %s", d.Id(), err)
  1890  		}
  1891  
  1892  		// Refresh Backends
  1893  		log.Printf("[DEBUG] Refreshing Backends for (%s)", d.Id())
  1894  		backendList, err := conn.ListBackends(&gofastly.ListBackendsInput{
  1895  			Service: d.Id(),
  1896  			Version: s.ActiveVersion.Number,
  1897  		})
  1898  
  1899  		if err != nil {
  1900  			return fmt.Errorf("[ERR] Error looking up Backends for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  1901  		}
  1902  
  1903  		bl := flattenBackends(backendList)
  1904  
  1905  		if err := d.Set("backend", bl); err != nil {
  1906  			log.Printf("[WARN] Error setting Backends for (%s): %s", d.Id(), err)
  1907  		}
  1908  
  1909  		// refresh headers
  1910  		log.Printf("[DEBUG] Refreshing Headers for (%s)", d.Id())
  1911  		headerList, err := conn.ListHeaders(&gofastly.ListHeadersInput{
  1912  			Service: d.Id(),
  1913  			Version: s.ActiveVersion.Number,
  1914  		})
  1915  
  1916  		if err != nil {
  1917  			return fmt.Errorf("[ERR] Error looking up Headers for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  1918  		}
  1919  
  1920  		hl := flattenHeaders(headerList)
  1921  
  1922  		if err := d.Set("header", hl); err != nil {
  1923  			log.Printf("[WARN] Error setting Headers for (%s): %s", d.Id(), err)
  1924  		}
  1925  
  1926  		// refresh gzips
  1927  		log.Printf("[DEBUG] Refreshing Gzips for (%s)", d.Id())
  1928  		gzipsList, err := conn.ListGzips(&gofastly.ListGzipsInput{
  1929  			Service: d.Id(),
  1930  			Version: s.ActiveVersion.Number,
  1931  		})
  1932  
  1933  		if err != nil {
  1934  			return fmt.Errorf("[ERR] Error looking up Gzips for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  1935  		}
  1936  
  1937  		gl := flattenGzips(gzipsList)
  1938  
  1939  		if err := d.Set("gzip", gl); err != nil {
  1940  			log.Printf("[WARN] Error setting Gzips for (%s): %s", d.Id(), err)
  1941  		}
  1942  
  1943  		// refresh Healthcheck
  1944  		log.Printf("[DEBUG] Refreshing Healthcheck for (%s)", d.Id())
  1945  		healthcheckList, err := conn.ListHealthChecks(&gofastly.ListHealthChecksInput{
  1946  			Service: d.Id(),
  1947  			Version: s.ActiveVersion.Number,
  1948  		})
  1949  
  1950  		if err != nil {
  1951  			return fmt.Errorf("[ERR] Error looking up Healthcheck for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  1952  		}
  1953  
  1954  		hcl := flattenHealthchecks(healthcheckList)
  1955  
  1956  		if err := d.Set("healthcheck", hcl); err != nil {
  1957  			log.Printf("[WARN] Error setting Healthcheck for (%s): %s", d.Id(), err)
  1958  		}
  1959  
  1960  		// refresh S3 Logging
  1961  		log.Printf("[DEBUG] Refreshing S3 Logging for (%s)", d.Id())
  1962  		s3List, err := conn.ListS3s(&gofastly.ListS3sInput{
  1963  			Service: d.Id(),
  1964  			Version: s.ActiveVersion.Number,
  1965  		})
  1966  
  1967  		if err != nil {
  1968  			return fmt.Errorf("[ERR] Error looking up S3 Logging for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  1969  		}
  1970  
  1971  		sl := flattenS3s(s3List)
  1972  
  1973  		if err := d.Set("s3logging", sl); err != nil {
  1974  			log.Printf("[WARN] Error setting S3 Logging for (%s): %s", d.Id(), err)
  1975  		}
  1976  
  1977  		// refresh Papertrail Logging
  1978  		log.Printf("[DEBUG] Refreshing Papertrail for (%s)", d.Id())
  1979  		papertrailList, err := conn.ListPapertrails(&gofastly.ListPapertrailsInput{
  1980  			Service: d.Id(),
  1981  			Version: s.ActiveVersion.Number,
  1982  		})
  1983  
  1984  		if err != nil {
  1985  			return fmt.Errorf("[ERR] Error looking up Papertrail for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  1986  		}
  1987  
  1988  		pl := flattenPapertrails(papertrailList)
  1989  
  1990  		if err := d.Set("papertrail", pl); err != nil {
  1991  			log.Printf("[WARN] Error setting Papertrail for (%s): %s", d.Id(), err)
  1992  		}
  1993  
  1994  		// refresh Sumologic Logging
  1995  		log.Printf("[DEBUG] Refreshing Sumologic for (%s)", d.Id())
  1996  		sumologicList, err := conn.ListSumologics(&gofastly.ListSumologicsInput{
  1997  			Service: d.Id(),
  1998  			Version: s.ActiveVersion.Number,
  1999  		})
  2000  
  2001  		if err != nil {
  2002  			return fmt.Errorf("[ERR] Error looking up Sumologic for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  2003  		}
  2004  
  2005  		sul := flattenSumologics(sumologicList)
  2006  		if err := d.Set("sumologic", sul); err != nil {
  2007  			log.Printf("[WARN] Error setting Sumologic for (%s): %s", d.Id(), err)
  2008  		}
  2009  
  2010  		// refresh GCS Logging
  2011  		log.Printf("[DEBUG] Refreshing GCS for (%s)", d.Id())
  2012  		GCSList, err := conn.ListGCSs(&gofastly.ListGCSsInput{
  2013  			Service: d.Id(),
  2014  			Version: s.ActiveVersion.Number,
  2015  		})
  2016  
  2017  		if err != nil {
  2018  			return fmt.Errorf("[ERR] Error looking up GCS for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  2019  		}
  2020  
  2021  		gcsl := flattenGCS(GCSList)
  2022  		if err := d.Set("gcs", gcsl); err != nil {
  2023  			log.Printf("[WARN] Error setting gcs for (%s): %s", d.Id(), err)
  2024  		}
  2025  
  2026  		// refresh Response Objects
  2027  		log.Printf("[DEBUG] Refreshing Response Object for (%s)", d.Id())
  2028  		responseObjectList, err := conn.ListResponseObjects(&gofastly.ListResponseObjectsInput{
  2029  			Service: d.Id(),
  2030  			Version: s.ActiveVersion.Number,
  2031  		})
  2032  
  2033  		if err != nil {
  2034  			return fmt.Errorf("[ERR] Error looking up Response Object for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  2035  		}
  2036  
  2037  		rol := flattenResponseObjects(responseObjectList)
  2038  
  2039  		if err := d.Set("response_object", rol); err != nil {
  2040  			log.Printf("[WARN] Error setting Response Object for (%s): %s", d.Id(), err)
  2041  		}
  2042  
  2043  		// refresh Conditions
  2044  		log.Printf("[DEBUG] Refreshing Conditions for (%s)", d.Id())
  2045  		conditionList, err := conn.ListConditions(&gofastly.ListConditionsInput{
  2046  			Service: d.Id(),
  2047  			Version: s.ActiveVersion.Number,
  2048  		})
  2049  
  2050  		if err != nil {
  2051  			return fmt.Errorf("[ERR] Error looking up Conditions for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  2052  		}
  2053  
  2054  		cl := flattenConditions(conditionList)
  2055  
  2056  		if err := d.Set("condition", cl); err != nil {
  2057  			log.Printf("[WARN] Error setting Conditions for (%s): %s", d.Id(), err)
  2058  		}
  2059  
  2060  		// refresh Request Settings
  2061  		log.Printf("[DEBUG] Refreshing Request Settings for (%s)", d.Id())
  2062  		rsList, err := conn.ListRequestSettings(&gofastly.ListRequestSettingsInput{
  2063  			Service: d.Id(),
  2064  			Version: s.ActiveVersion.Number,
  2065  		})
  2066  
  2067  		if err != nil {
  2068  			return fmt.Errorf("[ERR] Error looking up Request Settings for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  2069  		}
  2070  
  2071  		rl := flattenRequestSettings(rsList)
  2072  
  2073  		if err := d.Set("request_setting", rl); err != nil {
  2074  			log.Printf("[WARN] Error setting Request Settings for (%s): %s", d.Id(), err)
  2075  		}
  2076  
  2077  		// refresh VCLs
  2078  		log.Printf("[DEBUG] Refreshing VCLs for (%s)", d.Id())
  2079  		vclList, err := conn.ListVCLs(&gofastly.ListVCLsInput{
  2080  			Service: d.Id(),
  2081  			Version: s.ActiveVersion.Number,
  2082  		})
  2083  		if err != nil {
  2084  			return fmt.Errorf("[ERR] Error looking up VCLs for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  2085  		}
  2086  
  2087  		vl := flattenVCLs(vclList)
  2088  
  2089  		if err := d.Set("vcl", vl); err != nil {
  2090  			log.Printf("[WARN] Error setting VCLs for (%s): %s", d.Id(), err)
  2091  		}
  2092  
  2093  		// refresh Cache Settings
  2094  		log.Printf("[DEBUG] Refreshing Cache Settings for (%s)", d.Id())
  2095  		cslList, err := conn.ListCacheSettings(&gofastly.ListCacheSettingsInput{
  2096  			Service: d.Id(),
  2097  			Version: s.ActiveVersion.Number,
  2098  		})
  2099  		if err != nil {
  2100  			return fmt.Errorf("[ERR] Error looking up Cache Settings for (%s), version (%v): %s", d.Id(), s.ActiveVersion.Number, err)
  2101  		}
  2102  
  2103  		csl := flattenCacheSettings(cslList)
  2104  
  2105  		if err := d.Set("cache_setting", csl); err != nil {
  2106  			log.Printf("[WARN] Error setting Cache Settings for (%s): %s", d.Id(), err)
  2107  		}
  2108  
  2109  	} else {
  2110  		log.Printf("[DEBUG] Active Version for Service (%s) is empty, no state to refresh", d.Id())
  2111  	}
  2112  
  2113  	return nil
  2114  }
  2115  
  2116  func resourceServiceV1Delete(d *schema.ResourceData, meta interface{}) error {
  2117  	conn := meta.(*FastlyClient).conn
  2118  
  2119  	// Fastly will fail to delete any service with an Active Version.
  2120  	// If `force_destroy` is given, we deactivate the active version and then send
  2121  	// the DELETE call
  2122  	if d.Get("force_destroy").(bool) {
  2123  		s, err := conn.GetServiceDetails(&gofastly.GetServiceInput{
  2124  			ID: d.Id(),
  2125  		})
  2126  
  2127  		if err != nil {
  2128  			return err
  2129  		}
  2130  
  2131  		if s.ActiveVersion.Number != 0 {
  2132  			_, err := conn.DeactivateVersion(&gofastly.DeactivateVersionInput{
  2133  				Service: d.Id(),
  2134  				Version: s.ActiveVersion.Number,
  2135  			})
  2136  			if err != nil {
  2137  				return err
  2138  			}
  2139  		}
  2140  	}
  2141  
  2142  	err := conn.DeleteService(&gofastly.DeleteServiceInput{
  2143  		ID: d.Id(),
  2144  	})
  2145  
  2146  	if err != nil {
  2147  		return err
  2148  	}
  2149  
  2150  	_, err = findService(d.Id(), meta)
  2151  	if err != nil {
  2152  		switch err {
  2153  		// we expect no records to be found here
  2154  		case fastlyNoServiceFoundErr:
  2155  			d.SetId("")
  2156  			return nil
  2157  		default:
  2158  			return err
  2159  		}
  2160  	}
  2161  
  2162  	// findService above returned something and nil error, but shouldn't have
  2163  	return fmt.Errorf("[WARN] Tried deleting Service (%s), but was still found", d.Id())
  2164  
  2165  }
  2166  
  2167  func flattenDomains(list []*gofastly.Domain) []map[string]interface{} {
  2168  	dl := make([]map[string]interface{}, 0, len(list))
  2169  
  2170  	for _, d := range list {
  2171  		dl = append(dl, map[string]interface{}{
  2172  			"name":    d.Name,
  2173  			"comment": d.Comment,
  2174  		})
  2175  	}
  2176  
  2177  	return dl
  2178  }
  2179  
  2180  func flattenBackends(backendList []*gofastly.Backend) []map[string]interface{} {
  2181  	var bl []map[string]interface{}
  2182  	for _, b := range backendList {
  2183  		// Convert Backend to a map for saving to state.
  2184  		nb := map[string]interface{}{
  2185  			"name":                  b.Name,
  2186  			"address":               b.Address,
  2187  			"auto_loadbalance":      b.AutoLoadbalance,
  2188  			"between_bytes_timeout": int(b.BetweenBytesTimeout),
  2189  			"connect_timeout":       int(b.ConnectTimeout),
  2190  			"error_threshold":       int(b.ErrorThreshold),
  2191  			"first_byte_timeout":    int(b.FirstByteTimeout),
  2192  			"max_conn":              int(b.MaxConn),
  2193  			"port":                  int(b.Port),
  2194  			"shield":                b.Shield,
  2195  			"ssl_check_cert":        b.SSLCheckCert,
  2196  			"ssl_hostname":          b.SSLHostname,
  2197  			"ssl_cert_hostname":     b.SSLCertHostname,
  2198  			"ssl_sni_hostname":      b.SSLSNIHostname,
  2199  			"weight":                int(b.Weight),
  2200  			"request_condition":     b.RequestCondition,
  2201  			"healthcheck":           b.HealthCheck,
  2202  		}
  2203  
  2204  		bl = append(bl, nb)
  2205  	}
  2206  	return bl
  2207  }
  2208  
  2209  // findService finds a Fastly Service via the ListServices endpoint, returning
  2210  // the Service if found.
  2211  //
  2212  // Fastly API does not include any "deleted_at" type parameter to indicate
  2213  // that a Service has been deleted. GET requests to a deleted Service will
  2214  // return 200 OK and have the full output of the Service for an unknown time
  2215  // (days, in my testing). In order to determine if a Service is deleted, we
  2216  // need to hit /service and loop the returned Services, searching for the one
  2217  // in question. This endpoint only returns active or "alive" services. If the
  2218  // Service is not included, then it's "gone"
  2219  //
  2220  // Returns a fastlyNoServiceFoundErr error if the Service is not found in the
  2221  // ListServices response.
  2222  func findService(id string, meta interface{}) (*gofastly.Service, error) {
  2223  	conn := meta.(*FastlyClient).conn
  2224  
  2225  	l, err := conn.ListServices(&gofastly.ListServicesInput{})
  2226  	if err != nil {
  2227  		return nil, fmt.Errorf("[WARN] Error listing services (%s): %s", id, err)
  2228  	}
  2229  
  2230  	for _, s := range l {
  2231  		if s.ID == id {
  2232  			log.Printf("[DEBUG] Found Service (%s)", id)
  2233  			return s, nil
  2234  		}
  2235  	}
  2236  
  2237  	return nil, fastlyNoServiceFoundErr
  2238  }
  2239  
  2240  func flattenHeaders(headerList []*gofastly.Header) []map[string]interface{} {
  2241  	var hl []map[string]interface{}
  2242  	for _, h := range headerList {
  2243  		// Convert Header to a map for saving to state.
  2244  		nh := map[string]interface{}{
  2245  			"name":               h.Name,
  2246  			"action":             h.Action,
  2247  			"ignore_if_set":      h.IgnoreIfSet,
  2248  			"type":               h.Type,
  2249  			"destination":        h.Destination,
  2250  			"source":             h.Source,
  2251  			"regex":              h.Regex,
  2252  			"substitution":       h.Substitution,
  2253  			"priority":           int(h.Priority),
  2254  			"request_condition":  h.RequestCondition,
  2255  			"cache_condition":    h.CacheCondition,
  2256  			"response_condition": h.ResponseCondition,
  2257  		}
  2258  
  2259  		for k, v := range nh {
  2260  			if v == "" {
  2261  				delete(nh, k)
  2262  			}
  2263  		}
  2264  
  2265  		hl = append(hl, nh)
  2266  	}
  2267  	return hl
  2268  }
  2269  
  2270  func buildHeader(headerMap interface{}) (*gofastly.CreateHeaderInput, error) {
  2271  	df := headerMap.(map[string]interface{})
  2272  	opts := gofastly.CreateHeaderInput{
  2273  		Name:              df["name"].(string),
  2274  		IgnoreIfSet:       gofastly.CBool(df["ignore_if_set"].(bool)),
  2275  		Destination:       df["destination"].(string),
  2276  		Priority:          uint(df["priority"].(int)),
  2277  		Source:            df["source"].(string),
  2278  		Regex:             df["regex"].(string),
  2279  		Substitution:      df["substitution"].(string),
  2280  		RequestCondition:  df["request_condition"].(string),
  2281  		CacheCondition:    df["cache_condition"].(string),
  2282  		ResponseCondition: df["response_condition"].(string),
  2283  	}
  2284  
  2285  	act := strings.ToLower(df["action"].(string))
  2286  	switch act {
  2287  	case "set":
  2288  		opts.Action = gofastly.HeaderActionSet
  2289  	case "append":
  2290  		opts.Action = gofastly.HeaderActionAppend
  2291  	case "delete":
  2292  		opts.Action = gofastly.HeaderActionDelete
  2293  	case "regex":
  2294  		opts.Action = gofastly.HeaderActionRegex
  2295  	case "regex_repeat":
  2296  		opts.Action = gofastly.HeaderActionRegexRepeat
  2297  	}
  2298  
  2299  	ty := strings.ToLower(df["type"].(string))
  2300  	switch ty {
  2301  	case "request":
  2302  		opts.Type = gofastly.HeaderTypeRequest
  2303  	case "fetch":
  2304  		opts.Type = gofastly.HeaderTypeFetch
  2305  	case "cache":
  2306  		opts.Type = gofastly.HeaderTypeCache
  2307  	case "response":
  2308  		opts.Type = gofastly.HeaderTypeResponse
  2309  	}
  2310  
  2311  	return &opts, nil
  2312  }
  2313  
  2314  func buildCacheSetting(cacheMap interface{}) (*gofastly.CreateCacheSettingInput, error) {
  2315  	df := cacheMap.(map[string]interface{})
  2316  	opts := gofastly.CreateCacheSettingInput{
  2317  		Name:           df["name"].(string),
  2318  		StaleTTL:       uint(df["stale_ttl"].(int)),
  2319  		CacheCondition: df["cache_condition"].(string),
  2320  	}
  2321  
  2322  	if v, ok := df["ttl"]; ok {
  2323  		opts.TTL = uint(v.(int))
  2324  	}
  2325  
  2326  	act := strings.ToLower(df["action"].(string))
  2327  	switch act {
  2328  	case "cache":
  2329  		opts.Action = gofastly.CacheSettingActionCache
  2330  	case "pass":
  2331  		opts.Action = gofastly.CacheSettingActionPass
  2332  	case "restart":
  2333  		opts.Action = gofastly.CacheSettingActionRestart
  2334  	}
  2335  
  2336  	return &opts, nil
  2337  }
  2338  
  2339  func flattenGzips(gzipsList []*gofastly.Gzip) []map[string]interface{} {
  2340  	var gl []map[string]interface{}
  2341  	for _, g := range gzipsList {
  2342  		// Convert Gzip to a map for saving to state.
  2343  		ng := map[string]interface{}{
  2344  			"name":            g.Name,
  2345  			"cache_condition": g.CacheCondition,
  2346  		}
  2347  
  2348  		if g.Extensions != "" {
  2349  			e := strings.Split(g.Extensions, " ")
  2350  			var et []interface{}
  2351  			for _, ev := range e {
  2352  				et = append(et, ev)
  2353  			}
  2354  			ng["extensions"] = schema.NewSet(schema.HashString, et)
  2355  		}
  2356  
  2357  		if g.ContentTypes != "" {
  2358  			c := strings.Split(g.ContentTypes, " ")
  2359  			var ct []interface{}
  2360  			for _, cv := range c {
  2361  				ct = append(ct, cv)
  2362  			}
  2363  			ng["content_types"] = schema.NewSet(schema.HashString, ct)
  2364  		}
  2365  
  2366  		// prune any empty values that come from the default string value in structs
  2367  		for k, v := range ng {
  2368  			if v == "" {
  2369  				delete(ng, k)
  2370  			}
  2371  		}
  2372  
  2373  		gl = append(gl, ng)
  2374  	}
  2375  
  2376  	return gl
  2377  }
  2378  
  2379  func flattenHealthchecks(healthcheckList []*gofastly.HealthCheck) []map[string]interface{} {
  2380  	var hl []map[string]interface{}
  2381  	for _, h := range healthcheckList {
  2382  		// Convert HealthChecks to a map for saving to state.
  2383  		nh := map[string]interface{}{
  2384  			"name":              h.Name,
  2385  			"host":              h.Host,
  2386  			"path":              h.Path,
  2387  			"check_interval":    h.CheckInterval,
  2388  			"expected_response": h.ExpectedResponse,
  2389  			"http_version":      h.HTTPVersion,
  2390  			"initial":           h.Initial,
  2391  			"method":            h.Method,
  2392  			"threshold":         h.Threshold,
  2393  			"timeout":           h.Timeout,
  2394  			"window":            h.Window,
  2395  		}
  2396  
  2397  		// prune any empty values that come from the default string value in structs
  2398  		for k, v := range nh {
  2399  			if v == "" {
  2400  				delete(nh, k)
  2401  			}
  2402  		}
  2403  
  2404  		hl = append(hl, nh)
  2405  	}
  2406  
  2407  	return hl
  2408  }
  2409  
  2410  func flattenS3s(s3List []*gofastly.S3) []map[string]interface{} {
  2411  	var sl []map[string]interface{}
  2412  	for _, s := range s3List {
  2413  		// Convert S3s to a map for saving to state.
  2414  		ns := map[string]interface{}{
  2415  			"name":               s.Name,
  2416  			"bucket_name":        s.BucketName,
  2417  			"s3_access_key":      s.AccessKey,
  2418  			"s3_secret_key":      s.SecretKey,
  2419  			"path":               s.Path,
  2420  			"period":             s.Period,
  2421  			"domain":             s.Domain,
  2422  			"gzip_level":         s.GzipLevel,
  2423  			"format":             s.Format,
  2424  			"format_version":     s.FormatVersion,
  2425  			"timestamp_format":   s.TimestampFormat,
  2426  			"response_condition": s.ResponseCondition,
  2427  		}
  2428  
  2429  		// prune any empty values that come from the default string value in structs
  2430  		for k, v := range ns {
  2431  			if v == "" {
  2432  				delete(ns, k)
  2433  			}
  2434  		}
  2435  
  2436  		sl = append(sl, ns)
  2437  	}
  2438  
  2439  	return sl
  2440  }
  2441  
  2442  func flattenPapertrails(papertrailList []*gofastly.Papertrail) []map[string]interface{} {
  2443  	var pl []map[string]interface{}
  2444  	for _, p := range papertrailList {
  2445  		// Convert Papertrails to a map for saving to state.
  2446  		ns := map[string]interface{}{
  2447  			"name":               p.Name,
  2448  			"address":            p.Address,
  2449  			"port":               p.Port,
  2450  			"format":             p.Format,
  2451  			"response_condition": p.ResponseCondition,
  2452  		}
  2453  
  2454  		// prune any empty values that come from the default string value in structs
  2455  		for k, v := range ns {
  2456  			if v == "" {
  2457  				delete(ns, k)
  2458  			}
  2459  		}
  2460  
  2461  		pl = append(pl, ns)
  2462  	}
  2463  
  2464  	return pl
  2465  }
  2466  
  2467  func flattenSumologics(sumologicList []*gofastly.Sumologic) []map[string]interface{} {
  2468  	var l []map[string]interface{}
  2469  	for _, p := range sumologicList {
  2470  		// Convert Sumologic to a map for saving to state.
  2471  		ns := map[string]interface{}{
  2472  			"name":               p.Name,
  2473  			"url":                p.URL,
  2474  			"format":             p.Format,
  2475  			"response_condition": p.ResponseCondition,
  2476  			"message_type":       p.MessageType,
  2477  			"format_version":     int(p.FormatVersion),
  2478  		}
  2479  
  2480  		// prune any empty values that come from the default string value in structs
  2481  		for k, v := range ns {
  2482  			if v == "" {
  2483  				delete(ns, k)
  2484  			}
  2485  		}
  2486  
  2487  		l = append(l, ns)
  2488  	}
  2489  
  2490  	return l
  2491  }
  2492  
  2493  func flattenGCS(gcsList []*gofastly.GCS) []map[string]interface{} {
  2494  	var GCSList []map[string]interface{}
  2495  	for _, currentGCS := range gcsList {
  2496  		// Convert gcs to a map for saving to state.
  2497  		GCSMapString := map[string]interface{}{
  2498  			"name":               currentGCS.Name,
  2499  			"email":              currentGCS.User,
  2500  			"bucket_name":        currentGCS.Bucket,
  2501  			"secret_key":         currentGCS.SecretKey,
  2502  			"path":               currentGCS.Path,
  2503  			"period":             int(currentGCS.Period),
  2504  			"gzip_level":         int(currentGCS.GzipLevel),
  2505  			"response_condition": currentGCS.ResponseCondition,
  2506  			"format":             currentGCS.Format,
  2507  		}
  2508  
  2509  		// prune any empty values that come from the default string value in structs
  2510  		for k, v := range GCSMapString {
  2511  			if v == "" {
  2512  				delete(GCSMapString, k)
  2513  			}
  2514  		}
  2515  
  2516  		GCSList = append(GCSList, GCSMapString)
  2517  	}
  2518  
  2519  	return GCSList
  2520  }
  2521  
  2522  func flattenResponseObjects(responseObjectList []*gofastly.ResponseObject) []map[string]interface{} {
  2523  	var rol []map[string]interface{}
  2524  	for _, ro := range responseObjectList {
  2525  		// Convert ResponseObjects to a map for saving to state.
  2526  		nro := map[string]interface{}{
  2527  			"name":              ro.Name,
  2528  			"status":            ro.Status,
  2529  			"response":          ro.Response,
  2530  			"content":           ro.Content,
  2531  			"content_type":      ro.ContentType,
  2532  			"request_condition": ro.RequestCondition,
  2533  			"cache_condition":   ro.CacheCondition,
  2534  		}
  2535  
  2536  		// prune any empty values that come from the default string value in structs
  2537  		for k, v := range nro {
  2538  			if v == "" {
  2539  				delete(nro, k)
  2540  			}
  2541  		}
  2542  
  2543  		rol = append(rol, nro)
  2544  	}
  2545  
  2546  	return rol
  2547  }
  2548  
  2549  func flattenConditions(conditionList []*gofastly.Condition) []map[string]interface{} {
  2550  	var cl []map[string]interface{}
  2551  	for _, c := range conditionList {
  2552  		// Convert Conditions to a map for saving to state.
  2553  		nc := map[string]interface{}{
  2554  			"name":      c.Name,
  2555  			"statement": c.Statement,
  2556  			"type":      c.Type,
  2557  			"priority":  c.Priority,
  2558  		}
  2559  
  2560  		// prune any empty values that come from the default string value in structs
  2561  		for k, v := range nc {
  2562  			if v == "" {
  2563  				delete(nc, k)
  2564  			}
  2565  		}
  2566  
  2567  		cl = append(cl, nc)
  2568  	}
  2569  
  2570  	return cl
  2571  }
  2572  
  2573  func flattenRequestSettings(rsList []*gofastly.RequestSetting) []map[string]interface{} {
  2574  	var rl []map[string]interface{}
  2575  	for _, r := range rsList {
  2576  		// Convert Request Settings to a map for saving to state.
  2577  		nrs := map[string]interface{}{
  2578  			"name":              r.Name,
  2579  			"max_stale_age":     r.MaxStaleAge,
  2580  			"force_miss":        r.ForceMiss,
  2581  			"force_ssl":         r.ForceSSL,
  2582  			"action":            r.Action,
  2583  			"bypass_busy_wait":  r.BypassBusyWait,
  2584  			"hash_keys":         r.HashKeys,
  2585  			"xff":               r.XForwardedFor,
  2586  			"timer_support":     r.TimerSupport,
  2587  			"geo_headers":       r.GeoHeaders,
  2588  			"default_host":      r.DefaultHost,
  2589  			"request_condition": r.RequestCondition,
  2590  		}
  2591  
  2592  		// prune any empty values that come from the default string value in structs
  2593  		for k, v := range nrs {
  2594  			if v == "" {
  2595  				delete(nrs, k)
  2596  			}
  2597  		}
  2598  
  2599  		rl = append(rl, nrs)
  2600  	}
  2601  
  2602  	return rl
  2603  }
  2604  
  2605  func buildRequestSetting(requestSettingMap interface{}) (*gofastly.CreateRequestSettingInput, error) {
  2606  	df := requestSettingMap.(map[string]interface{})
  2607  	opts := gofastly.CreateRequestSettingInput{
  2608  		Name:             df["name"].(string),
  2609  		MaxStaleAge:      uint(df["max_stale_age"].(int)),
  2610  		ForceMiss:        gofastly.CBool(df["force_miss"].(bool)),
  2611  		ForceSSL:         gofastly.CBool(df["force_ssl"].(bool)),
  2612  		BypassBusyWait:   gofastly.CBool(df["bypass_busy_wait"].(bool)),
  2613  		HashKeys:         df["hash_keys"].(string),
  2614  		TimerSupport:     gofastly.CBool(df["timer_support"].(bool)),
  2615  		GeoHeaders:       gofastly.CBool(df["geo_headers"].(bool)),
  2616  		DefaultHost:      df["default_host"].(string),
  2617  		RequestCondition: df["request_condition"].(string),
  2618  	}
  2619  
  2620  	act := strings.ToLower(df["action"].(string))
  2621  	switch act {
  2622  	case "lookup":
  2623  		opts.Action = gofastly.RequestSettingActionLookup
  2624  	case "pass":
  2625  		opts.Action = gofastly.RequestSettingActionPass
  2626  	}
  2627  
  2628  	xff := strings.ToLower(df["xff"].(string))
  2629  	switch xff {
  2630  	case "clear":
  2631  		opts.XForwardedFor = gofastly.RequestSettingXFFClear
  2632  	case "leave":
  2633  		opts.XForwardedFor = gofastly.RequestSettingXFFLeave
  2634  	case "append":
  2635  		opts.XForwardedFor = gofastly.RequestSettingXFFAppend
  2636  	case "append_all":
  2637  		opts.XForwardedFor = gofastly.RequestSettingXFFAppendAll
  2638  	case "overwrite":
  2639  		opts.XForwardedFor = gofastly.RequestSettingXFFOverwrite
  2640  	}
  2641  
  2642  	return &opts, nil
  2643  }
  2644  
  2645  func flattenCacheSettings(csList []*gofastly.CacheSetting) []map[string]interface{} {
  2646  	var csl []map[string]interface{}
  2647  	for _, cl := range csList {
  2648  		// Convert Cache Settings to a map for saving to state.
  2649  		clMap := map[string]interface{}{
  2650  			"name":            cl.Name,
  2651  			"action":          cl.Action,
  2652  			"cache_condition": cl.CacheCondition,
  2653  			"stale_ttl":       cl.StaleTTL,
  2654  			"ttl":             cl.TTL,
  2655  		}
  2656  
  2657  		// prune any empty values that come from the default string value in structs
  2658  		for k, v := range clMap {
  2659  			if v == "" {
  2660  				delete(clMap, k)
  2661  			}
  2662  		}
  2663  
  2664  		csl = append(csl, clMap)
  2665  	}
  2666  
  2667  	return csl
  2668  }
  2669  
  2670  func flattenVCLs(vclList []*gofastly.VCL) []map[string]interface{} {
  2671  	var vl []map[string]interface{}
  2672  	for _, vcl := range vclList {
  2673  		// Convert VCLs to a map for saving to state.
  2674  		vclMap := map[string]interface{}{
  2675  			"name":    vcl.Name,
  2676  			"content": vcl.Content,
  2677  			"main":    vcl.Main,
  2678  		}
  2679  
  2680  		// prune any empty values that come from the default string value in structs
  2681  		for k, v := range vclMap {
  2682  			if v == "" {
  2683  				delete(vclMap, k)
  2684  			}
  2685  		}
  2686  
  2687  		vl = append(vl, vclMap)
  2688  	}
  2689  
  2690  	return vl
  2691  }
  2692  
  2693  func validateVCLs(d *schema.ResourceData) error {
  2694  	// TODO: this would be nice to move into a resource/collection validation function, once that is available
  2695  	// (see https://github.com/hashicorp/terraform/pull/4348 and https://github.com/hashicorp/terraform/pull/6508)
  2696  	vcls, exists := d.GetOk("vcl")
  2697  	if !exists {
  2698  		return nil
  2699  	}
  2700  
  2701  	numberOfMainVCLs, numberOfIncludeVCLs := 0, 0
  2702  	for _, vclElem := range vcls.(*schema.Set).List() {
  2703  		vcl := vclElem.(map[string]interface{})
  2704  		if mainVal, hasMain := vcl["main"]; hasMain && mainVal.(bool) {
  2705  			numberOfMainVCLs++
  2706  		} else {
  2707  			numberOfIncludeVCLs++
  2708  		}
  2709  	}
  2710  	if numberOfMainVCLs == 0 && numberOfIncludeVCLs > 0 {
  2711  		return errors.New("if you include VCL configurations, one of them should have main = true")
  2712  	}
  2713  	if numberOfMainVCLs > 1 {
  2714  		return errors.New("you cannot have more than one VCL configuration with main = true")
  2715  	}
  2716  	return nil
  2717  }