Merge pull request #5327 from Originate/s3-website-routing-rules
Added routing rules to s3 buckets
This commit is contained in:
commit
5b548e938c
|
@ -102,9 +102,16 @@ func resourceAwsS3Bucket() *schema.Resource {
|
|||
ConflictsWith: []string{
|
||||
"website.0.index_document",
|
||||
"website.0.error_document",
|
||||
"website.0.routing_rules",
|
||||
},
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"routing_rules": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
StateFunc: normalizeJson,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -367,6 +374,14 @@ func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error {
|
|||
}
|
||||
}
|
||||
|
||||
if v := ws.RoutingRules; v != nil {
|
||||
rr, err := normalizeRoutingRules(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error while marshaling routing rules: %s", err)
|
||||
}
|
||||
w["routing_rules"] = rr
|
||||
}
|
||||
|
||||
websites = append(websites, w)
|
||||
}
|
||||
if err := d.Set("website", websites); err != nil {
|
||||
|
@ -660,6 +675,7 @@ func resourceAwsS3BucketWebsitePut(s3conn *s3.S3, d *schema.ResourceData, websit
|
|||
indexDocument := website["index_document"].(string)
|
||||
errorDocument := website["error_document"].(string)
|
||||
redirectAllRequestsTo := website["redirect_all_requests_to"].(string)
|
||||
routingRules := website["routing_rules"].(string)
|
||||
|
||||
if indexDocument == "" && redirectAllRequestsTo == "" {
|
||||
return fmt.Errorf("Must specify either index_document or redirect_all_requests_to.")
|
||||
|
@ -684,6 +700,14 @@ func resourceAwsS3BucketWebsitePut(s3conn *s3.S3, d *schema.ResourceData, websit
|
|||
}
|
||||
}
|
||||
|
||||
if routingRules != "" {
|
||||
var unmarshaledRules []*s3.RoutingRule
|
||||
if err := json.Unmarshal([]byte(routingRules), &unmarshaledRules); err != nil {
|
||||
return err
|
||||
}
|
||||
websiteConfiguration.RoutingRules = unmarshaledRules
|
||||
}
|
||||
|
||||
putInput := &s3.PutBucketWebsiteInput{
|
||||
Bucket: aws.String(bucket),
|
||||
WebsiteConfiguration: websiteConfiguration,
|
||||
|
@ -841,11 +865,52 @@ func resourceAwsS3BucketLoggingUpdate(s3conn *s3.S3, d *schema.ResourceData) err
|
|||
return nil
|
||||
}
|
||||
|
||||
func normalizeRoutingRules(w []*s3.RoutingRule) (string, error) {
|
||||
withNulls, err := json.Marshal(w)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var rules []map[string]interface{}
|
||||
json.Unmarshal(withNulls, &rules)
|
||||
|
||||
var cleanRules []map[string]interface{}
|
||||
for _, rule := range rules {
|
||||
cleanRules = append(cleanRules, removeNil(rule))
|
||||
}
|
||||
|
||||
withoutNulls, err := json.Marshal(cleanRules)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(withoutNulls), nil
|
||||
}
|
||||
|
||||
func removeNil(data map[string]interface{}) map[string]interface{} {
|
||||
withoutNil := make(map[string]interface{})
|
||||
|
||||
for k, v := range data {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch v.(type) {
|
||||
case map[string]interface{}:
|
||||
withoutNil[k] = removeNil(v.(map[string]interface{}))
|
||||
default:
|
||||
withoutNil[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return withoutNil
|
||||
}
|
||||
|
||||
func normalizeJson(jsonString interface{}) string {
|
||||
if jsonString == nil {
|
||||
return ""
|
||||
}
|
||||
j := make(map[string]interface{})
|
||||
var j interface{}
|
||||
err := json.Unmarshal([]byte(jsonString.(string)), &j)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Error parsing JSON: %s", err)
|
||||
|
|
|
@ -185,6 +185,51 @@ func TestAccAWSS3Bucket_WebsiteRedirect(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccAWSS3Bucket_WebsiteRoutingRules(t *testing.T) {
|
||||
rInt := acctest.RandInt()
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSS3BucketDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSS3BucketWebsiteConfigWithRoutingRules(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"),
|
||||
testAccCheckAWSS3BucketWebsite(
|
||||
"aws_s3_bucket.bucket", "index.html", "error.html", "", ""),
|
||||
testAccCheckAWSS3BucketWebsiteRoutingRules(
|
||||
"aws_s3_bucket.bucket",
|
||||
[]*s3.RoutingRule{
|
||||
&s3.RoutingRule{
|
||||
Condition: &s3.Condition{
|
||||
KeyPrefixEquals: aws.String("docs/"),
|
||||
},
|
||||
Redirect: &s3.Redirect{
|
||||
ReplaceKeyPrefixWith: aws.String("documents/"),
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_s3_bucket.bucket", "website_endpoint", testAccWebsiteEndpoint(rInt)),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccAWSS3BucketConfig(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"),
|
||||
testAccCheckAWSS3BucketWebsite(
|
||||
"aws_s3_bucket.bucket", "", "", "", ""),
|
||||
testAccCheckAWSS3BucketWebsiteRoutingRules("aws_s3_bucket.bucket", nil),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_s3_bucket.bucket", "website_endpoint", ""),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Test TestAccAWSS3Bucket_shouldFailNotFound is designed to fail with a "plan
|
||||
// not empty" error in Terraform, to check against regresssions.
|
||||
// See https://github.com/hashicorp/terraform/pull/2925
|
||||
|
@ -396,6 +441,7 @@ func testAccCheckAWSS3BucketPolicy(n string, policy string) resource.TestCheckFu
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckAWSS3BucketWebsite(n string, indexDoc string, errorDoc string, redirectProtocol string, redirectTo string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, _ := s.RootModule().Resources[n]
|
||||
|
@ -452,6 +498,30 @@ func testAccCheckAWSS3BucketWebsite(n string, indexDoc string, errorDoc string,
|
|||
}
|
||||
}
|
||||
|
||||
func testAccCheckAWSS3BucketWebsiteRoutingRules(n string, routingRules []*s3.RoutingRule) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, _ := s.RootModule().Resources[n]
|
||||
conn := testAccProvider.Meta().(*AWSClient).s3conn
|
||||
|
||||
out, err := conn.GetBucketWebsite(&s3.GetBucketWebsiteInput{
|
||||
Bucket: aws.String(rs.Primary.ID),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if routingRules == nil {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("GetBucketWebsite error: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(out.RoutingRules, routingRules) {
|
||||
return fmt.Errorf("bad routing rule, expected: %v, got %v", routingRules, out.RoutingRules)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckAWSS3BucketVersioning(n string, versioningStatus string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, _ := s.RootModule().Resources[n]
|
||||
|
@ -478,6 +548,7 @@ func testAccCheckAWSS3BucketVersioning(n string, versioningStatus string) resour
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckAWSS3BucketCors(n string, corsRules []*s3.CORSRule) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, _ := s.RootModule().Resources[n]
|
||||
|
@ -610,6 +681,30 @@ resource "aws_s3_bucket" "bucket" {
|
|||
`, randInt)
|
||||
}
|
||||
|
||||
func testAccAWSS3BucketWebsiteConfigWithRoutingRules(randInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_s3_bucket" "bucket" {
|
||||
bucket = "tf-test-bucket-%d"
|
||||
acl = "public-read"
|
||||
|
||||
website {
|
||||
index_document = "index.html"
|
||||
error_document = "error.html"
|
||||
routing_rules = <<EOF
|
||||
[{
|
||||
"Condition": {
|
||||
"KeyPrefixEquals": "docs/"
|
||||
},
|
||||
"Redirect": {
|
||||
"ReplaceKeyPrefixWith": "documents/"
|
||||
}
|
||||
}]
|
||||
EOF
|
||||
}
|
||||
}
|
||||
`, randInt)
|
||||
}
|
||||
|
||||
func testAccAWSS3BucketConfigWithPolicy(randInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_s3_bucket" "bucket" {
|
||||
|
|
|
@ -37,6 +37,16 @@ resource "aws_s3_bucket" "b" {
|
|||
website {
|
||||
index_document = "index.html"
|
||||
error_document = "error.html"
|
||||
routing_rules = <<EOF
|
||||
[{
|
||||
"Condition": {
|
||||
"KeyPrefixEquals": "docs/"
|
||||
},
|
||||
"Redirect": {
|
||||
"ReplaceKeyPrefixWith": "documents/"
|
||||
}
|
||||
}]
|
||||
EOF
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -107,6 +117,8 @@ The `website` object supports the following:
|
|||
* `index_document` - (Required, unless using `redirect_all_requests_to`) Amazon S3 returns this index document when requests are made to the root domain or any of the subfolders.
|
||||
* `error_document` - (Optional) An absolute path to the document to return in case of a 4XX error.
|
||||
* `redirect_all_requests_to` - (Optional) A hostname to redirect all website requests for this bucket to. Hostname can optionally be prefixed with a protocol (`http://` or `https://`) to use when redirecting requests. The default is the protocol that is used in the original request.
|
||||
* `routing_rules` - (Optional) A json array containing [routing rules](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-websiteconfiguration-routingrules.html)
|
||||
describing redirect behavior and when redirects are applied.
|
||||
|
||||
The `CORS` object supports the following:
|
||||
|
||||
|
|
Loading…
Reference in New Issue