require 'base64' require 'openssl' require 'digest/sha1' require 'digest/md5' require 'net/https' module ConfigureS3Website class S3Client def self.configure_website(options) config_source = options[:config_source] begin enable_website_configuration(config_source) make_bucket_readable_to_everyone(config_source) configure_bucket_redirects(config_source) rescue NoSuchBucketError create_bucket(config_source) retry end end private def self.enable_website_configuration(config_source) body = %| #{config_source.index_document || "index.html"} #{config_source.error_document || "error.html"} | HttpHelper.call_s3_api( 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.make_bucket_readable_to_everyone(config_source) policy_json = %|{ "Version":"2008-10-17", "Statement":[{ "Sid":"PublicReadForGetBucketObjects", "Effect":"Allow", "Principal": { "AWS": "*" }, "Action":["s3:GetObject"], "Resource":["arn:aws:s3:::#{config_source.s3_bucket_name}/*"] }] }| HttpHelper.call_s3_api( path = "/#{config_source.s3_bucket_name}/?policy", method = Net::HTTP::Put, body = policy_json, config_source = config_source ) puts "Bucket #{config_source.s3_bucket_name} is now readable to the whole world" end def self.configure_bucket_redirects(config_source) routing_rules = config_source.routing_rules if routing_rules.is_a?(Array) && routing_rules.any? body = %| #{config_source.index_document || "index.html"} #{config_source.error_document || "error.html"} | routing_rules.each do |routing_rule| body << %| | body << XmlHelper.hash_to_api_xml(routing_rule, 7) body << %| | end body << %| | HttpHelper.call_s3_api( path = "/#{config_source.s3_bucket_name}/?website", method = Net::HTTP::Put, body = body, config_source = config_source ) puts "#{routing_rules.size} redirects configured for #{config_source.s3_bucket_name} bucket" else puts "No redirects to configure for #{config_source.s3_bucket_name} bucket" end end def self.create_bucket(config_source) endpoint = Endpoint.new(config_source.s3_endpoint || '') body = if endpoint.region == 'US Standard' '' # The standard endpoint does not need a location constraint else %| #{endpoint.location_constraint} | end HttpHelper.call_s3_api( path = "/#{config_source.s3_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 ] end end end private module ConfigureS3Website class Endpoint attr_reader :region, :location_constraint, :hostname, :website_hostname def initialize(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] @location_constraint = location_constraint end # http://docs.amazonwebservices.com/general/latest/gr/rande.html#s3_region def location_constraints eu_west_1_region = { :region => 'EU (Ireland)', :website_endpoint => 's3-website-eu-west-1.amazonaws.com', :endpoint => 's3-eu-west-1.amazonaws.com' } { '' => { :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, 'eu-central-1' => { :region => 'EU (Frankfurt)', :endpoint => 's3.eu-central-1.amazonaws.com', :website_endpoint => 's3-website.eu-central-1.amazonaws.com' }, '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' } } end def self.by_config_source(config_source) endpoint = Endpoint.new(config_source.s3_endpoint || '') end end end class InvalidS3LocationConstraintError < StandardError end class NoSuchBucketError < StandardError end