From ac1833d1f68f38f206b6b610446d15c92aa29c9c Mon Sep 17 00:00:00 2001 From: Read Sprabery Date: Thu, 9 Jan 2014 11:52:26 -0600 Subject: [PATCH 1/8] Added redirect buckets and route53 autoconfig. --- configure-s3-website.gemspec | 1 + lib/configure-s3-website.rb | 1 + .../config_source/config_source.rb | 6 + .../config_source/file_config_source.rb | 8 + lib/configure-s3-website/route53_client.rb | 169 ++++++++++++++++++ lib/configure-s3-website/runner.rb | 8 + lib/configure-s3-website/s3_client.rb | 95 +++++++--- 7 files changed, 267 insertions(+), 21 deletions(-) create mode 100644 lib/configure-s3-website/route53_client.rb diff --git a/configure-s3-website.gemspec b/configure-s3-website.gemspec index a2afa76..c8958f4 100644 --- a/configure-s3-website.gemspec +++ b/configure-s3-website.gemspec @@ -10,6 +10,7 @@ spec = Gem::Specification.new do |s| s.bindir = 'bin' s.add_dependency 'deep_merge', '= 1.0.0' + s.add_dependency 'route53', '= 0.2.2' s.add_development_dependency 'rspec', '~> 2.10.0' s.add_development_dependency 'rspec-expectations', '~> 2.10.0' diff --git a/lib/configure-s3-website.rb b/lib/configure-s3-website.rb index d7b9f07..1042661 100644 --- a/lib/configure-s3-website.rb +++ b/lib/configure-s3-website.rb @@ -1,6 +1,7 @@ require 'configure-s3-website/version' require 'configure-s3-website/s3_client' require 'configure-s3-website/cloudfront_client' +require 'configure-s3-website/route53_client' require 'configure-s3-website/xml_helper' require 'configure-s3-website/http_helper' require 'configure-s3-website/runner' diff --git a/lib/configure-s3-website/config_source/config_source.rb b/lib/configure-s3-website/config_source/config_source.rb index 4b7aa9a..a892d47 100644 --- a/lib/configure-s3-website/config_source/config_source.rb +++ b/lib/configure-s3-website/config_source/config_source.rb @@ -26,5 +26,11 @@ def cloudfront_distribution_id def cloudfront_distribution_id=(dist_id) end + + def redirect_domains + end + + def route53_enabled + end end end diff --git a/lib/configure-s3-website/config_source/file_config_source.rb b/lib/configure-s3-website/config_source/file_config_source.rb index 844471a..0b9c16e 100644 --- a/lib/configure-s3-website/config_source/file_config_source.rb +++ b/lib/configure-s3-website/config_source/file_config_source.rb @@ -55,6 +55,14 @@ def cloudfront_distribution_id=(dist_id) end end + def redirect_domains + @config['redirect_domains'] + end + + def route53_enabled + @config['route53'] + end + private def self.parse_config(yaml_file_path) diff --git a/lib/configure-s3-website/route53_client.rb b/lib/configure-s3-website/route53_client.rb new file mode 100644 index 0000000..fb978ee --- /dev/null +++ b/lib/configure-s3-website/route53_client.rb @@ -0,0 +1,169 @@ +require 'route53' + +module ConfigureS3Website + class Route53Client + + def self.apply(options) + @config_source = options[:config_source] + + # Set the domain for the site + domain = get_domain_name @config_source.s3_bucket_name + + # Set up the connection to route53 and store in @conn + get_connection + + # Check to ensure that there is a hosted zone for the given domain + zone = check_and_create_hosted_zone_if_user_agrees domain + if not zone.nil? + + # Create a route for the main s3_bucket + check_and_create_route(@config_source.s3_bucket_name, zone) + + # Create routes for redirect urls + unless @config_source.redirect_domains.nil? + @config_source.redirect_domains.each do |url| + # check to see if the domain of the redirect_urls matches the domain of the main bucket (s3_bucket_name) + redirect_domain = get_domain_name url + if redirect_domain != domain + redirect_zone = check_and_create_hosted_zone_if_user_agrees redirect_domain + unless redirect_zone.nil? + # Just create the route here, there is no need to check if it exists because we just created the + # new zone. + create_route(url, redirect_zone) + end + else + # Check to see if the route exists and create route to the specific redirect_url + check_and_create_route(url, zone) + end + end + end + end + end + + private + + def self.get_connection + # Establish connection with amazon + @conn = Route53::Connection.new(@config_source.s3_access_key_id, @config_source.s3_secret_access_key) + end + + def self.check_and_create_route(url, zone) + if route_exists?(url, zone) + # Ask the user if he/she wants to delete & recreate the route + puts "A route already exists for #{url}" + puts 'Do you want to re-create the existing entry and point it to your s3 bucket/Cloud Front?[y/N]' + case gets.chomp + when /(y|Y)/ + create_route(url, zone) if remove_route(url, zone) + end + else + create_route(url, zone) + end + end + + def self.remove_route(url, zone) + records = zone.get_records + records = records.select {|rec| rec.name.include? url} + if records.length == 1 + domain_record = records[0] + domain_record.delete + true + else + puts "Unable to remove record for #{url}, please do it in the AWS Management console." + false + end + end + + def self.create_route(url, zone) + + if not @config_source.cloudfront_distribution_id.nil? and url == @config_source.s3_bucket_name + # Then this needs to point to Cloud front + # From http://docs.aws.amazon.com/Route53/latest/APIReference/API_ChangeResourceRecordSets.html#change-rrsets-request-hosted-zone-id + # and http://docs.aws.amazon.com/general/latest/gr/rande.html#cf_region + hosted_zone_id = 'Z2FDTNDATAQYW2' + redirect_url = 'cloudfront.amazonaws.com.' + else + # Get the location of the s3-bucket (need a URL for the domain redirect) + redirect_url, hosted_zone_id = S3Client.get_endpoint(@config_source, @config_source.s3_bucket_name) + end + new_record = Route53::DNSRecord.new(url, 'A', '', [redirect_url], zone, hosted_zone_id) + resp = new_record.create + if resp.error? + puts resp + else + puts "Route53 Entry created for: #{url} pointing to #{redirect_url}" + end + end + + def self.get_domain_name(bucket) + bucket_name = bucket + parts = bucket_name.split('.') + domain = "#{parts.last(2).join('.')}." + end + + def self.hosted_zone_exits?(domain) + # Check to see if the user has already created a hosted zone + + zone = get_zone domain + + not zone.nil? + end + + def self.get_zone(domain) + # Get an array of the user's zones (usually one per domain) + zones = @conn.get_zones + + # Try and find the zone for the domain of the current blog + zone = zones.select { |zone| zone.name == domain}[0] + end + + def self.check_and_create_hosted_zone_if_user_agrees(domain) + # Creates a hosted zone if user says sure + success = false + if not hosted_zone_exits?(domain) # We need to have the user create the zone first. + + puts "A hosted zone for #{domain} does not exist, create one now?[y/N]?" + case gets.chomp + when /(y|Y)/ + zone = Route53::Zone.new(domain, nil, @conn) + resp = zone.create_zone + if resp.error? + puts resp + success = false + else + while resp.pending? + sleep 1 # Wait for the response to finish so that we can create routes on the zone + end + success = true + end + else + success = false + end + else # the domain already exists + success = true + zone = get_zone domain + end + + unless success + puts "Please create a hosted zone for #{domain} at: \n" + + "https://console.aws.amazon.com/route53/home before \n" + + "trying to auto-configure route53." + end + + return zone + end + + def self.route_exists?(url, zone) + # checks to see if the route already is in DNS (maybe it has been set to something other than s3) + records = zone.get_records + records.each do |rec| + if rec.name.include? url + return true + end + end + # if it's not found, return false + false + end + + end +end diff --git a/lib/configure-s3-website/runner.rb b/lib/configure-s3-website/runner.rb index d53a444..22d8afb 100644 --- a/lib/configure-s3-website/runner.rb +++ b/lib/configure-s3-website/runner.rb @@ -3,6 +3,7 @@ class Runner def self.run(options, standard_input = STDIN) S3Client.configure_website options maybe_create_or_update_cloudfront options, standard_input + maybe_create_or_update_route53 options, standard_input end private @@ -18,6 +19,13 @@ def self.maybe_create_or_update_cloudfront(options, standard_input) end end + def self.maybe_create_or_update_route53(options, standard_input) + route53_enabled = options[:config_source].route53_enabled + unless route53_enabled.nil? or not route53_enabled + Route53Client.apply(options) + end + end + def self.user_already_has_cf_configured(options) config_source = options[:config_source] config_source.cloudfront_distribution_id diff --git a/lib/configure-s3-website/s3_client.rb b/lib/configure-s3-website/s3_client.rb index 82536fc..09dd9ed 100644 --- a/lib/configure-s3-website/s3_client.rb +++ b/lib/configure-s3-website/s3_client.rb @@ -12,6 +12,7 @@ def self.configure_website(options) enable_website_configuration(config_source) make_bucket_readable_to_everyone(config_source) configure_bucket_redirects(config_source) + configure_sub_domain_redirects(config_source) rescue NoSuchBucketError create_bucket(config_source) retry @@ -32,14 +33,63 @@ def self.enable_website_configuration(config_source) | HttpHelper.call_s3_api( - path = "/#{config_source.s3_bucket_name}/?website", - method = Net::HTTP::Put, - body = body, - config_source = config_source + path = "/#{config_source.s3_bucket_name}/?website", + method = Net::HTTP::Put, + body = body, + config_source = config_source ) puts "Bucket #{config_source.s3_bucket_name} now functions as a website" end + def self.configure_sub_domain_redirects(config_source) + # Create buckets for each sub domain + unless config_source.redirect_domains.nil? + config_source.redirect_domains.each do |domain| + begin + enable_website_domain_redirects(config_source, domain) + rescue NoSuchBucketError + create_bucket(config_source, domain) + retry + end + end + end + end + + def self.enable_website_domain_redirects(config_source, bucket) + body = %| + + + #{config_source.s3_bucket_name} + + + | + HttpHelper.call_s3_api( + path = "/#{bucket}/?website", + method = Net::HTTP::Put, + body = body, + config_source = config_source + ) + puts "Bucket #{bucket} now redirects to #{config_source.s3_bucket_name}" + end + + def self.get_endpoint(config_source, bucket) + # Need a reliable way to get the end point of existing buckets so that I + # can do proper redirects in Route53Client + + # NOTES: I was going to send a request and get back the endpoint for the bucket, + # but that can't be done with SOAP. May want to look at moving to a REST API. + # + # That is why I ended up just reading the endpoint from the config file as done + # in the create function below. In the future, we should query to find the endpoint + # for a specific bucket. + endpoint = Endpoint.new(config_source.s3_endpoint || '') + + # return the website endpoint of the location & the hosted_zone_id + website_endpoint = endpoint.location_constraints[endpoint.location_constraint][:website_endpoint] + hosted_zone_id = endpoint.location_constraints[endpoint.location_constraint][:hosted_zone_id] + return website_endpoint, hosted_zone_id + end + def self.make_bucket_readable_to_everyone(config_source) policy_json = %|{ "Version":"2008-10-17", @@ -99,7 +149,11 @@ def self.configure_bucket_redirects(config_source) end end - def self.create_bucket(config_source) + def self.create_bucket(config_source, alt_bucket_name=nil) + bucket_name = config_source.s3_bucket_name + unless alt_bucket_name.nil? + bucket_name = alt_bucket_name + end endpoint = Endpoint.new(config_source.s3_endpoint || '') body = if endpoint.region == 'US Standard' '' # The standard endpoint does not need a location constraint @@ -112,16 +166,16 @@ def self.create_bucket(config_source) end HttpHelper.call_s3_api( - path = "/#{config_source.s3_bucket_name}", + path = "/#{bucket_name}", method = Net::HTTP::Put, body = body, config_source = config_source ) puts "Created bucket %s in the %s Region" % - [ - config_source.s3_bucket_name, - endpoint.region - ] + [ + bucket_name, + endpoint.region + ] end end end @@ -133,8 +187,7 @@ class Endpoint attr_reader :region, :location_constraint, :hostname, :website_hostname def initialize(location_constraint) - raise InvalidS3LocationConstraintError unless - location_constraints.has_key?location_constraint + raise InvalidS3LocationConstraintError unless location_constraints.has_key? location_constraint @region = location_constraints.fetch(location_constraint)[:region] @hostname = location_constraints.fetch(location_constraint)[:endpoint] @website_hostname = location_constraints.fetch(location_constraint)[:website_endpoint] @@ -142,6 +195,7 @@ def initialize(location_constraint) end # http://docs.amazonwebservices.com/general/latest/gr/rande.html#s3_region + # Added hosted zone info too (needed for route53 redirects) def location_constraints eu_west_1_region = { :region => 'EU (Ireland)', @@ -150,15 +204,14 @@ def location_constraints } { - '' => { :region => 'US Standard', :endpoint => 's3.amazonaws.com', :website_endpoint => 's3-website-us-east-1.amazonaws.com' }, - 'us-west-2' => { :region => 'US West (Oregon)', :endpoint => 's3-us-west-2.amazonaws.com', :website_endpoint => 's3-website-us-west-2.amazonaws.com' }, - 'us-west-1' => { :region => 'US West (Northern California)', :endpoint => 's3-us-west-1.amazonaws.com', :website_endpoint => 's3-website-us-west-1.amazonaws.com' }, - 'EU' => eu_west_1_region, - 'eu-west-1' => eu_west_1_region, - 'ap-southeast-1' => { :region => 'Asia Pacific (Singapore)', :endpoint => 's3-ap-southeast-1.amazonaws.com', :website_endpoint => 's3-website-ap-southeast-1.amazonaws.com' }, - 'ap-southeast-2' => { :region => 'Asia Pacific (Sydney)', :endpoint => 's3-ap-southeast-2.amazonaws.com', :website_endpoint => 's3-website-ap-southeast-2.amazonaws.com' }, - 'ap-northeast-1' => { :region => 'Asia Pacific (Tokyo)', :endpoint => 's3-ap-northeast-1.amazonaws.com', :website_endpoint => 's3-website-ap-northeast-1.amazonaws.com' }, - 'sa-east-1' => { :region => 'South America (Sao Paulo)', :endpoint => 's3-sa-east-1.amazonaws.com', :website_endpoint => 's3-website-sa-east-1.amazonaws.com' } + '' => {:region => 'US Standard', :endpoint => 's3.amazonaws.com', :website_endpoint => 's3-website-us-east-1.amazonaws.com', :hosted_zone_id => 'Z3AQBSTGFYJSTF'}, + 'us-west-2' => {:region => 'US West (Oregon)', :endpoint => 's3-us-west-2.amazonaws.com', :website_endpoint => 's3-website-us-west-2.amazonaws.com', :hosted_zone_id => 'Z3BJ6K6RIION7M'}, + 'us-west-1' => {:region => 'US West (Northern California)', :endpoint => 's3-us-west-1.amazonaws.com', :website_endpoint => 's3-website-us-west-1.amazonaws.com', :hosted_zone_id => 'Z2F56UZL2M1ACD'}, + 'EU' => {:region => 'EU (Ireland)', :endpoint => 's3-eu-west-1.amazonaws.com', :website_endpoint => 's3-website-eu-west-1.amazonaws.com', :hosted_zone_id => 'Z1BKCTXD74EZP#'}, + 'ap-southeast-1' => {:region => 'Asia Pacific (Singapore)', :endpoint => 's3-ap-southeast-1.amazonaws.com', :website_endpoint => 's3-website-ap-southeast-1.amazonaws.com', :hosted_zone_id => 'Z3O0J2DXBE1FTB'}, + 'ap-southeast-2' => {:region => 'Asia Pacific (Sydney)', :endpoint => 's3-ap-southeast-2.amazonaws.com', :website_endpoint => 's3-website-ap-southeast-2.amazonaws.com', :hosted_zone_id => 'Z1WCIGYICN2BYD'}, + 'ap-northeast-1' => {:region => 'Asia Pacific (Tokyo)', :endpoint => 's3-ap-northeast-1.amazonaws.com', :website_endpoint => 's3-website-ap-northeast-1.amazonaws.com', :hosted_zone_id => 'Z2M4EHUR26P7ZW'}, + 'sa-east-1' => {:region => 'South America (Sao Paulo)', :endpoint => 's3-sa-east-1.amazonaws.com', :website_endpoint => 's3-website-sa-east-1.amazonaws.com', :hosted_zone_id => 'Z7KQH4QJS55SO'} } end From 5aa892e6eb6e32ee2c286952f7f55da72e3bdf4d Mon Sep 17 00:00:00 2001 From: Read Sprabery Date: Wed, 19 Mar 2014 20:13:09 -0500 Subject: [PATCH 2/8] Improved formatting --- lib/configure-s3-website/route53_client.rb | 8 ++-- lib/configure-s3-website/s3_client.rb | 56 +++++++++++----------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/lib/configure-s3-website/route53_client.rb b/lib/configure-s3-website/route53_client.rb index fb978ee..7ecf22f 100644 --- a/lib/configure-s3-website/route53_client.rb +++ b/lib/configure-s3-website/route53_client.rb @@ -48,7 +48,7 @@ def self.get_connection end def self.check_and_create_route(url, zone) - if route_exists?(url, zone) + if route_exists?(url, zone) # Ask the user if he/she wants to delete & recreate the route puts "A route already exists for #{url}" puts 'Do you want to re-create the existing entry and point it to your s3 bucket/Cloud Front?[y/N]' @@ -56,9 +56,9 @@ def self.check_and_create_route(url, zone) when /(y|Y)/ create_route(url, zone) if remove_route(url, zone) end - else - create_route(url, zone) - end + else + create_route(url, zone) + end end def self.remove_route(url, zone) diff --git a/lib/configure-s3-website/s3_client.rb b/lib/configure-s3-website/s3_client.rb index 09dd9ed..ee61d0d 100644 --- a/lib/configure-s3-website/s3_client.rb +++ b/lib/configure-s3-website/s3_client.rb @@ -33,26 +33,26 @@ def self.enable_website_configuration(config_source) | HttpHelper.call_s3_api( - path = "/#{config_source.s3_bucket_name}/?website", - method = Net::HTTP::Put, - body = body, - config_source = config_source + path = "/#{config_source.s3_bucket_name}/?website", + method = Net::HTTP::Put, + body = body, + config_source = config_source ) puts "Bucket #{config_source.s3_bucket_name} now functions as a website" end def self.configure_sub_domain_redirects(config_source) # Create buckets for each sub domain - unless config_source.redirect_domains.nil? - config_source.redirect_domains.each do |domain| - begin - enable_website_domain_redirects(config_source, domain) - rescue NoSuchBucketError - create_bucket(config_source, domain) - retry - end + unless config_source.redirect_domains.nil? + config_source.redirect_domains.each do |domain| + begin + enable_website_domain_redirects(config_source, domain) + rescue NoSuchBucketError + create_bucket(config_source, domain) + retry end end + end end def self.enable_website_domain_redirects(config_source, bucket) @@ -64,10 +64,10 @@ def self.enable_website_domain_redirects(config_source, bucket) | HttpHelper.call_s3_api( - path = "/#{bucket}/?website", - method = Net::HTTP::Put, - body = body, - config_source = config_source + path = "/#{bucket}/?website", + method = Net::HTTP::Put, + body = body, + config_source = config_source ) puts "Bucket #{bucket} now redirects to #{config_source.s3_bucket_name}" end @@ -172,10 +172,10 @@ def self.create_bucket(config_source, alt_bucket_name=nil) config_source = config_source ) puts "Created bucket %s in the %s Region" % - [ - bucket_name, - endpoint.region - ] + [ + bucket_name, + endpoint.region + ] end end end @@ -204,14 +204,14 @@ def location_constraints } { - '' => {:region => 'US Standard', :endpoint => 's3.amazonaws.com', :website_endpoint => 's3-website-us-east-1.amazonaws.com', :hosted_zone_id => 'Z3AQBSTGFYJSTF'}, - 'us-west-2' => {:region => 'US West (Oregon)', :endpoint => 's3-us-west-2.amazonaws.com', :website_endpoint => 's3-website-us-west-2.amazonaws.com', :hosted_zone_id => 'Z3BJ6K6RIION7M'}, - 'us-west-1' => {:region => 'US West (Northern California)', :endpoint => 's3-us-west-1.amazonaws.com', :website_endpoint => 's3-website-us-west-1.amazonaws.com', :hosted_zone_id => 'Z2F56UZL2M1ACD'}, - 'EU' => {:region => 'EU (Ireland)', :endpoint => 's3-eu-west-1.amazonaws.com', :website_endpoint => 's3-website-eu-west-1.amazonaws.com', :hosted_zone_id => 'Z1BKCTXD74EZP#'}, - 'ap-southeast-1' => {:region => 'Asia Pacific (Singapore)', :endpoint => 's3-ap-southeast-1.amazonaws.com', :website_endpoint => 's3-website-ap-southeast-1.amazonaws.com', :hosted_zone_id => 'Z3O0J2DXBE1FTB'}, - 'ap-southeast-2' => {:region => 'Asia Pacific (Sydney)', :endpoint => 's3-ap-southeast-2.amazonaws.com', :website_endpoint => 's3-website-ap-southeast-2.amazonaws.com', :hosted_zone_id => 'Z1WCIGYICN2BYD'}, - 'ap-northeast-1' => {:region => 'Asia Pacific (Tokyo)', :endpoint => 's3-ap-northeast-1.amazonaws.com', :website_endpoint => 's3-website-ap-northeast-1.amazonaws.com', :hosted_zone_id => 'Z2M4EHUR26P7ZW'}, - 'sa-east-1' => {:region => 'South America (Sao Paulo)', :endpoint => 's3-sa-east-1.amazonaws.com', :website_endpoint => 's3-website-sa-east-1.amazonaws.com', :hosted_zone_id => 'Z7KQH4QJS55SO'} + '' => {:region => 'US Standard', :endpoint => 's3.amazonaws.com', :website_endpoint => 's3-website-us-east-1.amazonaws.com', :hosted_zone_id => 'Z3AQBSTGFYJSTF'}, + 'us-west-2' => {:region => 'US West (Oregon)', :endpoint => 's3-us-west-2.amazonaws.com', :website_endpoint => 's3-website-us-west-2.amazonaws.com', :hosted_zone_id => 'Z3BJ6K6RIION7M'}, + 'us-west-1' => {:region => 'US West (Northern California)', :endpoint => 's3-us-west-1.amazonaws.com', :website_endpoint => 's3-website-us-west-1.amazonaws.com', :hosted_zone_id => 'Z2F56UZL2M1ACD'}, + 'EU' => {:region => 'EU (Ireland)', :endpoint => 's3-eu-west-1.amazonaws.com', :website_endpoint => 's3-website-eu-west-1.amazonaws.com', :hosted_zone_id => 'Z1BKCTXD74EZP#'}, + 'ap-southeast-1' => {:region => 'Asia Pacific (Singapore)', :endpoint => 's3-ap-southeast-1.amazonaws.com', :website_endpoint => 's3-website-ap-southeast-1.amazonaws.com', :hosted_zone_id => 'Z3O0J2DXBE1FTB'}, + 'ap-southeast-2' => {:region => 'Asia Pacific (Sydney)', :endpoint => 's3-ap-southeast-2.amazonaws.com', :website_endpoint => 's3-website-ap-southeast-2.amazonaws.com', :hosted_zone_id => 'Z1WCIGYICN2BYD'}, + 'ap-northeast-1' => {:region => 'Asia Pacific (Tokyo)', :endpoint => 's3-ap-northeast-1.amazonaws.com', :website_endpoint => 's3-website-ap-northeast-1.amazonaws.com', :hosted_zone_id => 'Z2M4EHUR26P7ZW'}, + 'sa-east-1' => {:region => 'South America (Sao Paulo)', :endpoint => 's3-sa-east-1.amazonaws.com', :website_endpoint => 's3-website-sa-east-1.amazonaws.com', :hosted_zone_id => 'Z7KQH4QJS55SO'} } end From 895b44ca229f83e9f3741e90ab31af5343c4cec9 Mon Sep 17 00:00:00 2001 From: Read Sprabery Date: Wed, 19 Mar 2014 20:32:40 -0500 Subject: [PATCH 3/8] Made Route53Client instantiable and refactored a long function. --- lib/configure-s3-website/route53_client.rb | 47 +++++++++++----------- lib/configure-s3-website/runner.rb | 3 +- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/lib/configure-s3-website/route53_client.rb b/lib/configure-s3-website/route53_client.rb index 7ecf22f..299645d 100644 --- a/lib/configure-s3-website/route53_client.rb +++ b/lib/configure-s3-website/route53_client.rb @@ -2,16 +2,18 @@ module ConfigureS3Website class Route53Client - - def self.apply(options) + def initialize(options) @config_source = options[:config_source] + # Set up the connection to route53 and store in @conn + @conn = Route53::Connection.new(@config_source.s3_access_key_id, @config_source.s3_secret_access_key) + end + + def apply + # Set the domain for the site domain = get_domain_name @config_source.s3_bucket_name - # Set up the connection to route53 and store in @conn - get_connection - # Check to ensure that there is a hosted zone for the given domain zone = check_and_create_hosted_zone_if_user_agrees domain if not zone.nil? @@ -21,30 +23,29 @@ def self.apply(options) # Create routes for redirect urls unless @config_source.redirect_domains.nil? - @config_source.redirect_domains.each do |url| - # check to see if the domain of the redirect_urls matches the domain of the main bucket (s3_bucket_name) - redirect_domain = get_domain_name url - if redirect_domain != domain - redirect_zone = check_and_create_hosted_zone_if_user_agrees redirect_domain - unless redirect_zone.nil? - # Just create the route here, there is no need to check if it exists because we just created the - # new zone. - create_route(url, redirect_zone) - end - else - # Check to see if the route exists and create route to the specific redirect_url - check_and_create_route(url, zone) - end - end + check_and_create_redirect_routes(@config_source.redirect_domains) end end end private - def self.get_connection - # Establish connection with amazon - @conn = Route53::Connection.new(@config_source.s3_access_key_id, @config_source.s3_secret_access_key) + def self.check_and_create_redirect_routes(redirect_domains) + redirect_domains.each do |url| + # check to see if the domain of the redirect_urls matches the domain of the main bucket (s3_bucket_name) + redirect_domain = get_domain_name url + if redirect_domain != domain + redirect_zone = check_and_create_hosted_zone_if_user_agrees redirect_domain + unless redirect_zone.nil? + # Just create the route here, there is no need to check if it exists because we just created the + # new zone. + create_route(url, redirect_zone) + end + else + # Check to see if the route exists and create route to the specific redirect_url + check_and_create_route(url, zone) + end + end end def self.check_and_create_route(url, zone) diff --git a/lib/configure-s3-website/runner.rb b/lib/configure-s3-website/runner.rb index 22d8afb..1896b9d 100644 --- a/lib/configure-s3-website/runner.rb +++ b/lib/configure-s3-website/runner.rb @@ -22,7 +22,8 @@ def self.maybe_create_or_update_cloudfront(options, standard_input) def self.maybe_create_or_update_route53(options, standard_input) route53_enabled = options[:config_source].route53_enabled unless route53_enabled.nil? or not route53_enabled - Route53Client.apply(options) + route53_client = Route53Client.new(options) + route53_client.apply end end From 589a06e1cbf8142e1dd3ed710f20c2786c37df52 Mon Sep 17 00:00:00 2001 From: Read Sprabery Date: Wed, 19 Mar 2014 20:49:32 -0500 Subject: [PATCH 4/8] Refactored zone creation code --- lib/configure-s3-website/route53_client.rb | 42 ++++++++++++---------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/configure-s3-website/route53_client.rb b/lib/configure-s3-website/route53_client.rb index 299645d..83304e9 100644 --- a/lib/configure-s3-website/route53_client.rb +++ b/lib/configure-s3-website/route53_client.rb @@ -119,39 +119,43 @@ def self.get_zone(domain) end def self.check_and_create_hosted_zone_if_user_agrees(domain) - # Creates a hosted zone if user says sure - success = false - if not hosted_zone_exits?(domain) # We need to have the user create the zone first. + zone_exists = ask_user_to_create_zone + if zone_exists + zone = get_zone domain + else + puts "Please create a hosted zone for #{domain} at: \n" + + "https://console.aws.amazon.com/route53/home before \n" + + "trying to auto-configure route53." + zone = nil + end + return zone + end + def ask_user_to_create_zone(domain) + if not hosted_zone_exits?(domain) # We need to have the user create the zone first. puts "A hosted zone for #{domain} does not exist, create one now?[y/N]?" case gets.chomp when /(y|Y)/ + # Create a new zone object zone = Route53::Zone.new(domain, nil, @conn) + # Send the request to Amazon resp = zone.create_zone - if resp.error? + if resp.error? # The response failed, show the user the error puts resp - success = false + zone_exists = false else while resp.pending? - sleep 1 # Wait for the response to finish so that we can create routes on the zone + sleep 1 # Wait for the response to finish so that we can create routes on the zone end - success = true + zone_exists = true end - else - success = false + else # The user doesn't want to create a zone at this time + zone_exists = false end else # the domain already exists - success = true - zone = get_zone domain + zone_exists = true end - - unless success - puts "Please create a hosted zone for #{domain} at: \n" + - "https://console.aws.amazon.com/route53/home before \n" + - "trying to auto-configure route53." - end - - return zone + zone_exists end def self.route_exists?(url, zone) From 8ae061caf597d360c21277e5047595a7926ee067 Mon Sep 17 00:00:00 2001 From: Read Sprabery Date: Wed, 19 Mar 2014 20:54:25 -0500 Subject: [PATCH 5/8] Refactored an each block to a find block --- lib/configure-s3-website/route53_client.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/configure-s3-website/route53_client.rb b/lib/configure-s3-website/route53_client.rb index 83304e9..de81b47 100644 --- a/lib/configure-s3-website/route53_client.rb +++ b/lib/configure-s3-website/route53_client.rb @@ -161,13 +161,12 @@ def ask_user_to_create_zone(domain) def self.route_exists?(url, zone) # checks to see if the route already is in DNS (maybe it has been set to something other than s3) records = zone.get_records - records.each do |rec| - if rec.name.include? url - return true - end + record = records.find {|rec| rec.name.include? url} + if record.nil? + return false + else + return true end - # if it's not found, return false - false end end From 4ede5faacaa0502d7ad94f5a8fa37294469fddc4 Mon Sep 17 00:00:00 2001 From: Read Sprabery Date: Sun, 23 Mar 2014 12:53:05 -0500 Subject: [PATCH 6/8] Updated Route53 version to 0.3.0 --- configure-s3-website.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure-s3-website.gemspec b/configure-s3-website.gemspec index c8958f4..b690185 100644 --- a/configure-s3-website.gemspec +++ b/configure-s3-website.gemspec @@ -10,7 +10,7 @@ spec = Gem::Specification.new do |s| s.bindir = 'bin' s.add_dependency 'deep_merge', '= 1.0.0' - s.add_dependency 'route53', '= 0.2.2' + s.add_dependency 'route53', '= 0.3.0' s.add_development_dependency 'rspec', '~> 2.10.0' s.add_development_dependency 'rspec-expectations', '~> 2.10.0' From 1fcccbefd6391c674451a997112728ec79beb7a1 Mon Sep 17 00:00:00 2001 From: Read Sprabery Date: Mon, 24 Mar 2014 10:11:26 -0500 Subject: [PATCH 7/8] Fixed issues related to previous refactoring --- lib/configure-s3-website/route53_client.rb | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/configure-s3-website/route53_client.rb b/lib/configure-s3-website/route53_client.rb index de81b47..02032b4 100644 --- a/lib/configure-s3-website/route53_client.rb +++ b/lib/configure-s3-website/route53_client.rb @@ -23,14 +23,14 @@ def apply # Create routes for redirect urls unless @config_source.redirect_domains.nil? - check_and_create_redirect_routes(@config_source.redirect_domains) + check_and_create_redirect_routes(@config_source.redirect_domains, domain, zone) end end end private - def self.check_and_create_redirect_routes(redirect_domains) + def check_and_create_redirect_routes(redirect_domains, domain, zone) redirect_domains.each do |url| # check to see if the domain of the redirect_urls matches the domain of the main bucket (s3_bucket_name) redirect_domain = get_domain_name url @@ -48,7 +48,7 @@ def self.check_and_create_redirect_routes(redirect_domains) end end - def self.check_and_create_route(url, zone) + def check_and_create_route(url, zone) if route_exists?(url, zone) # Ask the user if he/she wants to delete & recreate the route puts "A route already exists for #{url}" @@ -62,7 +62,7 @@ def self.check_and_create_route(url, zone) end end - def self.remove_route(url, zone) + def remove_route(url, zone) records = zone.get_records records = records.select {|rec| rec.name.include? url} if records.length == 1 @@ -75,7 +75,7 @@ def self.remove_route(url, zone) end end - def self.create_route(url, zone) + def create_route(url, zone) if not @config_source.cloudfront_distribution_id.nil? and url == @config_source.s3_bucket_name # Then this needs to point to Cloud front @@ -96,13 +96,13 @@ def self.create_route(url, zone) end end - def self.get_domain_name(bucket) + def get_domain_name(bucket) bucket_name = bucket parts = bucket_name.split('.') domain = "#{parts.last(2).join('.')}." end - def self.hosted_zone_exits?(domain) + def hosted_zone_exits?(domain) # Check to see if the user has already created a hosted zone zone = get_zone domain @@ -110,7 +110,7 @@ def self.hosted_zone_exits?(domain) not zone.nil? end - def self.get_zone(domain) + def get_zone(domain) # Get an array of the user's zones (usually one per domain) zones = @conn.get_zones @@ -118,8 +118,8 @@ def self.get_zone(domain) zone = zones.select { |zone| zone.name == domain}[0] end - def self.check_and_create_hosted_zone_if_user_agrees(domain) - zone_exists = ask_user_to_create_zone + def check_and_create_hosted_zone_if_user_agrees(domain) + zone_exists = ask_user_to_create_zone domain if zone_exists zone = get_zone domain else @@ -158,7 +158,7 @@ def ask_user_to_create_zone(domain) zone_exists end - def self.route_exists?(url, zone) + def route_exists?(url, zone) # checks to see if the route already is in DNS (maybe it has been set to something other than s3) records = zone.get_records record = records.find {|rec| rec.name.include? url} From 4674b99cc857adccd99e6aa369bbc3de41824ef2 Mon Sep 17 00:00:00 2001 From: Read Sprabery Date: Mon, 24 Mar 2014 11:49:47 -0500 Subject: [PATCH 8/8] Fixed hosted_zone_id for EU regions --- lib/configure-s3-website/s3_client.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/configure-s3-website/s3_client.rb b/lib/configure-s3-website/s3_client.rb index ee61d0d..d907839 100644 --- a/lib/configure-s3-website/s3_client.rb +++ b/lib/configure-s3-website/s3_client.rb @@ -200,14 +200,16 @@ def location_constraints eu_west_1_region = { :region => 'EU (Ireland)', :website_hostname => 's3-website-eu-west-1.amazonaws.com', - :endpoint => 's3-eu-west-1.amazonaws.com' + :endpoint => 's3-eu-west-1.amazonaws.com', + :hosted_zone_id => 'Z1BKCTXD74EZP#' } { '' => {:region => 'US Standard', :endpoint => 's3.amazonaws.com', :website_endpoint => 's3-website-us-east-1.amazonaws.com', :hosted_zone_id => 'Z3AQBSTGFYJSTF'}, 'us-west-2' => {:region => 'US West (Oregon)', :endpoint => 's3-us-west-2.amazonaws.com', :website_endpoint => 's3-website-us-west-2.amazonaws.com', :hosted_zone_id => 'Z3BJ6K6RIION7M'}, 'us-west-1' => {:region => 'US West (Northern California)', :endpoint => 's3-us-west-1.amazonaws.com', :website_endpoint => 's3-website-us-west-1.amazonaws.com', :hosted_zone_id => 'Z2F56UZL2M1ACD'}, - 'EU' => {:region => 'EU (Ireland)', :endpoint => 's3-eu-west-1.amazonaws.com', :website_endpoint => 's3-website-eu-west-1.amazonaws.com', :hosted_zone_id => 'Z1BKCTXD74EZP#'}, + 'EU' => eu_west_1_region, + 'eu-west-1' => eu_west_1_region, 'ap-southeast-1' => {:region => 'Asia Pacific (Singapore)', :endpoint => 's3-ap-southeast-1.amazonaws.com', :website_endpoint => 's3-website-ap-southeast-1.amazonaws.com', :hosted_zone_id => 'Z3O0J2DXBE1FTB'}, 'ap-southeast-2' => {:region => 'Asia Pacific (Sydney)', :endpoint => 's3-ap-southeast-2.amazonaws.com', :website_endpoint => 's3-website-ap-southeast-2.amazonaws.com', :hosted_zone_id => 'Z1WCIGYICN2BYD'}, 'ap-northeast-1' => {:region => 'Asia Pacific (Tokyo)', :endpoint => 's3-ap-northeast-1.amazonaws.com', :website_endpoint => 's3-website-ap-northeast-1.amazonaws.com', :hosted_zone_id => 'Z2M4EHUR26P7ZW'},