github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/aws/resource_aws_cloudfront_distribution.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"time"
     7  
     8  	"github.com/aws/aws-sdk-go/aws"
     9  	"github.com/aws/aws-sdk-go/aws/awserr"
    10  	"github.com/aws/aws-sdk-go/service/cloudfront"
    11  	"github.com/hashicorp/errwrap"
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  	"github.com/hashicorp/terraform/helper/validation"
    15  )
    16  
    17  func resourceAwsCloudFrontDistribution() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsCloudFrontDistributionCreate,
    20  		Read:   resourceAwsCloudFrontDistributionRead,
    21  		Update: resourceAwsCloudFrontDistributionUpdate,
    22  		Delete: resourceAwsCloudFrontDistributionDelete,
    23  		Importer: &schema.ResourceImporter{
    24  			State: resourceAwsCloudFrontDistributionImport,
    25  		},
    26  
    27  		Schema: map[string]*schema.Schema{
    28  			"arn": {
    29  				Type:     schema.TypeString,
    30  				Computed: true,
    31  			},
    32  			"aliases": {
    33  				Type:     schema.TypeSet,
    34  				Optional: true,
    35  				Elem:     &schema.Schema{Type: schema.TypeString},
    36  				Set:      aliasesHash,
    37  			},
    38  			"cache_behavior": {
    39  				Type:     schema.TypeSet,
    40  				Optional: true,
    41  				Set:      cacheBehaviorHash,
    42  				Elem: &schema.Resource{
    43  					Schema: map[string]*schema.Schema{
    44  						"allowed_methods": {
    45  							Type:     schema.TypeList,
    46  							Required: true,
    47  							Elem:     &schema.Schema{Type: schema.TypeString},
    48  						},
    49  						"cached_methods": {
    50  							Type:     schema.TypeList,
    51  							Required: true,
    52  							Elem:     &schema.Schema{Type: schema.TypeString},
    53  						},
    54  						"compress": {
    55  							Type:     schema.TypeBool,
    56  							Optional: true,
    57  							Default:  false,
    58  						},
    59  						"default_ttl": {
    60  							Type:     schema.TypeInt,
    61  							Required: true,
    62  						},
    63  						"forwarded_values": {
    64  							Type:     schema.TypeSet,
    65  							Required: true,
    66  							Set:      forwardedValuesHash,
    67  							MaxItems: 1,
    68  							Elem: &schema.Resource{
    69  								Schema: map[string]*schema.Schema{
    70  									"cookies": {
    71  										Type:     schema.TypeSet,
    72  										Required: true,
    73  										Set:      cookiePreferenceHash,
    74  										MaxItems: 1,
    75  										Elem: &schema.Resource{
    76  											Schema: map[string]*schema.Schema{
    77  												"forward": {
    78  													Type:     schema.TypeString,
    79  													Required: true,
    80  												},
    81  												"whitelisted_names": {
    82  													Type:     schema.TypeList,
    83  													Optional: true,
    84  													Elem:     &schema.Schema{Type: schema.TypeString},
    85  												},
    86  											},
    87  										},
    88  									},
    89  									"headers": {
    90  										Type:     schema.TypeList,
    91  										Optional: true,
    92  										Elem:     &schema.Schema{Type: schema.TypeString},
    93  									},
    94  									"query_string": {
    95  										Type:     schema.TypeBool,
    96  										Required: true,
    97  									},
    98  									"query_string_cache_keys": {
    99  										Type:     schema.TypeList,
   100  										Optional: true,
   101  										Elem:     &schema.Schema{Type: schema.TypeString},
   102  									},
   103  								},
   104  							},
   105  						},
   106  						"lambda_function_association": {
   107  							Type:     schema.TypeSet,
   108  							Optional: true,
   109  							MaxItems: 4,
   110  							Elem: &schema.Resource{
   111  								Schema: map[string]*schema.Schema{
   112  									"event_type": {
   113  										Type:     schema.TypeString,
   114  										Required: true,
   115  									},
   116  									"lambda_arn": {
   117  										Type:     schema.TypeString,
   118  										Required: true,
   119  									},
   120  								},
   121  							},
   122  							Set: lambdaFunctionAssociationHash,
   123  						},
   124  						"max_ttl": {
   125  							Type:     schema.TypeInt,
   126  							Required: true,
   127  						},
   128  						"min_ttl": {
   129  							Type:     schema.TypeInt,
   130  							Required: true,
   131  						},
   132  						"path_pattern": {
   133  							Type:     schema.TypeString,
   134  							Required: true,
   135  						},
   136  						"smooth_streaming": {
   137  							Type:     schema.TypeBool,
   138  							Optional: true,
   139  						},
   140  						"target_origin_id": {
   141  							Type:     schema.TypeString,
   142  							Required: true,
   143  						},
   144  						"trusted_signers": {
   145  							Type:     schema.TypeList,
   146  							Optional: true,
   147  							Elem:     &schema.Schema{Type: schema.TypeString},
   148  						},
   149  						"viewer_protocol_policy": {
   150  							Type:     schema.TypeString,
   151  							Required: true,
   152  						},
   153  					},
   154  				},
   155  			},
   156  			"comment": {
   157  				Type:     schema.TypeString,
   158  				Optional: true,
   159  			},
   160  			"custom_error_response": {
   161  				Type:     schema.TypeSet,
   162  				Optional: true,
   163  				Set:      customErrorResponseHash,
   164  				Elem: &schema.Resource{
   165  					Schema: map[string]*schema.Schema{
   166  						"error_caching_min_ttl": {
   167  							Type:     schema.TypeInt,
   168  							Optional: true,
   169  						},
   170  						"error_code": {
   171  							Type:     schema.TypeInt,
   172  							Required: true,
   173  						},
   174  						"response_code": {
   175  							Type:     schema.TypeInt,
   176  							Optional: true,
   177  						},
   178  						"response_page_path": {
   179  							Type:     schema.TypeString,
   180  							Optional: true,
   181  						},
   182  					},
   183  				},
   184  			},
   185  			"default_cache_behavior": {
   186  				Type:     schema.TypeSet,
   187  				Required: true,
   188  				Set:      defaultCacheBehaviorHash,
   189  				MaxItems: 1,
   190  				Elem: &schema.Resource{
   191  					Schema: map[string]*schema.Schema{
   192  						"allowed_methods": {
   193  							Type:     schema.TypeList,
   194  							Required: true,
   195  							Elem:     &schema.Schema{Type: schema.TypeString},
   196  						},
   197  						"cached_methods": {
   198  							Type:     schema.TypeList,
   199  							Required: true,
   200  							Elem:     &schema.Schema{Type: schema.TypeString},
   201  						},
   202  						"compress": {
   203  							Type:     schema.TypeBool,
   204  							Optional: true,
   205  							Default:  false,
   206  						},
   207  						"default_ttl": {
   208  							Type:     schema.TypeInt,
   209  							Required: true,
   210  						},
   211  						"forwarded_values": {
   212  							Type:     schema.TypeSet,
   213  							Required: true,
   214  							Set:      forwardedValuesHash,
   215  							MaxItems: 1,
   216  							Elem: &schema.Resource{
   217  								Schema: map[string]*schema.Schema{
   218  									"cookies": {
   219  										Type:     schema.TypeSet,
   220  										Optional: true,
   221  										Set:      cookiePreferenceHash,
   222  										MaxItems: 1,
   223  										Elem: &schema.Resource{
   224  											Schema: map[string]*schema.Schema{
   225  												"forward": {
   226  													Type:     schema.TypeString,
   227  													Required: true,
   228  												},
   229  												"whitelisted_names": {
   230  													Type:     schema.TypeList,
   231  													Optional: true,
   232  													Elem:     &schema.Schema{Type: schema.TypeString},
   233  												},
   234  											},
   235  										},
   236  									},
   237  									"headers": {
   238  										Type:     schema.TypeList,
   239  										Optional: true,
   240  										Elem:     &schema.Schema{Type: schema.TypeString},
   241  									},
   242  									"query_string": {
   243  										Type:     schema.TypeBool,
   244  										Required: true,
   245  									},
   246  									"query_string_cache_keys": {
   247  										Type:     schema.TypeList,
   248  										Optional: true,
   249  										Elem:     &schema.Schema{Type: schema.TypeString},
   250  									},
   251  								},
   252  							},
   253  						},
   254  						"lambda_function_association": {
   255  							Type:     schema.TypeSet,
   256  							Optional: true,
   257  							MaxItems: 4,
   258  							Elem: &schema.Resource{
   259  								Schema: map[string]*schema.Schema{
   260  									"event_type": {
   261  										Type:     schema.TypeString,
   262  										Required: true,
   263  									},
   264  									"lambda_arn": {
   265  										Type:     schema.TypeString,
   266  										Required: true,
   267  									},
   268  								},
   269  							},
   270  							Set: lambdaFunctionAssociationHash,
   271  						},
   272  						"max_ttl": {
   273  							Type:     schema.TypeInt,
   274  							Required: true,
   275  						},
   276  						"min_ttl": {
   277  							Type:     schema.TypeInt,
   278  							Required: true,
   279  						},
   280  						"smooth_streaming": {
   281  							Type:     schema.TypeBool,
   282  							Optional: true,
   283  						},
   284  						"target_origin_id": {
   285  							Type:     schema.TypeString,
   286  							Required: true,
   287  						},
   288  						"trusted_signers": {
   289  							Type:     schema.TypeList,
   290  							Optional: true,
   291  							Elem:     &schema.Schema{Type: schema.TypeString},
   292  						},
   293  						"viewer_protocol_policy": {
   294  							Type:     schema.TypeString,
   295  							Required: true,
   296  						},
   297  					},
   298  				},
   299  			},
   300  			"default_root_object": {
   301  				Type:     schema.TypeString,
   302  				Optional: true,
   303  			},
   304  			"enabled": {
   305  				Type:     schema.TypeBool,
   306  				Required: true,
   307  			},
   308  			"http_version": {
   309  				Type:         schema.TypeString,
   310  				Optional:     true,
   311  				Default:      "http2",
   312  				ValidateFunc: validateHTTP,
   313  			},
   314  			"logging_config": {
   315  				Type:     schema.TypeSet,
   316  				Optional: true,
   317  				Set:      loggingConfigHash,
   318  				MaxItems: 1,
   319  				Elem: &schema.Resource{
   320  					Schema: map[string]*schema.Schema{
   321  						"bucket": {
   322  							Type:     schema.TypeString,
   323  							Required: true,
   324  						},
   325  						"include_cookies": {
   326  							Type:     schema.TypeBool,
   327  							Optional: true,
   328  							Default:  false,
   329  						},
   330  						"prefix": {
   331  							Type:     schema.TypeString,
   332  							Optional: true,
   333  							Default:  "",
   334  						},
   335  					},
   336  				},
   337  			},
   338  			"origin": {
   339  				Type:     schema.TypeSet,
   340  				Required: true,
   341  				Set:      originHash,
   342  				Elem: &schema.Resource{
   343  					Schema: map[string]*schema.Schema{
   344  						"custom_origin_config": {
   345  							Type:          schema.TypeSet,
   346  							Optional:      true,
   347  							ConflictsWith: []string{"origin.s3_origin_config"},
   348  							Set:           customOriginConfigHash,
   349  							MaxItems:      1,
   350  							Elem: &schema.Resource{
   351  								Schema: map[string]*schema.Schema{
   352  									"http_port": {
   353  										Type:     schema.TypeInt,
   354  										Required: true,
   355  									},
   356  									"https_port": {
   357  										Type:     schema.TypeInt,
   358  										Required: true,
   359  									},
   360  									"origin_keepalive_timeout": {
   361  										Type:         schema.TypeInt,
   362  										Optional:     true,
   363  										Default:      5,
   364  										ValidateFunc: validation.IntBetween(1, 60),
   365  									},
   366  									"origin_read_timeout": {
   367  										Type:         schema.TypeInt,
   368  										Optional:     true,
   369  										Default:      30,
   370  										ValidateFunc: validation.IntBetween(4, 60),
   371  									},
   372  									"origin_protocol_policy": {
   373  										Type:     schema.TypeString,
   374  										Required: true,
   375  									},
   376  									"origin_ssl_protocols": {
   377  										Type:     schema.TypeList,
   378  										Required: true,
   379  										Elem:     &schema.Schema{Type: schema.TypeString},
   380  									},
   381  								},
   382  							},
   383  						},
   384  						"domain_name": {
   385  							Type:     schema.TypeString,
   386  							Required: true,
   387  						},
   388  						"custom_header": {
   389  							Type:     schema.TypeSet,
   390  							Optional: true,
   391  							Set:      originCustomHeaderHash,
   392  							Elem: &schema.Resource{
   393  								Schema: map[string]*schema.Schema{
   394  									"name": {
   395  										Type:     schema.TypeString,
   396  										Required: true,
   397  									},
   398  									"value": {
   399  										Type:     schema.TypeString,
   400  										Required: true,
   401  									},
   402  								},
   403  							},
   404  						},
   405  						"origin_id": {
   406  							Type:     schema.TypeString,
   407  							Required: true,
   408  						},
   409  						"origin_path": {
   410  							Type:     schema.TypeString,
   411  							Optional: true,
   412  						},
   413  						"s3_origin_config": {
   414  							Type:          schema.TypeSet,
   415  							Optional:      true,
   416  							ConflictsWith: []string{"origin.custom_origin_config"},
   417  							Set:           s3OriginConfigHash,
   418  							MaxItems:      1,
   419  							Elem: &schema.Resource{
   420  								Schema: map[string]*schema.Schema{
   421  									"origin_access_identity": {
   422  										Type:     schema.TypeString,
   423  										Required: true,
   424  									},
   425  								},
   426  							},
   427  						},
   428  					},
   429  				},
   430  			},
   431  			"price_class": {
   432  				Type:     schema.TypeString,
   433  				Optional: true,
   434  				Default:  "PriceClass_All",
   435  			},
   436  			"restrictions": {
   437  				Type:     schema.TypeSet,
   438  				Required: true,
   439  				Set:      restrictionsHash,
   440  				MaxItems: 1,
   441  				Elem: &schema.Resource{
   442  					Schema: map[string]*schema.Schema{
   443  						"geo_restriction": {
   444  							Type:     schema.TypeSet,
   445  							Required: true,
   446  							Set:      geoRestrictionHash,
   447  							MaxItems: 1,
   448  							Elem: &schema.Resource{
   449  								Schema: map[string]*schema.Schema{
   450  									"locations": {
   451  										Type:     schema.TypeList,
   452  										Optional: true,
   453  										Elem:     &schema.Schema{Type: schema.TypeString},
   454  									},
   455  									"restriction_type": {
   456  										Type:     schema.TypeString,
   457  										Required: true,
   458  									},
   459  								},
   460  							},
   461  						},
   462  					},
   463  				},
   464  			},
   465  			"viewer_certificate": {
   466  				Type:     schema.TypeSet,
   467  				Required: true,
   468  				Set:      viewerCertificateHash,
   469  				MaxItems: 1,
   470  				Elem: &schema.Resource{
   471  					Schema: map[string]*schema.Schema{
   472  						"acm_certificate_arn": {
   473  							Type:          schema.TypeString,
   474  							Optional:      true,
   475  							ConflictsWith: []string{"viewer_certificate.cloudfront_default_certificate", "viewer_certificate.iam_certificate_id"},
   476  						},
   477  						"cloudfront_default_certificate": {
   478  							Type:          schema.TypeBool,
   479  							Optional:      true,
   480  							ConflictsWith: []string{"viewer_certificate.acm_certificate_arn", "viewer_certificate.iam_certificate_id"},
   481  						},
   482  						"iam_certificate_id": {
   483  							Type:          schema.TypeString,
   484  							Optional:      true,
   485  							ConflictsWith: []string{"viewer_certificate.acm_certificate_arn", "viewer_certificate.cloudfront_default_certificate"},
   486  						},
   487  						"minimum_protocol_version": {
   488  							Type:     schema.TypeString,
   489  							Optional: true,
   490  							Default:  "SSLv3",
   491  						},
   492  						"ssl_support_method": {
   493  							Type:     schema.TypeString,
   494  							Optional: true,
   495  						},
   496  					},
   497  				},
   498  			},
   499  			"web_acl_id": {
   500  				Type:     schema.TypeString,
   501  				Optional: true,
   502  			},
   503  			"caller_reference": {
   504  				Type:     schema.TypeString,
   505  				Computed: true,
   506  			},
   507  			"status": {
   508  				Type:     schema.TypeString,
   509  				Computed: true,
   510  			},
   511  			"active_trusted_signers": {
   512  				Type:     schema.TypeMap,
   513  				Computed: true,
   514  			},
   515  			"domain_name": {
   516  				Type:     schema.TypeString,
   517  				Computed: true,
   518  			},
   519  			"last_modified_time": {
   520  				Type:     schema.TypeString,
   521  				Computed: true,
   522  			},
   523  			"in_progress_validation_batches": {
   524  				Type:     schema.TypeInt,
   525  				Computed: true,
   526  			},
   527  			"etag": {
   528  				Type:     schema.TypeString,
   529  				Computed: true,
   530  			},
   531  			"hosted_zone_id": {
   532  				Type:     schema.TypeString,
   533  				Computed: true,
   534  			},
   535  			// retain_on_delete is a non-API attribute that may help facilitate speedy
   536  			// deletion of a resoruce. It's mainly here for testing purposes, so
   537  			// enable at your own risk.
   538  			"retain_on_delete": {
   539  				Type:     schema.TypeBool,
   540  				Optional: true,
   541  				Default:  false,
   542  			},
   543  			"is_ipv6_enabled": {
   544  				Type:     schema.TypeBool,
   545  				Optional: true,
   546  				Default:  false,
   547  			},
   548  
   549  			"tags": tagsSchema(),
   550  		},
   551  	}
   552  }
   553  
   554  func resourceAwsCloudFrontDistributionCreate(d *schema.ResourceData, meta interface{}) error {
   555  	conn := meta.(*AWSClient).cloudfrontconn
   556  
   557  	params := &cloudfront.CreateDistributionWithTagsInput{
   558  		DistributionConfigWithTags: &cloudfront.DistributionConfigWithTags{
   559  			DistributionConfig: expandDistributionConfig(d),
   560  			Tags:               tagsFromMapCloudFront(d.Get("tags").(map[string]interface{})),
   561  		},
   562  	}
   563  
   564  	resp, err := conn.CreateDistributionWithTags(params)
   565  	if err != nil {
   566  		return err
   567  	}
   568  	d.SetId(*resp.Distribution.Id)
   569  	return resourceAwsCloudFrontDistributionRead(d, meta)
   570  }
   571  
   572  func resourceAwsCloudFrontDistributionRead(d *schema.ResourceData, meta interface{}) error {
   573  	conn := meta.(*AWSClient).cloudfrontconn
   574  	params := &cloudfront.GetDistributionInput{
   575  		Id: aws.String(d.Id()),
   576  	}
   577  
   578  	resp, err := conn.GetDistribution(params)
   579  	if err != nil {
   580  		if errcode, ok := err.(awserr.Error); ok && errcode.Code() == "NoSuchDistribution" {
   581  			log.Printf("[WARN] No Distribution found: %s", d.Id())
   582  			d.SetId("")
   583  			return nil
   584  		}
   585  
   586  		return err
   587  	}
   588  
   589  	// Update attributes from DistributionConfig
   590  	err = flattenDistributionConfig(d, resp.Distribution.DistributionConfig)
   591  	if err != nil {
   592  		return err
   593  	}
   594  	// Update other attributes outside of DistributionConfig
   595  	d.SetId(*resp.Distribution.Id)
   596  	err = d.Set("active_trusted_signers", flattenActiveTrustedSigners(resp.Distribution.ActiveTrustedSigners))
   597  	if err != nil {
   598  		return err
   599  	}
   600  	d.Set("status", resp.Distribution.Status)
   601  	d.Set("domain_name", resp.Distribution.DomainName)
   602  	d.Set("last_modified_time", aws.String(resp.Distribution.LastModifiedTime.String()))
   603  	d.Set("in_progress_validation_batches", resp.Distribution.InProgressInvalidationBatches)
   604  	d.Set("etag", resp.ETag)
   605  	d.Set("arn", resp.Distribution.ARN)
   606  
   607  	tagResp, err := conn.ListTagsForResource(&cloudfront.ListTagsForResourceInput{
   608  		Resource: aws.String(d.Get("arn").(string)),
   609  	})
   610  
   611  	if err != nil {
   612  		return errwrap.Wrapf(fmt.Sprintf(
   613  			"Error retrieving EC2 tags for CloudFront Distribution %q (ARN: %q): {{err}}",
   614  			d.Id(), d.Get("arn").(string)), err)
   615  	}
   616  
   617  	if err := d.Set("tags", tagsToMapCloudFront(tagResp.Tags)); err != nil {
   618  		return err
   619  	}
   620  
   621  	return nil
   622  }
   623  
   624  func resourceAwsCloudFrontDistributionUpdate(d *schema.ResourceData, meta interface{}) error {
   625  	conn := meta.(*AWSClient).cloudfrontconn
   626  	params := &cloudfront.UpdateDistributionInput{
   627  		Id:                 aws.String(d.Id()),
   628  		DistributionConfig: expandDistributionConfig(d),
   629  		IfMatch:            aws.String(d.Get("etag").(string)),
   630  	}
   631  	_, err := conn.UpdateDistribution(params)
   632  	if err != nil {
   633  		return err
   634  	}
   635  
   636  	if err := setTagsCloudFront(conn, d, d.Get("arn").(string)); err != nil {
   637  		return err
   638  	}
   639  
   640  	return resourceAwsCloudFrontDistributionRead(d, meta)
   641  }
   642  
   643  func resourceAwsCloudFrontDistributionDelete(d *schema.ResourceData, meta interface{}) error {
   644  	conn := meta.(*AWSClient).cloudfrontconn
   645  
   646  	// manually disable the distribution first
   647  	d.Set("enabled", false)
   648  	err := resourceAwsCloudFrontDistributionUpdate(d, meta)
   649  	if err != nil {
   650  		return err
   651  	}
   652  
   653  	// skip delete if retain_on_delete is enabled
   654  	if d.Get("retain_on_delete").(bool) {
   655  		log.Printf("[WARN] Removing CloudFront Distribution ID %q with `retain_on_delete` set. Please delete this distribution manually.", d.Id())
   656  		d.SetId("")
   657  		return nil
   658  	}
   659  
   660  	// Distribution needs to be in deployed state again before it can be deleted.
   661  	err = resourceAwsCloudFrontDistributionWaitUntilDeployed(d.Id(), meta)
   662  	if err != nil {
   663  		return err
   664  	}
   665  
   666  	// now delete
   667  	params := &cloudfront.DeleteDistributionInput{
   668  		Id:      aws.String(d.Id()),
   669  		IfMatch: aws.String(d.Get("etag").(string)),
   670  	}
   671  
   672  	_, err = conn.DeleteDistribution(params)
   673  	if err != nil {
   674  		return err
   675  	}
   676  
   677  	// Done
   678  	d.SetId("")
   679  	return nil
   680  }
   681  
   682  // resourceAwsCloudFrontWebDistributionWaitUntilDeployed blocks until the
   683  // distribution is deployed. It currently takes exactly 15 minutes to deploy
   684  // but that might change in the future.
   685  func resourceAwsCloudFrontDistributionWaitUntilDeployed(id string, meta interface{}) error {
   686  	stateConf := &resource.StateChangeConf{
   687  		Pending:    []string{"InProgress"},
   688  		Target:     []string{"Deployed"},
   689  		Refresh:    resourceAwsCloudFrontWebDistributionStateRefreshFunc(id, meta),
   690  		Timeout:    70 * time.Minute,
   691  		MinTimeout: 15 * time.Second,
   692  		Delay:      10 * time.Minute,
   693  	}
   694  
   695  	_, err := stateConf.WaitForState()
   696  	return err
   697  }
   698  
   699  // The refresh function for resourceAwsCloudFrontWebDistributionWaitUntilDeployed.
   700  func resourceAwsCloudFrontWebDistributionStateRefreshFunc(id string, meta interface{}) resource.StateRefreshFunc {
   701  	return func() (interface{}, string, error) {
   702  		conn := meta.(*AWSClient).cloudfrontconn
   703  		params := &cloudfront.GetDistributionInput{
   704  			Id: aws.String(id),
   705  		}
   706  
   707  		resp, err := conn.GetDistribution(params)
   708  		if err != nil {
   709  			log.Printf("[WARN] Error retrieving CloudFront Distribution %q details: %s", id, err)
   710  			return nil, "", err
   711  		}
   712  
   713  		if resp == nil {
   714  			return nil, "", nil
   715  		}
   716  
   717  		return resp.Distribution, *resp.Distribution.Status, nil
   718  	}
   719  }
   720  
   721  // validateHTTP ensures that the http_version resource parameter is
   722  // correct.
   723  func validateHTTP(v interface{}, k string) (ws []string, errors []error) {
   724  	value := v.(string)
   725  
   726  	if value != "http1.1" && value != "http2" {
   727  		errors = append(errors, fmt.Errorf(
   728  			"%q contains an invalid HTTP version parameter %q. Valid parameters are either %q or %q.",
   729  			k, value, "http1.1", "http2"))
   730  	}
   731  	return
   732  }