configure-s3-website 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +102 -3
- data/changelog.md +6 -0
- data/configure-s3-website.gemspec +2 -0
- data/features/cassettes/cucumber_tags/apply-configs-on-cf-dist.yml +169 -0
- data/features/create_cloudfront_dist.feature +27 -1
- data/features/modify_cloudfront_dist.feature +19 -0
- data/features/step_definitions/steps.rb +9 -1
- data/features/support/sample_config_files/apply_configs_on_cf_dist.yml +8 -0
- data/features/support/sample_config_files/create_cf_dist_with_custom_configs.yml +7 -0
- data/features/support/vcr.rb +1 -0
- data/lib/configure-s3-website/cloudfront_client.rb +81 -18
- data/lib/configure-s3-website/config_source/config_source.rb +3 -0
- data/lib/configure-s3-website/config_source/file_config_source.rb +4 -0
- data/lib/configure-s3-website/http_helper.rb +19 -5
- data/lib/configure-s3-website/runner.rb +16 -2
- data/lib/configure-s3-website/version.rb +1 -1
- data/spec/cloudfront_client_spec.rb +71 -0
- metadata +30 -4
data/README.md
CHANGED
@@ -33,6 +33,10 @@ you.
|
|
33
33
|
|
34
34
|
### Deliver your website via CloudFront
|
35
35
|
|
36
|
+
This gem can create new CloudFront distributions and update existing ones.
|
37
|
+
|
38
|
+
#### Creating a new distribution
|
39
|
+
|
36
40
|
`configure-s3-website` can create a CloudFront distribution for you. It will ask
|
37
41
|
you whether you want to deliver your website via the CDN. If you answer yes,
|
38
42
|
`configure-s3-website` will create a CloudFront distribution that has the
|
@@ -55,9 +59,102 @@ configuration file, `configure-s3-website` will not create a new distribution.
|
|
55
59
|
Conversely, if you remove the `cloudfront_distribution_id` key from the file and
|
56
60
|
run `configure-s3-website` again, it will create you a new distribution.
|
57
61
|
|
58
|
-
|
62
|
+
##### Creating a new distribution with custom settings
|
63
|
+
|
64
|
+
If the default settings do not suit you, you can create a new distribution with
|
65
|
+
your settings by adding `cloudfront_distribution_config` values into your config
|
66
|
+
file. For example:
|
67
|
+
|
68
|
+
```yaml
|
69
|
+
cloudfront_distribution_config:
|
70
|
+
default_cache_behavior:
|
71
|
+
min_TTL: 600
|
72
|
+
default_root_object: index.json
|
73
|
+
```
|
74
|
+
|
75
|
+
See the section below for more information about the valid values of the
|
76
|
+
`cloudfront_distribution_config` setting.
|
77
|
+
|
78
|
+
If you want to, you can look at the distribution settings on the management console
|
59
79
|
at <https://console.aws.amazon.com/cloudfront>.
|
60
80
|
|
81
|
+
#### Updating an existing distribution
|
82
|
+
|
83
|
+
You can modify an existing CloudFront distribution by defining the id of the
|
84
|
+
distribution and the configs you wish to override the defaults with.
|
85
|
+
|
86
|
+
Let's say your config file contains the following fragment:
|
87
|
+
|
88
|
+
```yaml
|
89
|
+
cloudfront_distribution_id: AXSAXSSE134
|
90
|
+
cloudfront_distribution_config:
|
91
|
+
default_cache_behavior:
|
92
|
+
min_TTL: 600
|
93
|
+
default_root_object: index.json
|
94
|
+
```
|
95
|
+
|
96
|
+
When you invoke `configure-s3-website`, it will overwrite the default value of
|
97
|
+
*default_cache_behavior's* *min_TTL* as well as the default value of
|
98
|
+
*default_root_object* setting in the [default distribution configs](#default-distribution-configs).
|
99
|
+
|
100
|
+
This gem generates `<DistributionConfig>` of the CloudFront REST API. For
|
101
|
+
reference, see
|
102
|
+
<http://docs.aws.amazon.com/AmazonCloudFront/latest/APIReference/GetConfig.html#GetConfig_Responses>
|
103
|
+
(example) and
|
104
|
+
<https://cloudfront.amazonaws.com/doc/2012-07-01/AmazonCloudFrontCommon.xsd>
|
105
|
+
(XSD). In other words, When you call `configure-s3-website`, it will turn the values of your
|
106
|
+
`cloudfront_distribution_config` into XML, include them in the
|
107
|
+
`<DistributionConfig>` element and send them to the CloudFront REST API.
|
108
|
+
|
109
|
+
The YAML keys in the config file will be turned into CloudFront REST API XML
|
110
|
+
with the same logic as in [configuring redirects](#configuring-redirects).
|
111
|
+
|
112
|
+
Having the distribution settings in the config file is handy, because it allows
|
113
|
+
you to store most (in many cases all) website deployment settings in one file.
|
114
|
+
|
115
|
+
#### Default distribution configs
|
116
|
+
|
117
|
+
Below is the default CloudFront distribution config. It is built based on the
|
118
|
+
API version 2012-07-01 of [DistributionConfig Complex
|
119
|
+
Type](http://docs.aws.amazon.com/AmazonCloudFront/latest/APIReference/DistributionConfigDatatype.html).
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
{
|
123
|
+
'caller_reference' => 'configure-s3-website gem [generated-timestamp]',
|
124
|
+
'default_root_object' => 'index.html',
|
125
|
+
'logging' => {
|
126
|
+
'enabled' => 'false',
|
127
|
+
'include_cookies' => 'false',
|
128
|
+
'bucket' => '',
|
129
|
+
'prefix' => ''
|
130
|
+
},
|
131
|
+
'enabled' => 'true',
|
132
|
+
'comment' => 'Created by the configure-s3-website gem',
|
133
|
+
'aliases' => {
|
134
|
+
'quantity' => '0'
|
135
|
+
},
|
136
|
+
'default_cache_behavior' => {
|
137
|
+
'target_origin_id' => '[generated-string]',
|
138
|
+
'trusted_signers' => {
|
139
|
+
'enabled' => 'false',
|
140
|
+
'quantity' => '0'
|
141
|
+
},
|
142
|
+
'forwarded_values' => {
|
143
|
+
'query_string' => 'true',
|
144
|
+
'cookies' => {
|
145
|
+
'forward' => 'all'
|
146
|
+
}
|
147
|
+
},
|
148
|
+
'viewer_protocol_policy' => 'allow-all',
|
149
|
+
'min_TTL' => '86400'
|
150
|
+
},
|
151
|
+
'cache_behaviors' => {
|
152
|
+
'quantity' => '0'
|
153
|
+
},
|
154
|
+
'price_class' => 'PriceClass_All'
|
155
|
+
}
|
156
|
+
```
|
157
|
+
|
61
158
|
### Specifying a non-standard S3 endpoint
|
62
159
|
|
63
160
|
By default, `configure-s3-website` creates the S3 website into the US Standard
|
@@ -109,8 +206,10 @@ the bucket. In brief, it does the following things:
|
|
109
206
|
3. Make the bucket **readable to the whole world**
|
110
207
|
4. Apply the redirect (a.k.a routing) rules on the bucket website
|
111
208
|
|
112
|
-
|
113
|
-
|
209
|
+
When interacting with CloudFront, `configure-s3-website` uses the [POST
|
210
|
+
Distribution](http://docs.aws.amazon.com/AmazonCloudFront/latest/APIReference/CreateDistribution.html),
|
211
|
+
[GET
|
212
|
+
Distribution](http://docs.aws.amazon.com/AmazonCloudFront/latest/APIReference/GetDistribution.html) and [PUT Distribution Config](http://docs.aws.amazon.com/AmazonCloudFront/latest/APIReference/PutConfig.html) APIs.
|
114
213
|
|
115
214
|
## Development
|
116
215
|
|
data/changelog.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
This project uses [Semantic Versioning](http://semver.org).
|
4
4
|
|
5
|
+
## 1.4.0
|
6
|
+
|
7
|
+
* Allow the user to store his CloudFront settings in the config file
|
8
|
+
* Support updating configs of an existing CloudFront distribution
|
9
|
+
* Support creating of new distros with custom CloudFront configs
|
10
|
+
|
5
11
|
## 1.3.0
|
6
12
|
|
7
13
|
* Create a CloudFront distro if the user wants to deliver his S3 website via the
|
@@ -9,6 +9,8 @@ spec = Gem::Specification.new do |s|
|
|
9
9
|
s.summary = 'Configure your AWS S3 bucket to function as a web site'
|
10
10
|
s.bindir = 'bin'
|
11
11
|
|
12
|
+
s.add_dependency 'deep_merge', '= 1.0.0'
|
13
|
+
|
12
14
|
s.add_development_dependency 'rspec', '~> 2.10.0'
|
13
15
|
s.add_development_dependency 'rspec-expectations', '~> 2.10.0'
|
14
16
|
s.add_development_dependency 'cucumber', '~> 1.2.0'
|
@@ -0,0 +1,169 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: put
|
5
|
+
uri: https://s3.amazonaws.com/website-via-cf/?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
|
+
- Tue, 21 May 2013 12:19:26 UTC
|
15
|
+
Content-Type:
|
16
|
+
- ''
|
17
|
+
Content-Length:
|
18
|
+
- '298'
|
19
|
+
Authorization:
|
20
|
+
- AWS foo:foo
|
21
|
+
response:
|
22
|
+
status:
|
23
|
+
code: 200
|
24
|
+
message: OK
|
25
|
+
headers:
|
26
|
+
X-Amz-Id-2:
|
27
|
+
- GtZ029e+Hum8PqKTmknJf1cB2zfnh4ug71/wVX/uDySx4ETpPfU7YcoSj7nPkUeM
|
28
|
+
X-Amz-Request-Id:
|
29
|
+
- A12EA2F4AEDA0657
|
30
|
+
Date:
|
31
|
+
- Tue, 21 May 2013 12:19:33 GMT
|
32
|
+
Content-Length:
|
33
|
+
- '0'
|
34
|
+
Server:
|
35
|
+
- AmazonS3
|
36
|
+
body:
|
37
|
+
encoding: US-ASCII
|
38
|
+
string: ''
|
39
|
+
http_version:
|
40
|
+
recorded_at: Tue, 21 May 2013 12:19:28 GMT
|
41
|
+
- request:
|
42
|
+
method: put
|
43
|
+
uri: https://s3.amazonaws.com/website-via-cf/?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:::website-via-cf/*\"]\n }]\n }"
|
50
|
+
headers:
|
51
|
+
Date:
|
52
|
+
- Tue, 21 May 2013 12:19:28 UTC
|
53
|
+
Content-Type:
|
54
|
+
- ''
|
55
|
+
Content-Length:
|
56
|
+
- '283'
|
57
|
+
Authorization:
|
58
|
+
- AWS foo:foo
|
59
|
+
response:
|
60
|
+
status:
|
61
|
+
code: 204
|
62
|
+
message: No Content
|
63
|
+
headers:
|
64
|
+
X-Amz-Id-2:
|
65
|
+
- qWuZ40Adyj2wa4i/ywb3E5mjypu0HYJpQOuAhBXkQskqf7O3NBVTeZ2vQT2UzspU
|
66
|
+
X-Amz-Request-Id:
|
67
|
+
- E998426FB9B27996
|
68
|
+
Date:
|
69
|
+
- Tue, 21 May 2013 12:19:34 GMT
|
70
|
+
Server:
|
71
|
+
- AmazonS3
|
72
|
+
body:
|
73
|
+
encoding: US-ASCII
|
74
|
+
string: ''
|
75
|
+
http_version:
|
76
|
+
recorded_at: Tue, 21 May 2013 12:19:29 GMT
|
77
|
+
- request:
|
78
|
+
method: get
|
79
|
+
uri: https://cloudfront.amazonaws.com/2012-07-01/distribution/E13NX4HCPUP9BP/config
|
80
|
+
body:
|
81
|
+
encoding: US-ASCII
|
82
|
+
string: ''
|
83
|
+
headers:
|
84
|
+
Date:
|
85
|
+
- Tue, 21 May 2013 12:19:29 UTC
|
86
|
+
Content-Type:
|
87
|
+
- ''
|
88
|
+
Content-Length:
|
89
|
+
- '0'
|
90
|
+
Authorization:
|
91
|
+
- AWS foo:foo
|
92
|
+
response:
|
93
|
+
status:
|
94
|
+
code: 200
|
95
|
+
message: OK
|
96
|
+
headers:
|
97
|
+
X-Amzn-Requestid:
|
98
|
+
- b29153e6-c210-11e2-8f84-efb8518943a7
|
99
|
+
Etag:
|
100
|
+
- E234UVYUVB06TU
|
101
|
+
Content-Type:
|
102
|
+
- text/xml
|
103
|
+
Content-Length:
|
104
|
+
- '1150'
|
105
|
+
Date:
|
106
|
+
- Tue, 21 May 2013 12:19:33 GMT
|
107
|
+
body:
|
108
|
+
encoding: US-ASCII
|
109
|
+
string: ! '<?xml version="1.0"?>
|
110
|
+
|
111
|
+
<DistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2012-07-01/"><CallerReference>1369126181889</CallerReference><Aliases><Quantity>0</Quantity></Aliases><DefaultRootObject>index.json</DefaultRootObject><Origins><Quantity>1</Quantity><Items><Origin><Id>website-via-cf-S3-origin</Id><DomainName>website-via-cf.s3.amazonaws.com</DomainName><S3OriginConfig><OriginAccessIdentity></OriginAccessIdentity></S3OriginConfig></Origin></Items></Origins><DefaultCacheBehavior><TargetOriginId>website-via-cf-S3-origin</TargetOriginId><ForwardedValues><QueryString>true</QueryString><Cookies><Forward>all</Forward></Cookies></ForwardedValues><TrustedSigners><Enabled>false</Enabled><Quantity>0</Quantity></TrustedSigners><ViewerProtocolPolicy>allow-all</ViewerProtocolPolicy><MinTTL>3600</MinTTL></DefaultCacheBehavior><CacheBehaviors><Quantity>0</Quantity></CacheBehaviors><Comment>Created
|
112
|
+
by the configure-s3-website gem</Comment><Logging><Enabled>false</Enabled><IncludeCookies>false</IncludeCookies><Bucket></Bucket><Prefix></Prefix></Logging><PriceClass>PriceClass_All</PriceClass><Enabled>true</Enabled></DistributionConfig>'
|
113
|
+
http_version:
|
114
|
+
recorded_at: Tue, 21 May 2013 12:19:29 GMT
|
115
|
+
- request:
|
116
|
+
method: put
|
117
|
+
uri: https://cloudfront.amazonaws.com/2012-07-01/distribution/E13NX4HCPUP9BP/config
|
118
|
+
body:
|
119
|
+
encoding: US-ASCII
|
120
|
+
string: ! "\n <DistributionConfig xmlns=\"http://cloudfront.amazonaws.com/doc/2012-07-01/\">\n
|
121
|
+
\ <Origins>\n <Quantity>1</Quantity>\n <Items>\n <Origin>\n
|
122
|
+
\ <Id>website-via-cf-S3-origin</Id>\n <DomainName>website-via-cf.s3.amazonaws.com</DomainName>\n
|
123
|
+
\ <S3OriginConfig>\n <OriginAccessIdentity></OriginAccessIdentity>\n
|
124
|
+
\ </S3OriginConfig>\n </Origin>\n </Items>\n
|
125
|
+
\ </Origins>\n \n<CallerReference>1369126181889</CallerReference>\n<DefaultRootObject>index.json</DefaultRootObject>\n<Logging>\n
|
126
|
+
\ <Enabled>false</Enabled>\n <IncludeCookies>false</IncludeCookies>\n <Bucket></Bucket>\n
|
127
|
+
\ <Prefix></Prefix></Logging>\n<Enabled>true</Enabled>\n<Comment>Updated by
|
128
|
+
the configure-s3-website gem</Comment>\n<Aliases>\n <Quantity>0</Quantity></Aliases>\n<DefaultCacheBehavior>\n
|
129
|
+
\ <TargetOriginId>website-via-cf-S3-origin</TargetOriginId>\n <TrustedSigners>\n
|
130
|
+
\ <Enabled>false</Enabled>\n <Quantity>0</Quantity></TrustedSigners>\n
|
131
|
+
\ <ForwardedValues>\n <QueryString>true</QueryString>\n <Cookies>\n
|
132
|
+
\ <Forward>all</Forward></Cookies></ForwardedValues>\n <ViewerProtocolPolicy>allow-all</ViewerProtocolPolicy>\n
|
133
|
+
\ <MinTTL>3600</MinTTL></DefaultCacheBehavior>\n<CacheBehaviors>\n <Quantity>0</Quantity></CacheBehaviors>\n<PriceClass>PriceClass_All</PriceClass>\n
|
134
|
+
\ </DistributionConfig>\n "
|
135
|
+
headers:
|
136
|
+
Date:
|
137
|
+
- Tue, 21 May 2013 12:19:29 UTC
|
138
|
+
Content-Type:
|
139
|
+
- ''
|
140
|
+
Content-Length:
|
141
|
+
- '1381'
|
142
|
+
Authorization:
|
143
|
+
- AWS foo:foo
|
144
|
+
If-Match:
|
145
|
+
- E234UVYUVB06TU
|
146
|
+
response:
|
147
|
+
status:
|
148
|
+
code: 200
|
149
|
+
message: OK
|
150
|
+
headers:
|
151
|
+
X-Amzn-Requestid:
|
152
|
+
- b2e5dc3f-c210-11e2-83d0-6be74820cf4d
|
153
|
+
Etag:
|
154
|
+
- E257DGI0PKML9Q
|
155
|
+
Content-Type:
|
156
|
+
- text/xml
|
157
|
+
Content-Length:
|
158
|
+
- '1499'
|
159
|
+
Date:
|
160
|
+
- Tue, 21 May 2013 12:19:34 GMT
|
161
|
+
body:
|
162
|
+
encoding: US-ASCII
|
163
|
+
string: ! '<?xml version="1.0"?>
|
164
|
+
|
165
|
+
<Distribution xmlns="http://cloudfront.amazonaws.com/doc/2012-07-01/"><Id>E13NX4HCPUP9BP</Id><Status>InProgress</Status><LastModifiedTime>2013-05-21T12:19:35.353Z</LastModifiedTime><InProgressInvalidationBatches>0</InProgressInvalidationBatches><DomainName>d2u7lu18vsudvw.cloudfront.net</DomainName><ActiveTrustedSigners><Enabled>false</Enabled><Quantity>0</Quantity></ActiveTrustedSigners><DistributionConfig><CallerReference>1369126181889</CallerReference><Aliases><Quantity>0</Quantity></Aliases><DefaultRootObject>index.json</DefaultRootObject><Origins><Quantity>1</Quantity><Items><Origin><Id>website-via-cf-S3-origin</Id><DomainName>website-via-cf.s3.amazonaws.com</DomainName><S3OriginConfig><OriginAccessIdentity></OriginAccessIdentity></S3OriginConfig></Origin></Items></Origins><DefaultCacheBehavior><TargetOriginId>website-via-cf-S3-origin</TargetOriginId><ForwardedValues><QueryString>true</QueryString><Cookies><Forward>all</Forward></Cookies></ForwardedValues><TrustedSigners><Enabled>false</Enabled><Quantity>0</Quantity></TrustedSigners><ViewerProtocolPolicy>allow-all</ViewerProtocolPolicy><MinTTL>3600</MinTTL></DefaultCacheBehavior><CacheBehaviors><Quantity>0</Quantity></CacheBehaviors><Comment>Updated
|
166
|
+
by the configure-s3-website gem</Comment><Logging><Enabled>false</Enabled><IncludeCookies>false</IncludeCookies><Bucket></Bucket><Prefix></Prefix></Logging><PriceClass>PriceClass_All</PriceClass><Enabled>true</Enabled></DistributionConfig></Distribution>'
|
167
|
+
http_version:
|
168
|
+
recorded_at: Tue, 21 May 2013 12:19:30 GMT
|
169
|
+
recorded_with: VCR 2.3.0
|
@@ -1,4 +1,4 @@
|
|
1
|
-
Feature: Create CloudFront distribution
|
1
|
+
Feature: Create a CloudFront distribution
|
2
2
|
|
3
3
|
@create-cf-dist
|
4
4
|
Scenario: The user wants to deliver his website via CloudFront
|
@@ -18,6 +18,31 @@ Feature: Create CloudFront distribution
|
|
18
18
|
Added setting 'cloudfront_distribution_id: E45H2VN49KPDU' into features/support/sample_config_files/create_cf_dist.yml
|
19
19
|
|
20
20
|
"""
|
21
|
+
And the config file should contain the distribution id
|
22
|
+
|
23
|
+
@create-cf-dist
|
24
|
+
Scenario: The user wants create a CloudFront distribution with his own settings
|
25
|
+
Given I answer 'yes' to 'do you want to use CloudFront'
|
26
|
+
When I run the configure-s3-website command with parameters
|
27
|
+
| option | value |
|
28
|
+
| --config-file | features/support/sample_config_files/create_cf_dist_with_custom_configs.yml |
|
29
|
+
Then the output should be
|
30
|
+
"""
|
31
|
+
Bucket website-via-cf now functions as a website
|
32
|
+
Bucket website-via-cf is now readable to the whole world
|
33
|
+
No redirects to configure for website-via-cf bucket
|
34
|
+
Do you want to deliver your website via CloudFront, the CDN of Amazon? [y/N]
|
35
|
+
The distribution E45H2VN49KPDU at d3feoe9t5ufu01.cloudfront.net now delivers the bucket website-via-cf
|
36
|
+
Please allow up to 15 minutes for the distribution to initialise
|
37
|
+
For more information on the distribution, see https://console.aws.amazon.com/cloudfront
|
38
|
+
Added setting 'cloudfront_distribution_id: E45H2VN49KPDU' into features/support/sample_config_files/create_cf_dist_with_custom_configs.yml
|
39
|
+
Applied custom distribution settings:
|
40
|
+
default_cache_behavior:
|
41
|
+
min_TTL: 600
|
42
|
+
default_root_object: index.json
|
43
|
+
|
44
|
+
"""
|
45
|
+
And the config file should contain the distribution id
|
21
46
|
|
22
47
|
@create-cf-dist
|
23
48
|
Scenario: The user wants to deliver his website via CloudFront and see details on the new distribution
|
@@ -149,6 +174,7 @@ Feature: Create CloudFront distribution
|
|
149
174
|
Added setting 'cloudfront_distribution_id: E45H2VN49KPDU' into features/support/sample_config_files/create_cf_dist.yml
|
150
175
|
|
151
176
|
"""
|
177
|
+
And the config file should contain the distribution id
|
152
178
|
|
153
179
|
@bucket-exists
|
154
180
|
Scenario: The user already has CloudFront configured in his configuration file
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Feature: Modify an existing CloudFront distribution
|
2
|
+
|
3
|
+
@apply-configs-on-cf-dist
|
4
|
+
Scenario: The user wants to modify an existing CloudFront distribution
|
5
|
+
When I run the configure-s3-website command with parameters
|
6
|
+
| option | value |
|
7
|
+
| --config-file | features/support/sample_config_files/apply_configs_on_cf_dist.yml |
|
8
|
+
Then the output should be
|
9
|
+
"""
|
10
|
+
Bucket website-via-cf now functions as a website
|
11
|
+
Bucket website-via-cf is now readable to the whole world
|
12
|
+
No redirects to configure for website-via-cf bucket
|
13
|
+
Detected an existing CloudFront distribution (id E13NX4HCPUP9BP) ...
|
14
|
+
Applied custom distribution settings:
|
15
|
+
default_cache_behavior:
|
16
|
+
min_TTL: 3600
|
17
|
+
default_root_object: index.json
|
18
|
+
|
19
|
+
"""
|
@@ -3,7 +3,8 @@ require 'rspec'
|
|
3
3
|
When /^I run the configure-s3-website command with parameters$/ do |table|
|
4
4
|
options, optparse = ConfigureS3Website::CLI.optparse_and_options
|
5
5
|
optparse.parse! args_array_from_cucumber_table(table)
|
6
|
-
@
|
6
|
+
@config_source = options[:config_source]
|
7
|
+
@reset = create_reset_config_file_function @config_source.description
|
7
8
|
@console_output = capture_stdout {
|
8
9
|
ConfigureS3Website::Runner.run(options, stub_stdin)
|
9
10
|
}
|
@@ -21,6 +22,13 @@ Then /^the output should include$/ do |expected_console_output|
|
|
21
22
|
@console_output.should include(expected_console_output)
|
22
23
|
end
|
23
24
|
|
25
|
+
Then /^the config file should contain the distribution id$/ do
|
26
|
+
config_file_path = @config_source.description
|
27
|
+
File.open(config_file_path, 'r').read.should include(
|
28
|
+
"cloudfront_distribution_id: #{@config_source.cloudfront_distribution_id}"
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
24
32
|
def args_array_from_cucumber_table(table)
|
25
33
|
args = []
|
26
34
|
table.hashes.map do |entry|
|
data/features/support/vcr.rb
CHANGED
@@ -3,28 +3,82 @@ require "rexml/xpath"
|
|
3
3
|
|
4
4
|
module ConfigureS3Website
|
5
5
|
class CloudFrontClient
|
6
|
+
def self.apply_distribution_config(options)
|
7
|
+
config_source = options[:config_source]
|
8
|
+
puts "Detected an existing CloudFront distribution (id #{config_source.cloudfront_distribution_id}) ..."
|
9
|
+
|
10
|
+
# Get caller reference and ETag (will be required by the PUT config resource)
|
11
|
+
response = HttpHelper.call_cloudfront_api(
|
12
|
+
path = "/2012-07-01/distribution/#{config_source.cloudfront_distribution_id}/config",
|
13
|
+
method = Net::HTTP::Get,
|
14
|
+
body = '',
|
15
|
+
config_source
|
16
|
+
)
|
17
|
+
etag = response['ETag']
|
18
|
+
caller_reference = REXML::XPath.first(
|
19
|
+
REXML::Document.new(response.body),
|
20
|
+
'/DistributionConfig/CallerReference'
|
21
|
+
).get_text.to_s
|
22
|
+
|
23
|
+
# Call the PUT config resource with the caller reference and ETag
|
24
|
+
custom_distribution_config = config_source.cloudfront_distribution_config || {}
|
25
|
+
custom_distribution_config_with_caller_ref = custom_distribution_config.merge({
|
26
|
+
'caller_reference' => caller_reference,
|
27
|
+
'comment' => 'Updated by the configure-s3-website gem'
|
28
|
+
})
|
29
|
+
HttpHelper.call_cloudfront_api(
|
30
|
+
path = "/2012-07-01/distribution/#{options[:config_source].cloudfront_distribution_id}/config",
|
31
|
+
method = Net::HTTP::Put,
|
32
|
+
body = distribution_config_xml(
|
33
|
+
config_source,
|
34
|
+
custom_distribution_config_with_caller_ref
|
35
|
+
),
|
36
|
+
config_source,
|
37
|
+
headers = { 'If-Match' => etag }
|
38
|
+
)
|
39
|
+
|
40
|
+
# Report
|
41
|
+
unless custom_distribution_config.empty?
|
42
|
+
print_report_on_custom_distribution_config custom_distribution_config
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
6
46
|
def self.create_distribution_if_user_agrees(options, standard_input)
|
7
47
|
puts 'Do you want to deliver your website via CloudFront, the CDN of Amazon? [y/N]'
|
8
48
|
case standard_input.gets.chomp
|
9
|
-
when /(y|Y)/ then
|
49
|
+
when /(y|Y)/ then create_distribution options
|
10
50
|
end
|
11
51
|
end
|
12
52
|
|
13
53
|
private
|
14
54
|
|
15
|
-
def self.
|
55
|
+
def self.create_distribution(options)
|
16
56
|
config_source = options[:config_source]
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
57
|
+
custom_distribution_config = config_source.cloudfront_distribution_config || {}
|
58
|
+
response_xml = REXML::Document.new(
|
59
|
+
HttpHelper.call_cloudfront_api(
|
60
|
+
'/2012-07-01/distribution',
|
61
|
+
Net::HTTP::Post,
|
62
|
+
distribution_config_xml(config_source, custom_distribution_config),
|
63
|
+
config_source
|
64
|
+
).body
|
22
65
|
)
|
23
|
-
response_xml = REXML::Document.new(response.body)
|
24
66
|
dist_id = REXML::XPath.first(response_xml, '/Distribution/Id').get_text
|
25
67
|
print_report_on_new_dist response_xml, dist_id, options
|
26
68
|
config_source.cloudfront_distribution_id = dist_id.to_s
|
27
69
|
puts " Added setting 'cloudfront_distribution_id: #{dist_id}' into #{config_source.description}"
|
70
|
+
unless custom_distribution_config.empty?
|
71
|
+
print_report_on_custom_distribution_config custom_distribution_config
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.print_report_on_custom_distribution_config(custom_distribution_config, left_padding = 4)
|
76
|
+
puts ' Applied custom distribution settings:'
|
77
|
+
puts custom_distribution_config.
|
78
|
+
to_yaml.
|
79
|
+
to_s.
|
80
|
+
gsub("---\n", '').
|
81
|
+
gsub(/^/, padding(left_padding))
|
28
82
|
end
|
29
83
|
|
30
84
|
def self.print_report_on_new_dist(response_xml, dist_id, options)
|
@@ -35,21 +89,19 @@ module ConfigureS3Website
|
|
35
89
|
puts ' For more information on the distribution, see https://console.aws.amazon.com/cloudfront'
|
36
90
|
if options[:verbose]
|
37
91
|
puts ' Below is the response from the CloudFront API:'
|
38
|
-
|
92
|
+
print_verbose_response_from_cloudfront(response_xml)
|
39
93
|
end
|
40
94
|
end
|
41
95
|
|
42
|
-
def self.
|
96
|
+
def self.print_verbose_response_from_cloudfront(response_xml, left_padding = 4)
|
43
97
|
lines = []
|
44
98
|
response_xml.write(lines, 2)
|
45
|
-
padding = ""
|
46
|
-
left_padding.times { padding << " " }
|
47
99
|
puts lines.join().
|
48
|
-
gsub(/^/, "" + padding).
|
100
|
+
gsub(/^/, "" + padding(left_padding)).
|
49
101
|
gsub(/\s$/, "")
|
50
102
|
end
|
51
103
|
|
52
|
-
def self.distribution_config_xml(config_source, custom_cf_settings
|
104
|
+
def self.distribution_config_xml(config_source, custom_cf_settings)
|
53
105
|
%|
|
54
106
|
<DistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2012-07-01/">
|
55
107
|
<Origins>
|
@@ -65,14 +117,19 @@ module ConfigureS3Website
|
|
65
117
|
</Items>
|
66
118
|
</Origins>
|
67
119
|
#{
|
68
|
-
|
69
|
-
|
70
|
-
|
120
|
+
require 'deep_merge'
|
121
|
+
settings = default_cloudfront_settings config_source
|
122
|
+
settings.deep_merge! custom_cf_settings
|
123
|
+
XmlHelper.hash_to_api_xml(settings)
|
71
124
|
}
|
72
125
|
</DistributionConfig>
|
73
126
|
|
|
74
127
|
end
|
75
128
|
|
129
|
+
# Changing these default settings probably necessitates a
|
130
|
+
# backward incompatible release.
|
131
|
+
#
|
132
|
+
# If you change these settings, remember to update also the README.md.
|
76
133
|
def self.default_cloudfront_settings(config_source)
|
77
134
|
{
|
78
135
|
'caller_reference' => 'configure-s3-website gem ' + Time.now.to_s,
|
@@ -101,7 +158,7 @@ module ConfigureS3Website
|
|
101
158
|
}
|
102
159
|
},
|
103
160
|
'viewer_protocol_policy' => 'allow-all',
|
104
|
-
'min_TTL' =>
|
161
|
+
'min_TTL' => '86400'
|
105
162
|
},
|
106
163
|
'cache_behaviors' => {
|
107
164
|
'quantity' => '0'
|
@@ -113,5 +170,11 @@ module ConfigureS3Website
|
|
113
170
|
def self.origin_id(config_source)
|
114
171
|
"#{config_source.s3_bucket_name}-S3-origin"
|
115
172
|
end
|
173
|
+
|
174
|
+
def self.padding(amount)
|
175
|
+
padding = ''
|
176
|
+
amount.times { padding << " " }
|
177
|
+
padding
|
178
|
+
end
|
116
179
|
end
|
117
180
|
end
|
@@ -5,21 +5,34 @@ module ConfigureS3Website
|
|
5
5
|
date = Time.now.utc.strftime("%a, %d %b %Y %H:%M:%S %Z")
|
6
6
|
digest = create_s3_digest(path, method, config_source, date)
|
7
7
|
self.call_api(
|
8
|
-
path,
|
8
|
+
path,
|
9
|
+
method,
|
10
|
+
body,
|
11
|
+
config_source,
|
12
|
+
endpoint.hostname,
|
13
|
+
digest,
|
14
|
+
date
|
9
15
|
)
|
10
16
|
end
|
11
17
|
|
12
|
-
def self.call_cloudfront_api(path, method, body, config_source)
|
18
|
+
def self.call_cloudfront_api(path, method, body, config_source, headers = {})
|
13
19
|
date = Time.now.utc.strftime("%a, %d %b %Y %H:%M:%S %Z")
|
14
20
|
digest = create_cloudfront_digest(config_source, date)
|
15
21
|
self.call_api(
|
16
|
-
path,
|
22
|
+
path,
|
23
|
+
method,
|
24
|
+
body,
|
25
|
+
config_source,
|
26
|
+
'cloudfront.amazonaws.com',
|
27
|
+
digest,
|
28
|
+
date,
|
29
|
+
headers
|
17
30
|
)
|
18
31
|
end
|
19
32
|
|
20
33
|
private
|
21
34
|
|
22
|
-
def self.call_api(path, method, body, config_source, hostname, digest, date)
|
35
|
+
def self.call_api(path, method, body, config_source, hostname, digest, date, additional_headers = {})
|
23
36
|
url = "https://#{hostname}#{path}"
|
24
37
|
uri = URI.parse(url)
|
25
38
|
req = method.new(uri.to_s)
|
@@ -28,9 +41,10 @@ module ConfigureS3Website
|
|
28
41
|
'Content-Type' => '',
|
29
42
|
'Content-Length' => body.length.to_s,
|
30
43
|
'Authorization' => "AWS %s:%s" % [config_source.s3_access_key_id, digest]
|
31
|
-
})
|
44
|
+
}.merge(additional_headers))
|
32
45
|
req.body = body
|
33
46
|
http = Net::HTTP.new(uri.host, uri.port)
|
47
|
+
# http.set_debug_output $stderr
|
34
48
|
http.use_ssl = true
|
35
49
|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
36
50
|
res = http.request(req)
|
@@ -2,16 +2,30 @@ module ConfigureS3Website
|
|
2
2
|
class Runner
|
3
3
|
def self.run(options, standard_input = STDIN)
|
4
4
|
S3Client.configure_website options
|
5
|
+
maybe_create_or_update_cloudfront options, standard_input
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def self.maybe_create_or_update_cloudfront(options, standard_input)
|
5
11
|
unless user_already_has_cf_configured options
|
6
12
|
CloudFrontClient.create_distribution_if_user_agrees options, standard_input
|
13
|
+
return
|
14
|
+
end
|
15
|
+
if user_already_has_cf_configured(options) and user_has_custom_cf_dist_config(options)
|
16
|
+
CloudFrontClient.apply_distribution_config options
|
17
|
+
return
|
7
18
|
end
|
8
19
|
end
|
9
20
|
|
10
|
-
private
|
11
|
-
|
12
21
|
def self.user_already_has_cf_configured(options)
|
13
22
|
config_source = options[:config_source]
|
14
23
|
config_source.cloudfront_distribution_id
|
15
24
|
end
|
25
|
+
|
26
|
+
def self.user_has_custom_cf_dist_config(options)
|
27
|
+
config_source = options[:config_source]
|
28
|
+
config_source.cloudfront_distribution_config
|
29
|
+
end
|
16
30
|
end
|
17
31
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'configure-s3-website'
|
3
|
+
require "rexml/document"
|
4
|
+
require "rexml/xpath"
|
5
|
+
|
6
|
+
describe ConfigureS3Website::CloudFrontClient do
|
7
|
+
context '#distribution_config_xml' do
|
8
|
+
describe 'letting the user to override the default values' do
|
9
|
+
let(:config_source) {
|
10
|
+
mock = double('config_source')
|
11
|
+
mock.stub(:s3_bucket_name).and_return('test-bucket')
|
12
|
+
mock.stub(:s3_endpoint).and_return(nil)
|
13
|
+
mock
|
14
|
+
}
|
15
|
+
|
16
|
+
let(:custom_settings) {
|
17
|
+
{ 'default_cache_behavior' => { 'min_TTL' => '987' } }
|
18
|
+
}
|
19
|
+
|
20
|
+
let(:distribution_config_xml) {
|
21
|
+
REXML::Document.new(
|
22
|
+
ConfigureS3Website::CloudFrontClient.send(
|
23
|
+
:distribution_config_xml,
|
24
|
+
config_source,
|
25
|
+
custom_settings
|
26
|
+
)
|
27
|
+
)
|
28
|
+
}
|
29
|
+
|
30
|
+
it 'allows the user to override default CloudFront settings' do
|
31
|
+
REXML::XPath.first(
|
32
|
+
distribution_config_xml,
|
33
|
+
'/DistributionConfig/DefaultCacheBehavior/MinTTL'
|
34
|
+
).get_text.to_s.should eq('987')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'retains the default values that are not overriden' do
|
38
|
+
REXML::XPath.first(
|
39
|
+
distribution_config_xml,
|
40
|
+
'/DistributionConfig/DefaultCacheBehavior/ViewerProtocolPolicy'
|
41
|
+
).get_text.to_s.should eq('allow-all')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'inferring //Origins/Items/Origin/DomainName' do
|
46
|
+
let(:config_source) {
|
47
|
+
mock = double('config_source')
|
48
|
+
mock.stub(:s3_bucket_name).and_return('test-bucket')
|
49
|
+
mock.stub(:s3_endpoint).and_return('us-west-1')
|
50
|
+
mock
|
51
|
+
}
|
52
|
+
|
53
|
+
let(:distribution_config_xml) {
|
54
|
+
REXML::Document.new(
|
55
|
+
ConfigureS3Website::CloudFrontClient.send(
|
56
|
+
:distribution_config_xml,
|
57
|
+
config_source,
|
58
|
+
custom_distribution_config = {}
|
59
|
+
)
|
60
|
+
)
|
61
|
+
}
|
62
|
+
|
63
|
+
it 'honors the endpoint of the S3 bucket' do
|
64
|
+
REXML::XPath.first(
|
65
|
+
distribution_config_xml,
|
66
|
+
'/DistributionConfig/Origins/Items/Origin/DomainName'
|
67
|
+
).get_text.to_s.should eq('test-bucket.s3-us-west-1.amazonaws.com')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: configure-s3-website
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,24 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05-
|
12
|
+
date: 2013-05-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: deep_merge
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - '='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.0.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - '='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.0.0
|
14
30
|
- !ruby/object:Gem::Dependency
|
15
31
|
name: rspec
|
16
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -139,6 +155,7 @@ files:
|
|
139
155
|
- bin/configure-s3-website
|
140
156
|
- changelog.md
|
141
157
|
- configure-s3-website.gemspec
|
158
|
+
- features/cassettes/cucumber_tags/apply-configs-on-cf-dist.yml
|
142
159
|
- features/cassettes/cucumber_tags/bucket-does-not-exist-in-tokyo.yml
|
143
160
|
- features/cassettes/cucumber_tags/bucket-does-not-exist.yml
|
144
161
|
- features/cassettes/cucumber_tags/bucket-exists.yml
|
@@ -147,10 +164,13 @@ files:
|
|
147
164
|
- features/config_file.feature
|
148
165
|
- features/configure_bucket.feature
|
149
166
|
- features/create_cloudfront_dist.feature
|
167
|
+
- features/modify_cloudfront_dist.feature
|
150
168
|
- features/step_definitions/steps.rb
|
151
169
|
- features/support/env.rb
|
170
|
+
- features/support/sample_config_files/apply_configs_on_cf_dist.yml
|
152
171
|
- features/support/sample_config_files/config_with_cloudfront_distribution_id.yml
|
153
172
|
- features/support/sample_config_files/create_cf_dist.yml
|
173
|
+
- features/support/sample_config_files/create_cf_dist_with_custom_configs.yml
|
154
174
|
- features/support/sample_config_files/endpoint_tokyo.yml
|
155
175
|
- features/support/sample_config_files/redirects.yml
|
156
176
|
- features/support/sample_config_files/s3_config_with_existing_bucket.yml
|
@@ -166,6 +186,7 @@ files:
|
|
166
186
|
- lib/configure-s3-website/s3_client.rb
|
167
187
|
- lib/configure-s3-website/version.rb
|
168
188
|
- lib/configure-s3-website/xml_helper.rb
|
189
|
+
- spec/cloudfront_client_spec.rb
|
169
190
|
- spec/config_source/file_config_source_spec.rb
|
170
191
|
- spec/s3_client_spec.rb
|
171
192
|
- spec/sample_files/_config_file.yml
|
@@ -186,7 +207,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
186
207
|
version: '0'
|
187
208
|
segments:
|
188
209
|
- 0
|
189
|
-
hash:
|
210
|
+
hash: 2506779264415997513
|
190
211
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
212
|
none: false
|
192
213
|
requirements:
|
@@ -195,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
216
|
version: '0'
|
196
217
|
segments:
|
197
218
|
- 0
|
198
|
-
hash:
|
219
|
+
hash: 2506779264415997513
|
199
220
|
requirements: []
|
200
221
|
rubyforge_project:
|
201
222
|
rubygems_version: 1.8.25
|
@@ -203,6 +224,7 @@ signing_key:
|
|
203
224
|
specification_version: 3
|
204
225
|
summary: Configure your AWS S3 bucket to function as a web site
|
205
226
|
test_files:
|
227
|
+
- features/cassettes/cucumber_tags/apply-configs-on-cf-dist.yml
|
206
228
|
- features/cassettes/cucumber_tags/bucket-does-not-exist-in-tokyo.yml
|
207
229
|
- features/cassettes/cucumber_tags/bucket-does-not-exist.yml
|
208
230
|
- features/cassettes/cucumber_tags/bucket-exists.yml
|
@@ -211,15 +233,19 @@ test_files:
|
|
211
233
|
- features/config_file.feature
|
212
234
|
- features/configure_bucket.feature
|
213
235
|
- features/create_cloudfront_dist.feature
|
236
|
+
- features/modify_cloudfront_dist.feature
|
214
237
|
- features/step_definitions/steps.rb
|
215
238
|
- features/support/env.rb
|
239
|
+
- features/support/sample_config_files/apply_configs_on_cf_dist.yml
|
216
240
|
- features/support/sample_config_files/config_with_cloudfront_distribution_id.yml
|
217
241
|
- features/support/sample_config_files/create_cf_dist.yml
|
242
|
+
- features/support/sample_config_files/create_cf_dist_with_custom_configs.yml
|
218
243
|
- features/support/sample_config_files/endpoint_tokyo.yml
|
219
244
|
- features/support/sample_config_files/redirects.yml
|
220
245
|
- features/support/sample_config_files/s3_config_with_existing_bucket.yml
|
221
246
|
- features/support/sample_config_files/s3_config_with_non-existing_bucket.yml
|
222
247
|
- features/support/vcr.rb
|
248
|
+
- spec/cloudfront_client_spec.rb
|
223
249
|
- spec/config_source/file_config_source_spec.rb
|
224
250
|
- spec/s3_client_spec.rb
|
225
251
|
- spec/sample_files/_config_file.yml
|