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