From a15d23a35df1162ccd4c2cd7ac20e0c07f18c975 Mon Sep 17 00:00:00 2001 From: IBM-diksha <112411162+IBM-diksha@users.noreply.github.com> Date: Fri, 15 Sep 2023 10:35:55 +0530 Subject: [PATCH] Adding support for COS Static Web hosting (#4798) * Adding support for COS Static Web hosting * Addressing the review comments * Changes in the datasource documentation --- examples/ibm-cos-bucket-object/README.md | 12 + examples/ibm-cos-bucket-object/main.tf | 10 + examples/ibm-cos-bucket/README.md | 118 ++ examples/ibm-cos-bucket/main.tf | 100 ++ ibm/flex/structures.go | 131 +++ ibm/provider/provider.go | 1 + ibm/service/cos/data_source_ibm_cos_bucket.go | 147 +++ .../cos/data_source_ibm_cos_bucket_object.go | 7 + .../cos/resource_ibm_cos_bucket_object.go | 17 + ...ce_ibm_cos_bucket_website_configuration.go | 507 +++++++++ ...m_cos_bucket_website_configuration_test.go | 1004 +++++++++++++++++ website/docs/d/cos_bucket.html.markdown | 2 + .../docs/d/cos_bucket_object.html.markdown | 1 + .../docs/r/cos_bucket_object.html.markdown | 20 + ...bucket_website_configuration.html.markdown | 234 ++++ 15 files changed, 2311 insertions(+) create mode 100644 ibm/service/cos/resource_ibm_cos_bucket_website_configuration.go create mode 100644 ibm/service/cos/resource_ibm_cos_bucket_website_configuration_test.go create mode 100644 website/docs/r/cos_bucket_website_configuration.html.markdown diff --git a/examples/ibm-cos-bucket-object/README.md b/examples/ibm-cos-bucket-object/README.md index 81b75fcf74..77f38c2849 100644 --- a/examples/ibm-cos-bucket-object/README.md +++ b/examples/ibm-cos-bucket-object/README.md @@ -62,6 +62,17 @@ resource "ibm_cos_bucket_object" "object" { force_delete = true } ``` +## COS Website Redirect + + +```hcl +resource "ibm_cos_bucket_object" "object" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + key = "page1.html" + website_redirect = "/page2.html" +} +``` ## Requirements @@ -90,6 +101,7 @@ resource "ibm_cos_bucket_object" "object" { | object_lock_legal_hold_status | An object lock configuration on the object, the valid states are ON/OFF. When ON prevents deletion of the object version. | `string` | false | | object_lock_mode | Retention modes apply different levels of protection to the objects. | `string` | false | | object_lock_legal_hold_status | An object cannot be deleted when the current time is earlier than the retainUntilDate. After this date, the object can be deleted. | `string` | false | +| website_redirect | To redirect the request for a particular object. | `string` | false | ## Outputs diff --git a/examples/ibm-cos-bucket-object/main.tf b/examples/ibm-cos-bucket-object/main.tf index 63b12c2517..dcb3b2550e 100644 --- a/examples/ibm-cos-bucket-object/main.tf +++ b/examples/ibm-cos-bucket-object/main.tf @@ -62,3 +62,13 @@ resource "ibm_cos_bucket_object" "object_object_lock" { object_lock_legal_hold_status = "ON" force_delete = true } + +// COS static web hosting + +resource "ibm_cos_bucket_object" "object" { + bucket_crn = ibm_cos_bucket.object_lock.crn + bucket_location = ibm_cos_bucket.object_lock.cross_region_location + key = "page1.html" + website_redirect = "page2.html" +} + diff --git a/examples/ibm-cos-bucket/README.md b/examples/ibm-cos-bucket/README.md index 1e1a17c5d4..e459bc9a88 100644 --- a/examples/ibm-cos-bucket/README.md +++ b/examples/ibm-cos-bucket/README.md @@ -399,6 +399,113 @@ resource ibm_cos_bucket_object_lock_configuration "objectlock" { } } ``` + + +## COS Static Webhosting + +Provides an Static web hosting configuration resource. This resource is used to configure the website to use your documents as an index for the site and to potentially display errors.It can also be used to configure more advanced options including routing rules and request redirect for your domain. + +## Example usage +The following example creates an instance of IBM Cloud Object Storage, creates a bucket and adds a website configuration on the bucket.Along with the basic bucket configuration , example of redirect all requests and adding routing rules have been given below. + +```terraform + +# Create a bucket +resource "ibm_cos_bucket" "cos_bucket_website_configuration" { + bucket_name = var.bucket_name + resource_instance_id = ibm_resource_instance.cos_instance.id + region_location = var.regional_loc + storage_class = var.standard_storage_class + +} +# Give public access to above mentioned bucket +resource "ibm_iam_access_group_policy" "policy" { + depends_on = [ibm_cos_bucket.cos_bucket_website_configuration] + access_group_id = data.ibm_iam_access_group.public_access_group.groups[0].id + roles = ["Object Reader"] + + resources { + service = "cloud-object-storage" + resource_type = "bucket" + resource_instance_id = "COS instance guid" + resource = data.ibm_cos_bucket.cos_bucket_website_configuration.bucket_name + } +} + +# Add basic website configuration on a COS bucket +resource ibm_cos_bucket_website_configuration "website_configuration" { + bucket_crn = "bucket_crn" + bucket_location = data.ibm_cos_bucket.cos_bucket_website_configuration.regional_location + website_configuration { + error_document{ + key = "error.html" + } + index_document{ + suffix = "index.html" + } + } +} + +# Add a request redirect website configuration on a COS bucket +resource ibm_cos_bucket_website_configuration "website_configuration" { + bucket_crn = "bucket_crn" + bucket_location = data.ibm_cos_bucket.cos_bucket_website_configuration.regional_location + website_configuration { + redirect_all_requests_to{ + host_name = "exampleBucketName" + protocol = "https" + } + } +} + + +# Add a website configuration on a COS bucket with routing rule + +resource ibm_cos_bucket_website_configuration "website_configuration" { + bucket_crn = "bucket_crn" + bucket_location = data.ibm_cos_bucket.cos_bucket_website_configuration.regional_location + website_configuration { + error_document{ + key = "error.html" + } + index_document{ + suffix = "index.html" + } + routing_rule { + condition { + key_prefix_equals = "pages/" + } + redirect { + replace_key_prefix_with = "web_pages/" + } + } + } +} + +# Add a website configuration on a COS bucket with JSON routing rule +resource ibm_cos_bucket_website_configuration "website_configuration" { + bucket_crn = "bucket_crn" + bucket_location = data.ibm_cos_bucket.cos_bucket_website_configuration.regional_location + website_configuration { + error_document{ + key = "error.html" + } + index_document{ + suffix = "index.html" + } + routing_rules = < ## Requirements @@ -459,4 +566,15 @@ resource ibm_cos_bucket_object_lock_configuration "objectlock" { | mode | Retention mode for the Object Lock configuration. | `String` | yes | years | Retention period in terms of years after which the object can be deleted. | `int` | no | days | Retention period in terms of days after which the object can be deleted. | `int` | no +| key | Object key name to use when a 4XX class error occurs given as error document. | `String` | no +| suffix | The home or default page of the website when static web hosting configuration is added. | `String` | Yes +| hostname | Name of the host where requests are redirected. | `String` | Yes +| protocol | Protocol to use when redirecting requests. The default is the protocol that is used in the original request. | `String` | No +| http_error_code_returned_equals | HTTP error code when the redirect is applied. | `String` | No +| key_prefix_equals | Object key name prefix when the redirect is applied. | `String` | No +| host_name | Host name to use in the redirect request. | `String` | Yes +| protocol | Protocol to use when redirecting requests. | `String` | No +| http_redirect_code | HTTP redirect code to use on the response. | `String` | No +| replace_key_with | Specific object key to use in the redirect request. | `String` | No +| replace_key_prefix_with | Object key prefix to use in the redirect request. | `String` | No {: caption="inputs"} diff --git a/examples/ibm-cos-bucket/main.tf b/examples/ibm-cos-bucket/main.tf index e4fcfe0543..5fd73492d8 100644 --- a/examples/ibm-cos-bucket/main.tf +++ b/examples/ibm-cos-bucket/main.tf @@ -396,3 +396,103 @@ resource ibm_cos_bucket_object_lock_configuration "objectlock" { } } } + + + +#COS static webhosting + + +# Create a bucket +resource "ibm_cos_bucket" "cos_bucket_website_configuration" { + bucket_name = var.bucket_name + resource_instance_id = ibm_resource_instance.cos_instance.id + region_location = var.regional_loc + storage_class = var.standard_storage_class + +} +# Give public access to above mentioned bucket +resource "ibm_iam_access_group_policy" "policy" { + depends_on = [ibm_cos_bucket.cos_bucket_website_configuration] + access_group_id = data.ibm_iam_access_group.public_access_group.groups[0].id + roles = ["Object Reader"] + + resources { + service = "cloud-object-storage" + resource_type = "bucket" + resource_instance_id = "COS instance guid" + resource = data.ibm_cos_bucket.cos_bucket_website_configuration.bucket_name + } +} + +# Add basic website configuration on a COS bucket +resource ibm_cos_bucket_website_configuration "website_configuration" { + bucket_crn = "bucket_crn" + bucket_location = data.ibm_cos_bucket.cos_bucket_website_configuration.regional_location + website_configuration { + error_document{ + key = "error.html" + } + index_document{ + suffix = "index.html" + } + } +} + +# Add a request redirect website configuration on a COS bucket +resource ibm_cos_bucket_website_configuration "website_configuration" { + bucket_crn = "bucket_crn" + bucket_location = data.ibm_cos_bucket.cos_bucket_website_configuration.regional_location + website_configuration { + redirect_all_requests_to{ + host_name = "exampleBucketName" + protocol = "https" + } + } +} + + +# Add a website configuration on a COS bucket with routing rule +resource ibm_cos_bucket_website_configuration "website_configuration" { + bucket_crn = "bucket_crn" + bucket_location = data.ibm_cos_bucket.cos_bucket_website_configuration.regional_location + website_configuration { + error_document{ + key = "error.html" + } + index_document{ + suffix = "index.html" + } + routing_rule { + condition { + key_prefix_equals = "pages/" + } + redirect { + replace_key_prefix_with = "web_pages/" + } + } + } +} + +# Add a website configuration on a COS bucket with JSON routing rule +resource ibm_cos_bucket_website_configuration "website_configuration" { + bucket_crn = "bucket_crn" + bucket_location = data.ibm_cos_bucket.cos_bucket_website_configuration.regional_location + website_configuration { + error_document{ + key = "error.html" + } + index_document{ + suffix = "index.html" + } + routing_rules = < 0 { d.Set("object_lock_configuration", objectLockConfiguration) } + } //getBucketConfiguration + getBucketWebsiteConfigurationInput := &s3.GetBucketWebsiteInput{ + Bucket: aws.String(bucketName), + } + + outputBucketWebsite, err := s3Client.GetBucketWebsite(getBucketWebsiteConfigurationInput) + var outputBucketWebsiteptr *s3.WebsiteConfiguration + outputBucketWebsiteptr = (*s3.WebsiteConfiguration)(outputBucketWebsite) + if err != nil && !strings.Contains(err.Error(), "AccessDenied: Access Denied") { + return err + } + + if outputBucketWebsite != nil { + websiteConfiguration := flex.WebsiteConfigurationGet(outputBucketWebsiteptr) + if len(websiteConfiguration) > 0 { + d.Set("website_configuration", websiteConfiguration) + } + websiteEndpoint := getWebsiteEndpoint(bucketName, bucketRegion) + if websiteEndpoint != "" { + d.Set("website_endpoint", websiteEndpoint) + } + } return nil diff --git a/ibm/service/cos/data_source_ibm_cos_bucket_object.go b/ibm/service/cos/data_source_ibm_cos_bucket_object.go index 89de35fbf4..16b658a534 100644 --- a/ibm/service/cos/data_source_ibm_cos_bucket_object.go +++ b/ibm/service/cos/data_source_ibm_cos_bucket_object.go @@ -91,6 +91,10 @@ func DataSourceIBMCosBucketObject() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "website_redirect": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -171,6 +175,9 @@ func dataSourceIBMCosBucketObjectRead(ctx context.Context, d *schema.ResourceDat if out.ObjectLockLegalHoldStatus != nil { d.Set("object_lock_legal_hold_status", out.ObjectLockLegalHoldStatus) } + if out.WebsiteRedirectLocation != nil { + d.Set("website_redirect", out.WebsiteRedirectLocation) + } objectID := getObjectId(bucketCRN, objectKey, bucketLocation) d.SetId(objectID) diff --git a/ibm/service/cos/resource_ibm_cos_bucket_object.go b/ibm/service/cos/resource_ibm_cos_bucket_object.go index 1850c57e61..9b7afa0cf6 100644 --- a/ibm/service/cos/resource_ibm_cos_bucket_object.go +++ b/ibm/service/cos/resource_ibm_cos_bucket_object.go @@ -146,6 +146,11 @@ func ResourceIBMCOSBucketObject() *schema.Resource { ValidateFunc: validation.IsRFC3339Time, Description: "An object cannot be deleted when the current time is earlier than the retainUntilDate. After this date, the object can be deleted.", }, + "website_redirect": { + Type: schema.TypeString, + Optional: true, + Description: "Redirect a request to another object or an URL", + }, }, } } @@ -203,6 +208,10 @@ func resourceIBMCOSBucketObjectCreate(ctx context.Context, d *schema.ResourceDat Key: aws.String(objectKey), Body: body, } + //if website redirect location if given for a an object + if v, ok := d.GetOk("website_redirect"); ok { + putInput.WebsiteRedirectLocation = aws.String(v.(string)) + } if _, err := s3Client.PutObject(putInput); err != nil { return diag.FromErr(fmt.Errorf("[ERROR] Error putting object (%s) in COS bucket (%s): %s", objectKey, bucketName, err)) @@ -328,6 +337,9 @@ func resourceIBMCOSBucketObjectRead(ctx context.Context, d *schema.ResourceData, if out.ObjectLockLegalHoldStatus != nil { d.Set("object_lock_legal_hold_status", out.ObjectLockLegalHoldStatus) } + if out.WebsiteRedirectLocation != nil { + d.Set("website_redirect", out.WebsiteRedirectLocation) + } d.Set("key", objectKey) d.Set("version_id", out.VersionId) d.Set("object_sql_url", "cos://"+bucketLocation+"/"+bucketName+"/"+objectKey) @@ -387,6 +399,11 @@ func resourceIBMCOSBucketObjectUpdate(ctx context.Context, d *schema.ResourceDat Key: aws.String(objectKey), Body: body, } + if d.HasChange("website_redirect") { + if v, ok := d.GetOk("website_redirect"); ok { + putInput.WebsiteRedirectLocation = aws.String(v.(string)) + } + } if _, err := s3Client.PutObject(putInput); err != nil { return diag.FromErr(fmt.Errorf("[ERROR] Error putting object (%s) in COS bucket (%s): %s", objectKey, bucketName, err)) diff --git a/ibm/service/cos/resource_ibm_cos_bucket_website_configuration.go b/ibm/service/cos/resource_ibm_cos_bucket_website_configuration.go new file mode 100644 index 0000000000..f720e09e75 --- /dev/null +++ b/ibm/service/cos/resource_ibm_cos_bucket_website_configuration.go @@ -0,0 +1,507 @@ +package cos + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/validate" + "github.com/IBM/ibm-cos-sdk-go/aws" + "github.com/IBM/ibm-cos-sdk-go/service/s3" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" + validation "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceIBMCOSBucketWebsiteConfiguration() *schema.Resource { + return &schema.Resource{ + Create: resourceIBMCOSBucketWebsiteConfigurationCreate, + Read: resourceIBMCOSBucketWebsiteConfigurationRead, + Update: resourceIBMCOSBucketWebsiteConfigurationUpdate, + Delete: resourceIBMCOSBucketWebsiteConfigurationDelete, + Importer: &schema.ResourceImporter{}, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Update: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "bucket_crn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "COS bucket CRN", + }, + "bucket_location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "COS bucket location", + }, + "endpoint_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.ValidateAllowedStringValues([]string{"public", "private", "direct"}), + Description: "COS endpoint type: public, private, direct", + Default: "public", + }, + "website_configuration": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Description: "Configuration for Hosting a static website on COS with public access.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "error_document": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "This is returned when an error occurs.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "index_document": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "Home or the default page of the website.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "suffix": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "redirect_all_requests_to": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "Redirect requests can be set to specific page documents, individual routing rules, or redirect all requests globally to one bucket or domain.", + ConflictsWith: []string{"website_configuration.0.error_document", "website_configuration.0.index_document", "website_configuration.0.routing_rule", "website_configuration.0.routing_rules"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host_name": { + Type: schema.TypeString, + Required: true, + }, + "protocol": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(s3.Protocol_Values(), false), + }, + }, + }, + }, + "routing_rule": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Description: "Rules that define when a redirect is applied and the redirect behavior.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "condition": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "A condition that must be met for the specified redirect to be applie.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "http_error_code_returned_equals": { + Type: schema.TypeString, + Optional: true, + Description: "The HTTP error code when the redirect is applied. Valid codes are 4XX or 5XX..", + }, + "key_prefix_equals": { + Type: schema.TypeString, + Optional: true, + Description: "The object key name prefix when the redirect is applied..", + }, + }, + }, + }, + "redirect": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Description: ".", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host_name": { + Type: schema.TypeString, + Optional: true, + Description: "The host name the request should be redirected to.", + }, + "http_redirect_code": { + Type: schema.TypeString, + Optional: true, + Description: "The HTTP redirect code to use on the response. Valid codes are 3XX except 300..", + }, + "protocol": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(s3.Protocol_Values(), false), + Description: "Protocol to be used in the Location header that is returned in the response.", + }, + "replace_key_prefix_with": { + Type: schema.TypeString, + Optional: true, + Description: "The prefix of the object key name that replaces the value of KeyPrefixEquals in the redirect request.", + }, + "replace_key_with": { + Type: schema.TypeString, + Optional: true, + Description: "The object key to be used in the Location header that is returned in the response.", + }, + }, + }, + }, + }, + }, + }, + "routing_rules": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Rules that define when a redirect is applied and the redirect behavior.", + ConflictsWith: []string{"website_configuration.0.routing_rule"}, + ValidateFunc: validation.StringIsJSON, + StateFunc: func(v interface{}) string { + json, _ := structure.NormalizeJsonString(v) + return json + }, + }, + }, + }, + }, + "website_endpoint": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func errorDocumentSetFunction(errorDocument []interface{}) *s3.ErrorDocument { + var error_document *s3.ErrorDocument + errorDocumentValue := s3.ErrorDocument{} + if len(errorDocument) != 0 { + errorDocumentMap := errorDocument[0].(map[string]interface{}) + if keyValue, ok := errorDocumentMap["key"].(string); ok && keyValue != "" { + errorDocumentValue.Key = aws.String(keyValue) + } + error_document = &errorDocumentValue + return error_document + } else { + return nil + } +} + +func indexDocumentSetFunction(indexDocument []interface{}) *s3.IndexDocument { + var index_document *s3.IndexDocument + indexDocumentValue := s3.IndexDocument{} + if len(indexDocument) != 0 { + indexDocumentMap, _ := indexDocument[0].(map[string]interface{}) + + if suffixValue, ok := indexDocumentMap["suffix"].(string); ok && suffixValue != "" { + indexDocumentValue.Suffix = aws.String(suffixValue) + } + index_document = &indexDocumentValue + return index_document + } else { + return nil + } +} + +func redirectAllRequestsSetFunction(redirectAllRequestsSet []interface{}) *s3.RedirectAllRequestsTo { + var redirect_all_requests *s3.RedirectAllRequestsTo + redirectAllRequestsValue := s3.RedirectAllRequestsTo{} + if len(redirectAllRequestsSet) != 0 { + redirectAllRequestsMap, _ := redirectAllRequestsSet[0].(map[string]interface{}) + if hostName, ok := redirectAllRequestsMap["host_name"].(string); ok && hostName != "" { + redirectAllRequestsValue.HostName = aws.String(hostName) + } + if protocol, ok := redirectAllRequestsMap["protocol"].(string); ok && protocol != "" { + redirectAllRequestsValue.Protocol = aws.String(protocol) + } + redirect_all_requests = &redirectAllRequestsValue + return redirect_all_requests + } else { + return nil + } +} + +func routingRouteConditionSet(routingRuleConditionSet []interface{}) *s3.Condition { + var routingRuleCondition *s3.Condition + conditionValue := s3.Condition{} + if len(routingRuleConditionSet) != 0 { + routingRuleConditionMap, _ := routingRuleConditionSet[0].(map[string]interface{}) + if httpErrorCodeReturnedEquals, ok := routingRuleConditionMap["http_error_code_returned_equals"].(string); ok && httpErrorCodeReturnedEquals != "" { + conditionValue.HttpErrorCodeReturnedEquals = aws.String(httpErrorCodeReturnedEquals) + } + if keyPrefixEquals, ok := routingRuleConditionMap["key_prefix_equals"].(string); ok && keyPrefixEquals != "" { + conditionValue.KeyPrefixEquals = aws.String(keyPrefixEquals) + } + routingRuleCondition = &conditionValue + return routingRuleCondition + } else { + return nil + } +} + +func routingRouteRedirectSet(routingRuleRedirectSet []interface{}) *s3.Redirect { + var routingRuleRedirect *s3.Redirect + redirectValue := s3.Redirect{} + if len(routingRuleRedirectSet) != 0 { + routingRuleRedirectMap, _ := routingRuleRedirectSet[0].(map[string]interface{}) + if hostName, ok := routingRuleRedirectMap["host_name"].(string); ok && hostName != "" { + redirectValue.HostName = aws.String(hostName) + } + if httpRedirectCode, ok := routingRuleRedirectMap["http_redirect_code"].(string); ok && httpRedirectCode != "" { + redirectValue.HttpRedirectCode = aws.String(httpRedirectCode) + } + if protocol, ok := routingRuleRedirectMap["protocol"].(string); ok && protocol != "" { + redirectValue.Protocol = aws.String(protocol) + } + if replaceKeyPrefixWith, ok := routingRuleRedirectMap["replace_key_prefix_with"].(string); ok && replaceKeyPrefixWith != "" { + redirectValue.ReplaceKeyPrefixWith = aws.String(replaceKeyPrefixWith) + } + if replaceKeyWith, ok := routingRuleRedirectMap["replace_key_with"].(string); ok && replaceKeyWith != "" { + redirectValue.ReplaceKeyWith = aws.String(replaceKeyWith) + } + routingRuleRedirect = &redirectValue + return routingRuleRedirect + } else { + return nil + } +} + +func routingRuleSetFunction(routingRuleSet []interface{}) []*s3.RoutingRule { + var rules []*s3.RoutingRule + for _, l := range routingRuleSet { + ruleMap, ok := l.(map[string]interface{}) + if !ok { + continue + } + routing_rule := s3.RoutingRule{} + if condition, ok := ruleMap["condition"].([]interface{}); ok && len(condition) > 0 && condition[0] != nil { + routing_rule.Condition = routingRouteConditionSet(condition) + } + if redirect, ok := ruleMap["redirect"].([]interface{}); ok && len(redirect) > 0 && redirect[0] != nil { + routing_rule.Redirect = routingRouteRedirectSet(redirect) + } + rules = append(rules, &routing_rule) + } + return rules +} + +func websiteConfigurationSet(websiteConfigurationList []interface{}) (*s3.WebsiteConfiguration, error) { + var websiteConfig *s3.WebsiteConfiguration + website_configuration := s3.WebsiteConfiguration{} + configurationMap, _ := websiteConfigurationList[0].(map[string]interface{}) + // error document is provided + if errorDocumentSet, exist := configurationMap["error_document"]; exist { + website_configuration.ErrorDocument = errorDocumentSetFunction(errorDocumentSet.([]interface{})) + } + //index document is provided + if indexDocumentSet, exist := configurationMap["index_document"]; exist { + website_configuration.IndexDocument = indexDocumentSetFunction(indexDocumentSet.([]interface{})) + } + //redirect_all_requests_to is provided + if redirectAllRequestsSet, exist := configurationMap["redirect_all_requests_to"]; exist { + + website_configuration.RedirectAllRequestsTo = redirectAllRequestsSetFunction(redirectAllRequestsSet.([]interface{})) + } + // routing_rules provided + if routingRulesSet, exist := configurationMap["routing_rule"]; exist { + website_configuration.RoutingRules = routingRuleSetFunction(routingRulesSet.([]interface{})) + } + // if json routing routes are provided + if routingRulesJsonSet, exist := configurationMap["routing_rules"]; exist { + jsonValue := fmt.Sprint(configurationMap["routing_rules"]) + if jsonValue != "" { + var unmarshalledRules []*s3.RoutingRule + if err := json.Unmarshal([]byte(routingRulesJsonSet.(string)), &unmarshalledRules); err != nil { + return nil, fmt.Errorf("failed to update the json routing rules in the website configuration : %v", err) + } + fmt.Println("unmarshal rules : ", unmarshalledRules) + website_configuration.RoutingRules = unmarshalledRules + } + } + websiteConfig = &website_configuration + return websiteConfig, nil +} + +func resourceIBMCOSBucketWebsiteConfigurationCreate(d *schema.ResourceData, meta interface{}) error { + bucketCRN := d.Get("bucket_crn").(string) + bucketName := strings.Split(bucketCRN, ":bucket:")[1] + instanceCRN := fmt.Sprintf("%s::", strings.Split(bucketCRN, ":bucket:")[0]) + bucketLocation := d.Get("bucket_location").(string) + endpointType := d.Get("endpoint_type").(string) + bxSession, err := meta.(conns.ClientSession).BluemixSession() + if err != nil { + return err + } + s3Client, err := getS3ClientSession(bxSession, bucketLocation, endpointType, instanceCRN) + var websiteConfiguration *s3.WebsiteConfiguration + configuration, ok := d.GetOk("website_configuration") + if ok { + websiteConfiguration, err = websiteConfigurationSet(configuration.([]interface{})) + } + if err != nil { + return fmt.Errorf("failed to read website configuration for COS bucket %s, %v", bucketName, err) + } + putBucketWebsiteConfigurationInput := s3.PutBucketWebsiteInput{ + Bucket: aws.String(bucketName), + WebsiteConfiguration: websiteConfiguration, + } + _, err = s3Client.PutBucketWebsite(&putBucketWebsiteConfigurationInput) + + if err != nil { + return fmt.Errorf("failed to put website configuration on the COS bucket %s, %v", bucketName, err) + } + bktID := fmt.Sprintf("%s:%s:%s:meta:%s:%s", strings.Replace(instanceCRN, "::", "", -1), "bucket", bucketName, bucketLocation, endpointType) + d.SetId(bktID) + return resourceIBMCOSBucketWebsiteConfigurationUpdate(d, meta) +} + +func resourceIBMCOSBucketWebsiteConfigurationUpdate(d *schema.ResourceData, meta interface{}) error { + bucketCRN := d.Get("bucket_crn").(string) + bucketName := strings.Split(bucketCRN, ":bucket:")[1] + instanceCRN := fmt.Sprintf("%s::", strings.Split(bucketCRN, ":bucket:")[0]) + bucketLocation := d.Get("bucket_location").(string) + endpointType := d.Get("endpoint_type").(string) + bxSession, err := meta.(conns.ClientSession).BluemixSession() + if err != nil { + return err + } + s3Client, err := getS3ClientSession(bxSession, bucketLocation, endpointType, instanceCRN) + if err != nil { + return err + } + if d.HasChange("website_configuration") { + var websiteConfiguration *s3.WebsiteConfiguration + configuration, ok := d.GetOk("website_configuration") + if ok { + websiteConfiguration, err = websiteConfigurationSet(configuration.([]interface{})) + + } + if err != nil { + return fmt.Errorf("failed to read website configuration for COS bucket %s, %v", bucketName, err) + } + putBucketWebsiteConfigurationInput := s3.PutBucketWebsiteInput{ + Bucket: aws.String(bucketName), + WebsiteConfiguration: websiteConfiguration, + } + _, err = s3Client.PutBucketWebsite(&putBucketWebsiteConfigurationInput) + + if err != nil { + return fmt.Errorf("failed to update website configuration on the COS bucket %s, %v", bucketName, err) + } + } + return resourceIBMCOSBucketWebsiteConfigurationRead(d, meta) +} + +func resourceIBMCOSBucketWebsiteConfigurationRead(d *schema.ResourceData, meta interface{}) error { + bucketCRN := parseWebsiteId(d.Id(), "bucketCRN") + bucketName := parseWebsiteId(d.Id(), "bucketName") + bucketLocation := parseWebsiteId(d.Id(), "bucketLocation") + instanceCRN := parseWebsiteId(d.Id(), "instanceCRN") + endpointType := parseWebsiteId(d.Id(), "endpointType") + d.Set("bucket_crn", bucketCRN) + d.Set("bucket_location", bucketLocation) + if endpointType != "" { + d.Set("endpoint_type", endpointType) + } + bxSession, err := meta.(conns.ClientSession).BluemixSession() + if err != nil { + return err + } + s3Client, err := getS3ClientSession(bxSession, bucketLocation, endpointType, instanceCRN) + if err != nil { + return err + } + //getBucketConfiguration + getBucketWebsiteConfigurationInput := &s3.GetBucketWebsiteInput{ + Bucket: aws.String(bucketName), + } + output, err := s3Client.GetBucketWebsite(getBucketWebsiteConfigurationInput) + var outputptr *s3.WebsiteConfiguration + outputptr = (*s3.WebsiteConfiguration)(output) + if err != nil && !strings.Contains(err.Error(), "AccessDenied: Access Denied") { + return err + } + if output != nil { + websiteConfiguration := flex.WebsiteConfigurationGet(outputptr) + if len(websiteConfiguration) > 0 { + d.Set("website_configuration", websiteConfiguration) + } + websiteEndpoint := getWebsiteEndpoint(bucketName, bucketLocation) + if websiteEndpoint != "" { + d.Set("website_endpoint", websiteEndpoint) + } + } + return nil +} + +func resourceIBMCOSBucketWebsiteConfigurationDelete(d *schema.ResourceData, meta interface{}) error { + bucketName := parseWebsiteId(d.Id(), "bucketName") + bucketLocation := parseWebsiteId(d.Id(), "bucketLocation") + instanceCRN := parseWebsiteId(d.Id(), "instanceCRN") + endpointType := parseWebsiteId(d.Id(), "endpointType") + bxSession, err := meta.(conns.ClientSession).BluemixSession() + if err != nil { + return err + } + s3Client, err := getS3ClientSession(bxSession, bucketLocation, endpointType, instanceCRN) + if err != nil { + return err + } + deleteBucketWebsiteInput := &s3.DeleteBucketWebsiteInput{ + Bucket: aws.String(bucketName), + } + _, err = s3Client.DeleteBucketWebsite(deleteBucketWebsiteInput) + if err != nil { + return fmt.Errorf("failed to delete the objectlock configuration on the COS bucket %s, %v", bucketName, err) + } + return nil +} + +func parseWebsiteId(id string, info string) string { + bucketCRN := strings.Split(id, ":meta:")[0] + meta := strings.Split(id, ":meta:")[1] + if info == "bucketName" { + return strings.Split(bucketCRN, ":bucket:")[1] + } + if info == "instanceCRN" { + return fmt.Sprintf("%s::", strings.Split(bucketCRN, ":bucket:")[0]) + } + if info == "bucketCRN" { + return bucketCRN + } + if info == "bucketLocation" { + return strings.Split(meta, ":")[0] + } + if info == "endpointType" { + return strings.Split(meta, ":")[1] + } + if info == "keyName" { + return strings.Split(meta, ":key:")[1] + } + return parseBucketId(bucketCRN, info) +} + +func getWebsiteEndpoint(bucketName string, bucketLocation string) string { + return fmt.Sprintf("https://%s.s3-web.%s.cloud-object-storage.appdomain.cloud", bucketName, bucketLocation) +} diff --git a/ibm/service/cos/resource_ibm_cos_bucket_website_configuration_test.go b/ibm/service/cos/resource_ibm_cos_bucket_website_configuration_test.go new file mode 100644 index 0000000000..5959dc2c0b --- /dev/null +++ b/ibm/service/cos/resource_ibm_cos_bucket_website_configuration_test.go @@ -0,0 +1,1004 @@ +package cos_test + +import ( + "fmt" + "regexp" + "testing" + + acc "github.com/IBM-Cloud/terraform-provider-ibm/ibm/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccIBMCosBucket_Website_Configuration_Bucket_Basic(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraformStaticWebHosting%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + indexSuffix := "index.html" + errorKey := "error.html" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Website_Configuration_Bucket_Basic(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, indexSuffix, errorKey), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMCosBucketExists("ibm_resource_instance.instance", "ibm_cos_bucket.bucket", bucketRegionType, bucketRegion, bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "cross_region_location", bucketRegion), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.error_document.0.key", errorKey), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.index_document.0.suffix", indexSuffix), + ), + }, + }, + }) +} + +func TestAccIBMCosBucket_Website_Configuration_Bucket_Without_Public_Access(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraformStaticWebHosting%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + indexSuffix := "index.html" + errorKey := "error.html" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Website_Configuration_Bucket_Without_Public_Access(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, indexSuffix, errorKey), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMCosBucketExists("ibm_resource_instance.instance", "ibm_cos_bucket.bucket", bucketRegionType, bucketRegion, bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "cross_region_location", bucketRegion), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.error_document.0.key", errorKey), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.index_document.0.suffix", indexSuffix), + ), + }, + }, + }) +} +func TestAccIBMCosBucket_Website_Configuration_Bucket_Index_Document_Only(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraformStaticWebHosting%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + indexSuffix := "index.html" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Website_Configuration_Bucket_Index_Document_Only(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, indexSuffix), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMCosBucketExists("ibm_resource_instance.instance", "ibm_cos_bucket.bucket", bucketRegionType, bucketRegion, bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "cross_region_location", bucketRegion), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.index_document.0.suffix", indexSuffix), + ), + }, + }, + }) +} + +func TestAccIBMCosBucket_Website_Configuration_Bucket_With_Routing_Rule(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraformStaticWebHosting%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + indexSuffix := "index.html" + errorKey := "error.html" + hostName := fmt.Sprintf(bucketName, ".s3-web.us-south.cloud-object-storage.appdomain.cloud") + protocol := "https" + routingRulehttpRedirectCode := "302" + routingRuleReplaceKeyWith := "error404.html" + routingRulehttpErrorCodeReturnedEquals := "404" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Website_Configuration_Bucket_With_Routing_Rule(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, indexSuffix, errorKey, routingRulehttpErrorCodeReturnedEquals, hostName, protocol, routingRulehttpRedirectCode, routingRuleReplaceKeyWith), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMCosBucketExists("ibm_resource_instance.instance", "ibm_cos_bucket.bucket", bucketRegionType, bucketRegion, bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "cross_region_location", bucketRegion), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.routing_rule.0.condition.0.http_error_code_returned_equals", routingRulehttpErrorCodeReturnedEquals), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.routing_rule.0.redirect.0.host_name", hostName), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.routing_rule.0.redirect.0.protocol", protocol), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.routing_rule.0.redirect.0.http_redirect_code", routingRulehttpRedirectCode), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.routing_rule.0.redirect.0.replace_key_with", routingRuleReplaceKeyWith), + ), + }, + }, + }) +} + +func TestAccIBMCosBucket_Website_Configuration_Bucket_With_JSON_Routing_Rule(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraformStaticWebHosting%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + routingRuleKeyPrefixEquals := "docs/" + indexSuffix := "index.html" + errorKey := "error.html" + routingRuleReplaceKeyPrefixWith := "documents/" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Website_Configuration_Bucket_With_JSON_Routing_Rule(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, indexSuffix, errorKey), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMCosBucketExists("ibm_resource_instance.instance", "ibm_cos_bucket.bucket", bucketRegionType, bucketRegion, bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "cross_region_location", bucketRegion), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.routing_rule.0.condition.0.key_prefix_equals", routingRuleKeyPrefixEquals), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.routing_rule.0.condition.0.key_prefix_equals", routingRuleReplaceKeyPrefixWith), + ), + }, + }, + }) +} +func TestAccIBMCosBucket_Website_Configuration_Bucket_With_Routing_Rule_Condition_Only(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraformStaticWebHosting%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + indexSuffix := "index.html" + errorKey := "error.html" + routingRulehttpErrorCodeReturnedEquals := "404" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Website_Configuration_Bucket_With_Routing_Rule_Condition_Only(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, indexSuffix, errorKey, routingRulehttpErrorCodeReturnedEquals), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMCosBucketExists("ibm_resource_instance.instance", "ibm_cos_bucket.bucket", bucketRegionType, bucketRegion, bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "cross_region_location", bucketRegion), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.routing_rule.0.condition.0.http_error_code_returned_equals", routingRulehttpErrorCodeReturnedEquals), + ), + }, + }, + }) +} + +func TestAccIBMCosBucket_Website_Configuration_Bucket_With_Routing_Rule_Redirect_Only(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraformStaticWebHosting%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + indexSuffix := "index.html" + errorKey := "error.html" + hostName := fmt.Sprintf(bucketName, ".s3-web.us-south.cloud-object-storage.appdomain.cloud") + protocol := "https" + routingRuleHttpRedirectCode := "302" + routingRuleReplaceKeyWith := "error404.html" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Website_Configuration_Bucket_With_Routing_Rule_Redirect_Only(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, indexSuffix, errorKey, hostName, protocol, routingRuleHttpRedirectCode, routingRuleReplaceKeyWith), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMCosBucketExists("ibm_resource_instance.instance", "ibm_cos_bucket.bucket", bucketRegionType, bucketRegion, bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "cross_region_location", bucketRegion), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.routing_rule.0.redirect.0.host_name", hostName), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.routing_rule.0.redirect.0.protocol", protocol), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.routing_rule.0.redirect.0.http_redirect_code", routingRuleHttpRedirectCode), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.routing_rule.0.redirect.0.replace_key_with", routingRuleReplaceKeyWith), + ), + }, + }, + }) +} + +func TestAccIBMCosBucket_Website_Configuration_Bucket_With_Multiple_Routing_Rules(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraformStaticWebHosting%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + indexSuffix := "index.html" + errorKey := "error.html" + hostName := fmt.Sprintf(bucketName, ".s3-web.us-south.cloud-object-storage.appdomain.cloud") + protocol := "https" + routingRuleHttpRedirectCode := "302" + routingRuleReplaceKeyWith := "error404.html" + routingRuleHttpErrorCodeReturnedEquals := "404" + routingRuleHttpRedirectCode2 := "303" + routingRuleReplaceKeyWith2 := "error405.html" + routingRuleHttpErrorCodeReturnedEquals2 := "405" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Website_Configuration_Bucket_With_Multiple_Routing_Rule(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, indexSuffix, errorKey, routingRuleHttpErrorCodeReturnedEquals, hostName, protocol, routingRuleHttpRedirectCode, routingRuleReplaceKeyWith, routingRuleHttpErrorCodeReturnedEquals2, routingRuleHttpRedirectCode2, routingRuleReplaceKeyWith2), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMCosBucketExists("ibm_resource_instance.instance", "ibm_cos_bucket.bucket", bucketRegionType, bucketRegion, bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "cross_region_location", bucketRegion), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.routing_rule.#", "2"), + ), + }, + }, + }) +} + +func TestAccIBMCosBucket_Website_Configuration_Bucket_Upload_Object_With_Redirect(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraformStaticWebHosting%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + indexSuffix := "index.html" + websiteRedirectLocation := "/redirect" + key := "key1" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Website_Configuration_Bucket_Upload_Object_With_Redirect(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, indexSuffix, key, websiteRedirectLocation), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMCosBucketExists("ibm_resource_instance.instance", "ibm_cos_bucket.bucket", bucketRegionType, bucketRegion, bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "cross_region_location", bucketRegion), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket_website_configuration.website", "website_configuration.0.index_document.0.suffix", indexSuffix), + resource.TestCheckResourceAttr("ibm_cos_bucket_object.object", "website_redirect", websiteRedirectLocation), + ), + }, + }, + }) +} + +func TestAccIBMCosBucket_Website_Configuration_Bucket_Empty_Config(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraformStaticWebHosting%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Website_Configuration_Bucket_Empty(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass), + ExpectError: regexp.MustCompile("Error: failed to put website configuration on the COS bucket"), + }, + }, + }) +} + +func TestAccIBMCosBucket_Website_Configuration_Bucket_Index_And_Redirect_Together(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraformStaticWebHosting%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + indexSuffix := "index.html" + hostName := fmt.Sprintf(bucketName, ".s3-web.us-south.cloud-object-storage.appdomain.cloud") + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Website_Configuration_Bucket_Index_And_Redirect_Together(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, indexSuffix, hostName), + ExpectError: regexp.MustCompile("Error: Conflicting configuration arguments"), + }, + }, + }) +} + +func TestAccIBMCosBucket_Website_Configuration_Bucket_No_Config(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraformStaticWebHosting%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Website_Configuration_Bucket_No_Config(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass), + ExpectError: regexp.MustCompile("Error: Insufficient website_configuration blocks"), + }, + }, + }) +} + +func testAccCheckIBMCosBucket_Website_Configuration_Bucket_Basic(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, indexSuffix string, errorKey string) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + } + data "ibm_iam_access_group" "public_access_group" { + access_group_name = "Public Access" + } + + resource "ibm_iam_access_group_policy" "policy" { + depends_on = [ibm_cos_bucket.bucket] + access_group_id = data.ibm_iam_access_group.public_access_group.groups[0].id + roles = ["Object Reader"] + resources { + service = "cloud-object-storage" + resource_type = "bucket" + resource_instance_id = "%s" + resource = "%s" + } + } + + resource ibm_cos_bucket_website_configuration "website" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + website_configuration { + error_document{ + key = "%s" + } + index_document{ + suffix = "%s" + } + } + } + + `, cosServiceName, bucketName, region, storageClass, cosServiceName, bucketName, errorKey, indexSuffix) +} + +func testAccCheckIBMCosBucket_Website_Configuration_Bucket_Index_Document_Only(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, indexSuffix string) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + } + data "ibm_iam_access_group" "public_access_group" { + access_group_name = "Public Access" + } + + resource "ibm_iam_access_group_policy" "policy" { + depends_on = [ibm_cos_bucket.bucket] + access_group_id = data.ibm_iam_access_group.public_access_group.groups[0].id + roles = ["Object Reader"] + resources { + service = "cloud-object-storage" + resource_type = "bucket" + resource_instance_id = "%s" + resource = "%s" + } + } + + resource ibm_cos_bucket_website_configuration "website" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + website_configuration { + index_document{ + suffix = "%s" + } + } + } + + `, cosServiceName, bucketName, region, storageClass, cosServiceName, bucketName, indexSuffix) +} + +func testAccCheckIBMCosBucket_Website_Configuration_Bucket_With_Routing_Rule(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, indexSuffix string, errorKey string, httpErrorCodeReturnedEquals string, hostName string, protocol string, httpsRedirectCode string, replaceKeyWith string) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + } + data "ibm_iam_access_group" "public_access_group" { + access_group_name = "Public Access" + } + + resource "ibm_iam_access_group_policy" "policy" { + depends_on = [ibm_cos_bucket.bucket] + access_group_id = data.ibm_iam_access_group.public_access_group.groups[0].id + roles = ["Object Reader"] + resources { + service = "cloud-object-storage" + resource_type = "bucket" + resource_instance_id = "%s" + resource = "%s" + } + } + + resource ibm_cos_bucket_website_configuration "website" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + website_configuration { + error_document{ + key = "%s" + } + index_document{ + suffix = "%s" + } + routing_rule{ + condition{ + http_error_code_returned_equals= "%s" + } + redirect{ + host_name= "%s" + http_redirect_code= "%s" + protocol = "%s" + replace_key_with = "%s" + } + } + } + } + + `, cosServiceName, bucketName, region, storageClass, cosServiceName, bucketName, errorKey, indexSuffix, httpErrorCodeReturnedEquals, hostName, httpsRedirectCode, protocol, replaceKeyWith) +} + +func testAccCheckIBMCosBucket_Website_Configuration_Bucket_With_JSON_Routing_Rule(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, indexSuffix string, errorKey string) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + } + data "ibm_iam_access_group" "public_access_group" { + access_group_name = "Public Access" + } + + resource "ibm_iam_access_group_policy" "policy" { + depends_on = [ibm_cos_bucket.bucket] + access_group_id = data.ibm_iam_access_group.public_access_group.groups[0].id + roles = ["Object Reader"] + resources { + service = "cloud-object-storage" + resource_type = "bucket" + resource_instance_id = "%s" + resource = "%s" + } + } + + resource ibm_cos_bucket_website_configuration "website" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + website_configuration { + error_document{ + key = "%s" + } + index_document{ + suffix = "%s" + } + routing_rules = <