From 1b92964074f5c2f1a9a2cf8c3f628cffc149ee17 Mon Sep 17 00:00:00 2001 From: Ray Krishardi Layadi Date: Fri, 8 Dec 2023 16:48:45 +1100 Subject: [PATCH] feat: s3 website (#26) --- s3.cfndsl.rb | 41 +++++++++++++++++++ ...website_redirect_to_another_bucket_spec.rb | 39 ++++++++++++++++++ spec/website_spec.rb | 39 ++++++++++++++++++ tests/website.test.yaml | 26 ++++++++++++ ...bsite_redirect_to_another_bucket.test.yaml | 13 ++++++ 5 files changed, 158 insertions(+) create mode 100644 spec/website_redirect_to_another_bucket_spec.rb create mode 100644 spec/website_spec.rb create mode 100644 tests/website.test.yaml create mode 100644 tests/website_redirect_to_another_bucket.test.yaml diff --git a/s3.cfndsl.rb b/s3.cfndsl.rb index 6e4ab06..cb92e8f 100644 --- a/s3.cfndsl.rb +++ b/s3.cfndsl.rb @@ -61,6 +61,46 @@ ownership_controls_rules.append(ownership_control_rule) end + # s3 website + website_configuration = {} + website = config.has_key?('website') ? config['website'] : {} + if !website.empty? + if website['redirect_requests'] + website_configuration['RedirectAllRequestsTo'] = { + 'HostName': website['redirect_hostname'], + 'Protocol': website['redirect_protocol'] + } + else + website_configuration['ErrorDocument'] = website['error_document'] + website_configuration['IndexDocument'] = website['index_document'] + + if website.has_key?('routing_rules') + routing_rules = [] + website['routing_rules'].each do |rule| + routing_rule = {} + + # Redirect rule + routing_rule['RedirectRule'] = {} + routing_rule['RedirectRule']['HostName'] = rule['redirect_rule']['hostname'] if rule['redirect_rule'].has_key?('hostname') + routing_rule['RedirectRule']['HttpRedirectCode'] = rule['redirect_rule']['http_redirect_code'] if rule['redirect_rule'].has_key?('http_redirect_code') + routing_rule['RedirectRule']['Protocol'] = rule['redirect_rule']['protocol'] if rule['redirect_rule'].has_key?('protocol') + + # ReplaceKeyPrefixWith and ReplaceKeyWith cannot be specified together (can only be 1 or the other) + routing_rule['RedirectRule']['ReplaceKeyPrefixWith'] = rule['redirect_rule']['replace_key_prefix_with'] if rule['redirect_rule'].has_key?('replace_key_prefix_with') and !rule['redirect_rule'].has_key?('replace_key_with') + routing_rule['RedirectRule']['ReplaceKeyWith'] = rule['redirect_rule']['replace_key_with'] if rule['redirect_rule'].has_key?('replace_key_with') and !rule['redirect_rule'].has_key?('replace_key_prefix_with') + + # Routing rule condition + routing_rule['RoutingRuleCondition'] = {} + routing_rule['RoutingRuleCondition']['HttpErrorCodeReturnedEquals'] = rule['routing_rule_condition']['http_error_code_returned_equals'] if rule['routing_rule_condition'].has_key?('http_error_code_returned_equals') + routing_rule['RoutingRuleCondition']['KeyPrefixEquals'] = rule['routing_rule_condition']['key_prefix_equals'] if rule['routing_rule_condition'].has_key?('key_prefix_equals') + + routing_rules.append(routing_rule) + end + website_configuration['RoutingRules'] = routing_rules if !routing_rules.empty? + end + end + end + if bucket_type == 'create_if_not_exists' Resource("#{safe_bucket_name}") do Type 'Custom::S3BucketCreateOnly' @@ -98,6 +138,7 @@ OwnershipControls ({ Rules: ownership_controls_rules }) if !ownership_controls_rules.empty? + WebsiteConfiguration website_configuration if !website_configuration.empty? end end diff --git a/spec/website_redirect_to_another_bucket_spec.rb b/spec/website_redirect_to_another_bucket_spec.rb new file mode 100644 index 0000000..2361fe1 --- /dev/null +++ b/spec/website_redirect_to_another_bucket_spec.rb @@ -0,0 +1,39 @@ +require 'yaml' + +describe 'compiled component s3' do + + context 'cftest' do + it 'compiles test' do + expect(system("cfhighlander cftest #{@validate} --tests tests/website_redirect_to_another_bucket.test.yaml")).to be_truthy + end + end + + let(:template) { YAML.load_file("#{File.dirname(__FILE__)}/../out/tests/website_redirect_to_another_bucket/s3.compiled.yaml") } + + context "Resource" do + + + context "Normalbucket" do + let(:resource) { template["Resources"]["Normalbucket"] } + + it "is of type AWS::S3::Bucket" do + expect(resource["Type"]).to eq("AWS::S3::Bucket") + end + + it "to have property BucketName" do + expect(resource["Properties"]["BucketName"]).to eq({"Fn::Sub"=>"normal-bucket"}) + end + + it "to have property Tags" do + expect(resource["Properties"]["Tags"]).to eq([{"Key"=>"Name", "Value"=>{"Fn::Sub"=>"${EnvironmentName}-normal-bucket"}}, {"Key"=>"Environment", "Value"=>{"Ref"=>"EnvironmentName"}}, {"Key"=>"EnvironmentType", "Value"=>{"Ref"=>"EnvironmentType"}}]) + end + + it "to have property WebsiteConfiguration" do + expect(resource["Properties"]["WebsiteConfiguration"]).to eq({"RedirectAllRequestsTo"=>{"HostName"=>"testbucket", "Protocol"=>"http"}}) + end + + end + + end + +end \ No newline at end of file diff --git a/spec/website_spec.rb b/spec/website_spec.rb new file mode 100644 index 0000000..79a4301 --- /dev/null +++ b/spec/website_spec.rb @@ -0,0 +1,39 @@ +require 'yaml' + +describe 'compiled component s3' do + + context 'cftest' do + it 'compiles test' do + expect(system("cfhighlander cftest #{@validate} --tests tests/website.test.yaml")).to be_truthy + end + end + + let(:template) { YAML.load_file("#{File.dirname(__FILE__)}/../out/tests/website/s3.compiled.yaml") } + + context "Resource" do + + + context "Normalbucket" do + let(:resource) { template["Resources"]["Normalbucket"] } + + it "is of type AWS::S3::Bucket" do + expect(resource["Type"]).to eq("AWS::S3::Bucket") + end + + it "to have property BucketName" do + expect(resource["Properties"]["BucketName"]).to eq({"Fn::Sub"=>"normal-bucket"}) + end + + it "to have property Tags" do + expect(resource["Properties"]["Tags"]).to eq([{"Key"=>"Name", "Value"=>{"Fn::Sub"=>"${EnvironmentName}-normal-bucket"}}, {"Key"=>"Environment", "Value"=>{"Ref"=>"EnvironmentName"}}, {"Key"=>"EnvironmentType", "Value"=>{"Ref"=>"EnvironmentType"}}]) + end + + it "to have property WebsiteConfiguration" do + expect(resource["Properties"]["WebsiteConfiguration"]).to eq({"ErrorDocument"=>"error.html", "IndexDocument"=>"index.html", "RoutingRules"=>[{"RedirectRule"=>{"HostName"=>"test1", "HttpRedirectCode"=>"301", "Protocol"=>"http", "ReplaceKeyWith"=>"test1"}, "RoutingRuleCondition"=>{"HttpErrorCodeReturnedEquals"=>"400", "KeyPrefixEquals"=>"test1"}}, {"RedirectRule"=>{"ReplaceKeyPrefixWith"=>"documents/"}, "RoutingRuleCondition"=>{"KeyPrefixEquals"=>"docs/"}}]}) + end + + end + + end + +end \ No newline at end of file diff --git a/tests/website.test.yaml b/tests/website.test.yaml new file mode 100644 index 0000000..80a8678 --- /dev/null +++ b/tests/website.test.yaml @@ -0,0 +1,26 @@ +test_metadata: + type: config + name: website + description: s3 website with multiple routing rules + +# Insert your tests here +buckets: + normal-bucket: + type: default + website: + redirect_requests: false + error_document: error.html + index_document: index.html + routing_rules: + - redirect_rule: + hostname: "test1" + http_redirect_code: "301" + protocol: "http" + replace_key_with: "test1" + routing_rule_condition: + http_error_code_returned_equals: "400" + key_prefix_equals: "test1" + - redirect_rule: + replace_key_prefix_with: "documents/" + routing_rule_condition: + key_prefix_equals: "docs/" \ No newline at end of file diff --git a/tests/website_redirect_to_another_bucket.test.yaml b/tests/website_redirect_to_another_bucket.test.yaml new file mode 100644 index 0000000..96e56f1 --- /dev/null +++ b/tests/website_redirect_to_another_bucket.test.yaml @@ -0,0 +1,13 @@ +test_metadata: + type: config + name: website_redirect_to_another_bucket + description: s3 website that redirects to another s3 bucket + +# Insert your tests here +buckets: + normal-bucket: + type: default + website: + redirect_requests: true + redirect_hostname: "testbucket" + redirect_protocol: "http" \ No newline at end of file