configure-s3-website 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ tmp
2
+ pkg
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
@@ -0,0 +1,54 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ configure-s3-website (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ addressable (2.3.2)
10
+ aruba (0.4.11)
11
+ childprocess (>= 0.2.3)
12
+ cucumber (>= 1.1.1)
13
+ ffi (>= 1.0.11)
14
+ rspec (>= 2.7.0)
15
+ builder (3.1.4)
16
+ childprocess (0.3.6)
17
+ ffi (~> 1.0, >= 1.0.6)
18
+ crack (0.3.1)
19
+ cucumber (1.2.1)
20
+ builder (>= 2.1.2)
21
+ diff-lcs (>= 1.1.3)
22
+ gherkin (~> 2.11.0)
23
+ json (>= 1.4.6)
24
+ diff-lcs (1.1.3)
25
+ ffi (1.2.0)
26
+ gherkin (2.11.5)
27
+ json (>= 1.4.6)
28
+ json (1.7.5)
29
+ rake (0.9.6)
30
+ rspec (2.10.0)
31
+ rspec-core (~> 2.10.0)
32
+ rspec-expectations (~> 2.10.0)
33
+ rspec-mocks (~> 2.10.0)
34
+ rspec-core (2.10.1)
35
+ rspec-expectations (2.10.0)
36
+ diff-lcs (~> 1.1.3)
37
+ rspec-mocks (2.10.1)
38
+ vcr (2.3.0)
39
+ webmock (1.8.11)
40
+ addressable (>= 2.2.7)
41
+ crack (>= 0.1.7)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ aruba (~> 0.4.0)
48
+ configure-s3-website!
49
+ cucumber (~> 1.2.0)
50
+ rake (~> 0.9.0)
51
+ rspec (~> 2.10.0)
52
+ rspec-expectations (~> 2.10.0)
53
+ vcr (~> 2.3.0)
54
+ webmock (~> 1.8.0)
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2012 Lauri lehmijoki
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,72 @@
1
+ # Configure-s3-website
2
+
3
+ [![Build Status](https://secure.travis-ci.org/laurilehmijoki/configure-s3-website.png)](http://travis-ci.org/laurilehmijoki/configure-s3-website)
4
+ [![Gem Version](https://fury-badge.herokuapp.com/rb/configure-s3-website.png)](http://badge.fury.io/rb/configure-s3-website)
5
+
6
+ Configure an AWS S3 bucket to function as a website. Easily from the
7
+ command-line interface.
8
+
9
+ The Ruby gem `configure-s3-website` can configure an S3 bucket to function as a
10
+ website. The bucket may or may not exist. If the bucket does not exist,
11
+ `configure-s3-website` will create it.
12
+
13
+ ## Install
14
+
15
+ gem install configure-s3-website
16
+
17
+ ## Usage
18
+
19
+ Create a file that contains the S3 credentials and the name of the bucket:
20
+
21
+ ```yaml
22
+ s3_id: your-aws-access-key
23
+ s3_secret: your-aws-secret-key
24
+ s3_bucket: name-of-your-bucket
25
+ ```
26
+
27
+ Save the file (as *config.yml*, for example). Now you are ready to go. Run the
28
+ following command:
29
+
30
+ configure-s3-website --config-file config.yml
31
+
32
+ Congratulations! You now have an S3 bucket that can act as a website server for
33
+ you.
34
+
35
+ ## How does `configure-s3-website` work?
36
+
37
+ It calls the [PUT Bucket
38
+ website](http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTwebsite.html)
39
+ API with the following XML:
40
+
41
+ ```xml
42
+ <WebsiteConfiguration xmlns='http://s3.amazonaws.com/doc/2006-03-01/'>
43
+ <IndexDocument>
44
+ <Suffix>index.html</Suffix>
45
+ </IndexDocument>
46
+ <ErrorDocument>
47
+ <Key>error.html</Key>
48
+ </ErrorDocument>
49
+ </WebsiteConfiguration>
50
+ ```
51
+
52
+ Then **it makes all the objects on the bucket visible to the whole world** by
53
+ calling the [PUT Bucket
54
+ policy](http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTpolicy.html)
55
+ API with the following JSON:
56
+
57
+ ```json
58
+ {
59
+ "Version":"2008-10-17",
60
+ "Statement":[{
61
+ "Sid":"PublicReadForGetBucketObjects",
62
+ "Effect":"Allow",
63
+ "Principal": { "AWS": "*" },
64
+ "Action":["s3:GetObject"],
65
+ "Resource":["arn:aws:s3:::your-bucket-name/*"]
66
+ }]
67
+ }
68
+ ```
69
+
70
+ ## License
71
+
72
+ See file LICENSE.
@@ -0,0 +1,16 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ desc "Build the project"
5
+ task :default => 'test'
6
+
7
+ desc "Run tests"
8
+ task :test do
9
+ sh "bundle exec rspec"
10
+ sh "bundle exec cucumber"
11
+ end
12
+
13
+ desc 'Run features tagged with @wip'
14
+ task 'cucumber:wip' do
15
+ sh "bundle exec cucumber --tags @wip"
16
+ end
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'optparse'
6
+ require 'configure-s3-website'
7
+
8
+ options = {}
9
+
10
+ banner = <<END
11
+ Usage: #{File.basename(__FILE__)} arguments
12
+
13
+ Configure your S3 bucket to function as a web site
14
+
15
+ Arguments:
16
+ END
17
+
18
+ optparse = OptionParser.new do |opts|
19
+ opts.banner = banner
20
+ opts.on('-f', '--config-file FILE',
21
+ 'Pick credentials and the S3 bucket name from a config file') do
22
+ |yaml_file_path|
23
+ options[:config_source] =
24
+ ConfigureS3Website::FileConfigSource.new yaml_file_path
25
+ end
26
+ opts.on('--help', 'Display this screen') do
27
+ puts opts
28
+ exit
29
+ end
30
+ end
31
+
32
+ optparse.parse!
33
+
34
+ ConfigureS3Website::S3Client.configure_website(options[:config_source])
@@ -0,0 +1,24 @@
1
+ require File.join([File.dirname(__FILE__),'lib','configure-s3-website','version.rb'])
2
+ spec = Gem::Specification.new do |s|
3
+ s.name = 'configure-s3-website'
4
+ s.version = ConfigureS3Website::VERSION
5
+ s.author = 'Lauri Lehmijoki'
6
+ s.email = 'lauri.lehmijoki@iki.fi'
7
+ s.homepage = 'https://github.com/laurilehmijoki/configure-s3-website'
8
+ s.platform = Gem::Platform::RUBY
9
+ s.summary = 'Configure your S3 bucket to function as a web site'
10
+ s.bindir = 'bin'
11
+
12
+ s.add_development_dependency 'rspec', '~> 2.10.0'
13
+ s.add_development_dependency 'rspec-expectations', '~> 2.10.0'
14
+ s.add_development_dependency 'cucumber', '~> 1.2.0'
15
+ s.add_development_dependency 'aruba', '~> 0.4.0'
16
+ s.add_development_dependency 'rake', '~> 0.9.0'
17
+ s.add_development_dependency 'vcr', '~> 2.3.0'
18
+ s.add_development_dependency 'webmock', '~> 1.8.0'
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+ end
@@ -0,0 +1,159 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: put
5
+ uri: https://s3.amazonaws.com/name-of-a-new-bucket/?website
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ! "\n <WebsiteConfiguration xmlns='http://s3.amazonaws.com/doc/2006-03-01/'>\n
9
+ \ <IndexDocument>\n <Suffix>index.html</Suffix>\n </IndexDocument>\n
10
+ \ <ErrorDocument>\n <Key>error.html</Key>\n </ErrorDocument>\n
11
+ \ </WebsiteConfiguration>\n "
12
+ headers:
13
+ Date:
14
+ - Sun, 30 Dec 2012 19:59:34 EET
15
+ Content-Type:
16
+ - ''
17
+ Content-Length:
18
+ - '298'
19
+ Authorization:
20
+ - AWS foo
21
+ response:
22
+ status:
23
+ code: 404
24
+ message: Not Found
25
+ headers:
26
+ X-Amz-Request-Id:
27
+ - CD16DF6FF66B5922
28
+ X-Amz-Id-2:
29
+ - Xu30q0VM0aaYY091koYh8FIUDPB2KWOQLRPqnLulIK3ScIrMWgOwF7M2XRYIi5Vs
30
+ Content-Type:
31
+ - application/xml
32
+ Transfer-Encoding:
33
+ - chunked
34
+ Date:
35
+ - Sun, 30 Dec 2012 17:59:34 GMT
36
+ Connection:
37
+ - close
38
+ Server:
39
+ - AmazonS3
40
+ body:
41
+ encoding: US-ASCII
42
+ string: ! '<?xml version="1.0" encoding="UTF-8"?>
43
+
44
+ <Error><Code>NoSuchBucket</Code><Message>The specified bucket does not exist</Message><BucketName>name-of-a-new-bucket</BucketName><RequestId>CD16DF6FF66B5922</RequestId><HostId>Xu30q0VM0aaYY091koYh8FIUDPB2KWOQLRPqnLulIK3ScIrMWgOwF7M2XRYIi5Vs</HostId></Error>'
45
+ http_version:
46
+ recorded_at: Sun, 30 Dec 2012 17:59:37 GMT
47
+ - request:
48
+ method: put
49
+ uri: https://s3.amazonaws.com/name-of-a-new-bucket
50
+ body:
51
+ encoding: US-ASCII
52
+ string: ''
53
+ headers:
54
+ Date:
55
+ - Sun, 30 Dec 2012 19:59:37 EET
56
+ Content-Type:
57
+ - ''
58
+ Content-Length:
59
+ - '0'
60
+ Authorization:
61
+ - AWS foo
62
+ response:
63
+ status:
64
+ code: 200
65
+ message: OK
66
+ headers:
67
+ X-Amz-Id-2:
68
+ - 7HuGyurBPI/HV8DVtjtRufaMaLRJLU+vCch+BPuBAysYAlE7XS4Eavz7srhB2dTx
69
+ X-Amz-Request-Id:
70
+ - 431755651DECBBC5
71
+ Date:
72
+ - Sun, 30 Dec 2012 17:59:39 GMT
73
+ Location:
74
+ - /name-of-a-new-bucket
75
+ Content-Length:
76
+ - '0'
77
+ Server:
78
+ - AmazonS3
79
+ body:
80
+ encoding: US-ASCII
81
+ string: ''
82
+ http_version:
83
+ recorded_at: Sun, 30 Dec 2012 17:59:39 GMT
84
+ - request:
85
+ method: put
86
+ uri: https://s3.amazonaws.com/name-of-a-new-bucket/?website
87
+ body:
88
+ encoding: US-ASCII
89
+ string: ! "\n <WebsiteConfiguration xmlns='http://s3.amazonaws.com/doc/2006-03-01/'>\n
90
+ \ <IndexDocument>\n <Suffix>index.html</Suffix>\n </IndexDocument>\n
91
+ \ <ErrorDocument>\n <Key>error.html</Key>\n </ErrorDocument>\n
92
+ \ </WebsiteConfiguration>\n "
93
+ headers:
94
+ Date:
95
+ - Sun, 30 Dec 2012 19:59:39 EET
96
+ Content-Type:
97
+ - ''
98
+ Content-Length:
99
+ - '298'
100
+ Authorization:
101
+ - AWS foo
102
+ response:
103
+ status:
104
+ code: 200
105
+ message: OK
106
+ headers:
107
+ X-Amz-Id-2:
108
+ - XZE0T/tWFNuRi6/yu1O+O3A7fJ/j2oas8ZqK4p6rLbaRxevXe44K/pAOsrB381ei
109
+ X-Amz-Request-Id:
110
+ - 514396AE3C1BB2C1
111
+ Date:
112
+ - Sun, 30 Dec 2012 17:59:41 GMT
113
+ Content-Length:
114
+ - '0'
115
+ Server:
116
+ - AmazonS3
117
+ body:
118
+ encoding: US-ASCII
119
+ string: ''
120
+ http_version:
121
+ recorded_at: Sun, 30 Dec 2012 17:59:42 GMT
122
+ - request:
123
+ method: put
124
+ uri: https://s3.amazonaws.com/name-of-a-new-bucket/?policy
125
+ body:
126
+ encoding: US-ASCII
127
+ string: ! "{\n \"Version\":\"2008-10-17\",\n \"Statement\":[{\n
128
+ \ \"Sid\":\"PublicReadForGetBucketObjects\",\n \"Effect\":\"Allow\",\n
129
+ \ \"Principal\": { \"AWS\": \"*\" },\n \"Action\":[\"s3:GetObject\"],\n
130
+ \ \"Resource\":[\"arn:aws:s3:::name-of-a-new-bucket/*\"]\n }]\n
131
+ \ }"
132
+ headers:
133
+ Date:
134
+ - Sun, 30 Dec 2012 19:59:42 EET
135
+ Content-Type:
136
+ - ''
137
+ Content-Length:
138
+ - '289'
139
+ Authorization:
140
+ - AWS foo
141
+ response:
142
+ status:
143
+ code: 204
144
+ message: No Content
145
+ headers:
146
+ X-Amz-Id-2:
147
+ - z/XdtqiOW0ihAGadAXwCNCAuE0OLeFisy4TN5e/3OmEI72dQxQjfftyXFSNNwOQN
148
+ X-Amz-Request-Id:
149
+ - 82E8EBABB5D150C5
150
+ Date:
151
+ - Sun, 30 Dec 2012 17:59:44 GMT
152
+ Server:
153
+ - AmazonS3
154
+ body:
155
+ encoding: US-ASCII
156
+ string: ''
157
+ http_version:
158
+ recorded_at: Sun, 30 Dec 2012 17:59:44 GMT
159
+ recorded_with: VCR 2.3.0
@@ -0,0 +1,78 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: put
5
+ uri: https://s3.amazonaws.com/name-of-an-existing-bucket/?website
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ! "\n <WebsiteConfiguration xmlns='http://s3.amazonaws.com/doc/2006-03-01/'>\n
9
+ \ <IndexDocument>\n <Suffix>index.html</Suffix>\n </IndexDocument>\n
10
+ \ <ErrorDocument>\n <Key>error.html</Key>\n </ErrorDocument>\n
11
+ \ </WebsiteConfiguration>\n "
12
+ headers:
13
+ Date:
14
+ - Sun, 30 Dec 2012 20:40:12 EET
15
+ Content-Type:
16
+ - ''
17
+ Content-Length:
18
+ - '298'
19
+ Authorization:
20
+ - AWS
21
+ response:
22
+ status:
23
+ code: 200
24
+ message: OK
25
+ headers:
26
+ X-Amz-Id-2:
27
+ - IjY5jr731On+Fs4U70tsMf5f6KmYMhe+zCgbrIRBIrVqlRflrm5o9gpPWLzhO9SG
28
+ X-Amz-Request-Id:
29
+ - 2675BEDE56DD5A89
30
+ Date:
31
+ - Sun, 30 Dec 2012 18:40:14 GMT
32
+ Content-Length:
33
+ - '0'
34
+ Server:
35
+ - AmazonS3
36
+ body:
37
+ encoding: US-ASCII
38
+ string: ''
39
+ http_version:
40
+ recorded_at: Sun, 30 Dec 2012 18:40:14 GMT
41
+ - request:
42
+ method: put
43
+ uri: https://s3.amazonaws.com/name-of-an-existing-bucket/?policy
44
+ body:
45
+ encoding: US-ASCII
46
+ string: ! "{\n \"Version\":\"2008-10-17\",\n \"Statement\":[{\n
47
+ \ \"Sid\":\"PublicReadForGetBucketObjects\",\n \"Effect\":\"Allow\",\n
48
+ \ \"Principal\": { \"AWS\": \"*\" },\n \"Action\":[\"s3:GetObject\"],\n
49
+ \ \"Resource\":[\"arn:aws:s3:::name-of-an-existing-bucket/*\"]\n }]\n
50
+ \ }"
51
+ headers:
52
+ Date:
53
+ - Sun, 30 Dec 2012 20:40:14 EET
54
+ Content-Type:
55
+ - ''
56
+ Content-Length:
57
+ - '295'
58
+ Authorization:
59
+ - AWS
60
+ response:
61
+ status:
62
+ code: 204
63
+ message: No Content
64
+ headers:
65
+ X-Amz-Id-2:
66
+ - i1+BrkVJ09WJjTMD4FrlD7WU1lDeE3FrTJHs+GPui8JoXcFW0dFWFjykJVnXA+4g
67
+ X-Amz-Request-Id:
68
+ - 3959D1657C7E30DC
69
+ Date:
70
+ - Sun, 30 Dec 2012 18:40:15 GMT
71
+ Server:
72
+ - AmazonS3
73
+ body:
74
+ encoding: US-ASCII
75
+ string: ''
76
+ http_version:
77
+ recorded_at: Sun, 30 Dec 2012 18:40:15 GMT
78
+ recorded_with: VCR 2.3.0
@@ -0,0 +1,57 @@
1
+ Feature: load credentials and the S3 bucket name from a file
2
+
3
+ As an AWS S3 user
4
+ Who has S3 credentials and the S3 bucket name in a YAML file
5
+ I would like to configure the bucket to function as a website
6
+
7
+ Scenario: config file is missing property "s3_id"
8
+ Given a directory named "website-project"
9
+ And a file named "website-project/s3_config.yml" with:
10
+ """
11
+ id: key
12
+ s3_secret: SECRET
13
+ s3_bucket: my-bucket
14
+ """
15
+ When I run `configure-s3-website --config-file website-project/s3_config.yml`
16
+ Then the output should contain:
17
+ """
18
+ website-project/s3_config.yml does not contain the required key(s) s3_id
19
+ """
20
+
21
+ Scenario: config file is missing property "s3_secret"
22
+ Given a directory named "website-project"
23
+ And a file named "website-project/s3_config.yml" with:
24
+ """
25
+ s3_id: key
26
+ s3_bucket: my-bucket
27
+ """
28
+ When I run `configure-s3-website --config-file website-project/s3_config.yml`
29
+ Then the output should contain:
30
+ """
31
+ website-project/s3_config.yml does not contain the required key(s) s3_secret
32
+ """
33
+
34
+ Scenario: config file is missing property "s3_bucket"
35
+ Given a directory named "website-project"
36
+ And a file named "website-project/s3_config.yml" with:
37
+ """
38
+ s3_id: key
39
+ s3_secret: secret
40
+ """
41
+ When I run `configure-s3-website --config-file website-project/s3_config.yml`
42
+ Then the output should contain:
43
+ """
44
+ website-project/s3_config.yml does not contain the required key(s) s3_bucket
45
+ """
46
+
47
+ Scenario: config file is missing properties "s3_bucket" and "s3_id"
48
+ Given a directory named "website-project"
49
+ And a file named "website-project/s3_config.yml" with:
50
+ """
51
+ s3_secret: secret
52
+ """
53
+ When I run `configure-s3-website --config-file website-project/s3_config.yml`
54
+ Then the output should contain:
55
+ """
56
+ website-project/s3_config.yml does not contain the required key(s) s3_id, s3_bucket
57
+ """
@@ -0,0 +1,24 @@
1
+ Feature: configure an S3 bucket to function as a website
2
+
3
+ @bucket-does-not-exist
4
+ Scenario: The bucket does not yet exist
5
+ Given my config file is in "features/support/sample_config_files/s3_config_with_non-existing_bucket.yml"
6
+ When I run the configure-s3-website command
7
+ Then the output should be
8
+ """
9
+ Created bucket name-of-a-new-bucket
10
+ Bucket name-of-a-new-bucket now functions as a website
11
+ Bucket name-of-a-new-bucket is now readable to the whole world
12
+
13
+ """
14
+
15
+ @bucket-exists
16
+ Scenario: The bucket already exists
17
+ Given my config file is in "features/support/sample_config_files/s3_config_with_existing_bucket.yml"
18
+ When I run the configure-s3-website command
19
+ Then the output should be
20
+ """
21
+ Bucket name-of-an-existing-bucket now functions as a website
22
+ Bucket name-of-an-existing-bucket is now readable to the whole world
23
+
24
+ """
@@ -0,0 +1,29 @@
1
+ require 'rspec'
2
+
3
+ Given /^my config file is in "(.*?)"$/ do |config_file_path|
4
+ @config_file_path = config_file_path
5
+ end
6
+
7
+ When /^I run the configure-s3-website command$/ do
8
+ @console_output = capture_stdout {
9
+ config_source = ConfigureS3Website::FileConfigSource.new(@config_file_path)
10
+ ConfigureS3Website::S3Client.configure_website(config_source)
11
+ }
12
+ end
13
+
14
+ Then /^the output should be$/ do |expected_console_output|
15
+ @console_output.should eq(expected_console_output)
16
+ end
17
+
18
+ module Kernel
19
+ require 'stringio'
20
+
21
+ def capture_stdout
22
+ out = StringIO.new
23
+ $stdout = out
24
+ yield
25
+ out.string
26
+ ensure
27
+ $stdout = STDOUT
28
+ end
29
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require
5
+
6
+ require 'aruba/cucumber'
7
+ require 'cucumber/rspec/doubles'
8
+
9
+ # Following from 'aruba/cucumber'
10
+ Before do
11
+ @__aruba_original_paths = (ENV['PATH'] || '').split(File::PATH_SEPARATOR)
12
+ ENV['PATH'] = ([File.expand_path('bin')] + @__aruba_original_paths).join(File::PATH_SEPARATOR)
13
+ end
14
+
15
+ After do
16
+ ENV['PATH'] = @__aruba_original_paths.join(File::PATH_SEPARATOR)
17
+ end
18
+ # End of following from 'aruba/cucumber'
@@ -0,0 +1,3 @@
1
+ s3_id: foo
2
+ s3_secret: foo
3
+ s3_bucket: name-of-an-existing-bucket
@@ -0,0 +1,3 @@
1
+ s3_id: foo
2
+ s3_secret: foo
3
+ s3_bucket: name-of-a-new-bucket
@@ -0,0 +1,11 @@
1
+ require 'vcr'
2
+
3
+ VCR.configure do |c|
4
+ c.hook_into :webmock
5
+ c.cassette_library_dir = 'features/cassettes'
6
+ end
7
+
8
+ VCR.cucumber_tags do |t|
9
+ t.tag '@bucket-does-not-exist'
10
+ t.tag '@bucket-exists'
11
+ end
@@ -0,0 +1,4 @@
1
+ require 'configure-s3-website/version'
2
+ require 'configure-s3-website/s3_client'
3
+ require 'configure-s3-website/config_source/config_source'
4
+ require 'configure-s3-website/config_source/file_config_source'
@@ -0,0 +1,12 @@
1
+ module ConfigureS3Website
2
+ class ConfigSource
3
+ def s3_access_key_id
4
+ end
5
+
6
+ def s3_secret_access_key
7
+ end
8
+
9
+ def s3_bucket_name
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+
4
+ module ConfigureS3Website
5
+ class FileConfigSource < ConfigSource
6
+ def initialize(yaml_file_path)
7
+ @config = parse_config yaml_file_path
8
+ end
9
+
10
+ def s3_access_key_id
11
+ @config['s3_id']
12
+ end
13
+
14
+ def s3_secret_access_key
15
+ @config['s3_secret']
16
+ end
17
+
18
+ def s3_bucket_name
19
+ @config['s3_bucket']
20
+ end
21
+
22
+ private
23
+
24
+ def parse_config(yaml_file_path)
25
+ config = YAML.load(ERB.new(File.read(yaml_file_path)).result)
26
+ validate_config(config, yaml_file_path)
27
+ config
28
+ end
29
+
30
+ def validate_config(config, yaml_file_path)
31
+ required_keys = %w{s3_id s3_secret s3_bucket}
32
+ missing_keys = required_keys.reject do |key| config.keys.include?key end
33
+ unless missing_keys.empty?
34
+ raise "File #{yaml_file_path} does not contain the required key(s) #{missing_keys.join(', ')}"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,127 @@
1
+ require 'base64'
2
+ require 'openssl'
3
+ require 'digest/sha1'
4
+ require 'digest/md5'
5
+ require 'net/https'
6
+
7
+ module ConfigureS3Website
8
+ class S3Client
9
+ def self.configure_website(config_source)
10
+ begin
11
+ enable_website_configuration(config_source)
12
+ make_bucket_readable_to_everyone(config_source)
13
+ rescue NoSuchBucketError
14
+ create_bucket(config_source)
15
+ retry
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def self.enable_website_configuration(config_source)
22
+ body = %|
23
+ <WebsiteConfiguration xmlns='http://s3.amazonaws.com/doc/2006-03-01/'>
24
+ <IndexDocument>
25
+ <Suffix>index.html</Suffix>
26
+ </IndexDocument>
27
+ <ErrorDocument>
28
+ <Key>error.html</Key>
29
+ </ErrorDocument>
30
+ </WebsiteConfiguration>
31
+ |
32
+ call_s3_api(
33
+ path = "/#{config_source.s3_bucket_name}/?website",
34
+ method = Net::HTTP::Put,
35
+ body = body,
36
+ config_source = config_source
37
+ )
38
+ puts "Bucket #{config_source.s3_bucket_name} now functions as a website"
39
+ end
40
+
41
+ def self.make_bucket_readable_to_everyone(config_source)
42
+ policy_json = %|{
43
+ "Version":"2008-10-17",
44
+ "Statement":[{
45
+ "Sid":"PublicReadForGetBucketObjects",
46
+ "Effect":"Allow",
47
+ "Principal": { "AWS": "*" },
48
+ "Action":["s3:GetObject"],
49
+ "Resource":["arn:aws:s3:::#{config_source.s3_bucket_name}/*"]
50
+ }]
51
+ }|
52
+ call_s3_api(
53
+ path = "/#{config_source.s3_bucket_name}/?policy",
54
+ method = Net::HTTP::Put,
55
+ body = policy_json,
56
+ config_source = config_source
57
+ )
58
+ puts "Bucket #{config_source.s3_bucket_name} is now readable to the whole world"
59
+ end
60
+
61
+ def self.create_bucket(config_source)
62
+ call_s3_api(
63
+ path = "/#{config_source.s3_bucket_name}",
64
+ method = Net::HTTP::Put,
65
+ body = '',
66
+ config_source = config_source
67
+ )
68
+ puts "Created bucket #{config_source.s3_bucket_name}"
69
+ end
70
+
71
+ def self.call_s3_api(path, method, body, config_source)
72
+ date = Time.now.strftime("%a, %d %b %Y %H:%M:%S %Z")
73
+ digest = create_digest(path, method, config_source, date)
74
+ url = "https://s3.amazonaws.com#{path}"
75
+ uri = URI.parse(url)
76
+ req = method.new(uri.to_s)
77
+ req.initialize_http_header({
78
+ 'Date' => date,
79
+ 'Content-Type' => '',
80
+ 'Content-Length' => body.length.to_s,
81
+ 'Authorization' => "AWS %s:%s" % [config_source.s3_access_key_id, digest]
82
+ })
83
+ req.body = body
84
+ http = Net::HTTP.new(uri.host, uri.port)
85
+ http.use_ssl = true
86
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
87
+ res = http.request(req)
88
+ if res.code.to_i.between? 200, 299
89
+ res
90
+ else
91
+ raise ConfigureS3Website::ErrorParser.create_error res.body
92
+ end
93
+ end
94
+
95
+ def self.create_digest(path, method, config_source, date)
96
+ digest = OpenSSL::Digest::Digest.new('sha1')
97
+ method_string = method.to_s.match(/Net::HTTP::(\w+)/)[1].upcase
98
+ can_string = "#{method_string}\n\n\n#{date}\n#{path}"
99
+ hmac = OpenSSL::HMAC.digest(digest, config_source.s3_secret_access_key, can_string)
100
+ signature = Base64.encode64(hmac).strip
101
+ end
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ module ConfigureS3Website
108
+ class ErrorParser
109
+ def self.create_error(amazon_error_xml)
110
+ error_code = amazon_error_xml.delete('\n').match(/<Code>(.*?)<\/Code>/)[1]
111
+ begin
112
+ Object.const_get("#{error_code}Error").new
113
+ rescue NameError
114
+ GenericS3Error.new(amazon_error_xml)
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ class NoSuchBucketError < StandardError
121
+ end
122
+
123
+ class GenericS3Error < StandardError
124
+ def initialize(error_message)
125
+ super("AWS API call failed:\n#{error_message}")
126
+ end
127
+ end
@@ -0,0 +1,3 @@
1
+ module ConfigureS3Website
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,11 @@
1
+ require 'rspec'
2
+ require 'configure-s3-website'
3
+
4
+ describe ConfigureS3Website::FileConfigSource do
5
+ it 'can parse files that contain eRuby code' do
6
+ extractor = ConfigureS3Website::FileConfigSource.new('spec/sample_files/_config_file_with_eruby.yml')
7
+ extractor.s3_access_key_id.should eq('hello world')
8
+ extractor.s3_secret_access_key.should eq('secret world')
9
+ extractor.s3_bucket_name.should eq('my-bucket')
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ s3_id: <%= 'hello world' %>
2
+ s3_secret: <%= 'secret world' %>
3
+ s3_bucket: my-bucket
metadata ADDED
@@ -0,0 +1,193 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: configure-s3-website
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Lauri Lehmijoki
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.10.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.10.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec-expectations
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 2.10.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.10.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: cucumber
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.2.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.2.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: aruba
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.4.0
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 0.4.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 0.9.0
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 0.9.0
94
+ - !ruby/object:Gem::Dependency
95
+ name: vcr
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: 2.3.0
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: 2.3.0
110
+ - !ruby/object:Gem::Dependency
111
+ name: webmock
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: 1.8.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: 1.8.0
126
+ description:
127
+ email: lauri.lehmijoki@iki.fi
128
+ executables:
129
+ - configure-s3-website
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - .gitignore
134
+ - .travis.yml
135
+ - Gemfile
136
+ - Gemfile.lock
137
+ - LICENSE
138
+ - README.md
139
+ - Rakefile
140
+ - bin/configure-s3-website
141
+ - configure-s3-website.gemspec
142
+ - features/cassettes/cucumber_tags/bucket-does-not-exist.yml
143
+ - features/cassettes/cucumber_tags/bucket-exists.yml
144
+ - features/config_file.feature
145
+ - features/configure_bucket.feature
146
+ - features/step_definitions/steps.rb
147
+ - features/support/env.rb
148
+ - features/support/sample_config_files/s3_config_with_existing_bucket.yml
149
+ - features/support/sample_config_files/s3_config_with_non-existing_bucket.yml
150
+ - features/support/vcr.rb
151
+ - lib/configure-s3-website.rb
152
+ - lib/configure-s3-website/config_source/config_source.rb
153
+ - lib/configure-s3-website/config_source/file_config_source.rb
154
+ - lib/configure-s3-website/s3_client.rb
155
+ - lib/configure-s3-website/version.rb
156
+ - spec/config_extractor/file_config_extractor_spec.rb
157
+ - spec/sample_files/_config_file_with_eruby.yml
158
+ homepage: https://github.com/laurilehmijoki/configure-s3-website
159
+ licenses: []
160
+ post_install_message:
161
+ rdoc_options: []
162
+ require_paths:
163
+ - lib
164
+ required_ruby_version: !ruby/object:Gem::Requirement
165
+ none: false
166
+ requirements:
167
+ - - ! '>='
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ required_rubygems_version: !ruby/object:Gem::Requirement
171
+ none: false
172
+ requirements:
173
+ - - ! '>='
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ requirements: []
177
+ rubyforge_project:
178
+ rubygems_version: 1.8.24
179
+ signing_key:
180
+ specification_version: 3
181
+ summary: Configure your S3 bucket to function as a web site
182
+ test_files:
183
+ - features/cassettes/cucumber_tags/bucket-does-not-exist.yml
184
+ - features/cassettes/cucumber_tags/bucket-exists.yml
185
+ - features/config_file.feature
186
+ - features/configure_bucket.feature
187
+ - features/step_definitions/steps.rb
188
+ - features/support/env.rb
189
+ - features/support/sample_config_files/s3_config_with_existing_bucket.yml
190
+ - features/support/sample_config_files/s3_config_with_non-existing_bucket.yml
191
+ - features/support/vcr.rb
192
+ - spec/config_extractor/file_config_extractor_spec.rb
193
+ - spec/sample_files/_config_file_with_eruby.yml