configure-s3-website 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.8.7
4
3
  - 1.9.2
5
4
  - 1.9.3
5
+ - 2.0.0
data/README.md CHANGED
@@ -31,6 +31,33 @@ following command:
31
31
  Congratulations! You now have an S3 bucket that can act as a website server for
32
32
  you.
33
33
 
34
+ ### Deliver your website via CloudFront
35
+
36
+ `configure-s3-website` can create a CloudFront distribution for you. It will ask
37
+ you whether you want to deliver your website via the CDN. If you answer yes,
38
+ `configure-s3-website` will create a CloudFront distribution that has the
39
+ configured S3 bucket as its origin. In addition, it will add the entry
40
+ `cloudfront_distribution_id: [id-of-the-new-distribution]` into your
41
+ configuration file.
42
+
43
+ CloudFront can be configured in various ways. However, the distribution created
44
+ by `configure-s3-website` uses sensible defaults for an S3-based website and
45
+ thus saves you the burden of figuring out how to configure CloudFront. For
46
+ example, it assumes that your default root object is *index.html*.
47
+
48
+ You can see all the settings this gem applies on the new distribution by running
49
+ the command in verbose mode:
50
+
51
+ configure-s3-website --config-file config.yml --verbose
52
+
53
+ Note that if you already have the key `cloudfront_distribution_id` in your
54
+ configuration file, `configure-s3-website` will not create a new distribution.
55
+ Conversely, if you remove the `cloudfront_distribution_id` key from the file and
56
+ run `configure-s3-website` again, it will create you a new distribution.
57
+
58
+ If you want to, you can tune the distribution settings on the management console
59
+ at <https://console.aws.amazon.com/cloudfront>.
60
+
34
61
  ### Specifying a non-standard S3 endpoint
35
62
 
36
63
  By default, `configure-s3-website` creates the S3 website into the US Standard
@@ -67,43 +94,23 @@ supports. All you have to do is to replace the uppercase letter in AWS XML with
67
94
  an underscore and an undercase version of the same letter. For example,
68
95
  `KeyPrefixEquals` becomes `key_prefix_equals` in the config file.
69
96
 
97
+ Apply the rules by invoking `configure-s3-website --config [your-config-file]`
98
+ on the command-line interface. You can verify the results by looking at your
99
+ bucket on the [S3 console](https://console.aws.amazon.com/s3/home).
100
+
70
101
  ## How does `configure-s3-website` work?
71
102
 
72
- It calls the [PUT Bucket
73
- website](http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTwebsite.html)
74
- API with the following XML:
75
-
76
- ```xml
77
- <WebsiteConfiguration xmlns='http://s3.amazonaws.com/doc/2006-03-01/'>
78
- <IndexDocument>
79
- <Suffix>index.html</Suffix>
80
- </IndexDocument>
81
- <ErrorDocument>
82
- <Key>error.html</Key>
83
- </ErrorDocument>
84
- </WebsiteConfiguration>
85
- ```
103
+ `configure-s3-website` uses the AWS REST API of S3 for creating and modifying
104
+ the bucket. In brief, it does the following things:
86
105
 
87
- Then **it makes all the objects on the bucket visible to the whole world** by
88
- calling the [PUT Bucket
89
- policy](http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTpolicy.html)
90
- API with the following JSON:
91
-
92
- ```json
93
- {
94
- "Version":"2008-10-17",
95
- "Statement":[{
96
- "Sid":"PublicReadForGetBucketObjects",
97
- "Effect":"Allow",
98
- "Principal": { "AWS": "*" },
99
- "Action":["s3:GetObject"],
100
- "Resource":["arn:aws:s3:::your-bucket-name/*"]
101
- }]
102
- }
103
- ```
106
+ 1. Create a bucket for you (if it does not yet exist)
107
+ 2. Add the website configuration on the bucket via the [website REST
108
+ API](http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTwebsite.html)
109
+ 3. Make the bucket **readable to the whole world**
110
+ 4. Apply the redirect (a.k.a routing) rules on the bucket website
104
111
 
105
- If you define `routing_rules` in the config file, `configure-s3-website` will
106
- make an additional call to the AWS API.
112
+ In addition, if you instruct `configure-s3-website` to create a CloudFront
113
+ distribution to you, it will call the [CloudFront POST Distribution](http://docs.aws.amazon.com/AmazonCloudFront/latest/APIReference/CreateDistribution.html) API.
107
114
 
108
115
  ## Development
109
116
 
@@ -118,6 +125,10 @@ Big thanks to the following contributors (in alphabetical order):
118
125
  * SlawD
119
126
  * Steve Schwartz
120
127
 
128
+ ## Supported Ruby versions
129
+
130
+ The file `.travis.yml` defines the supported Ruby versions.
131
+
121
132
  ## License
122
133
 
123
134
  See the file LICENSE.
@@ -5,30 +5,8 @@ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
5
5
  require 'optparse'
6
6
  require 'configure-s3-website'
7
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
8
+ options, optparse = ConfigureS3Website::CLI.optparse_and_options
31
9
 
32
10
  optparse.parse!
33
11
 
34
- ConfigureS3Website::S3Client.configure_website(options[:config_source])
12
+ ConfigureS3Website::Runner.run(options)
@@ -2,6 +2,11 @@
2
2
 
3
3
  This project uses [Semantic Versioning](http://semver.org).
4
4
 
5
+ ## 1.3.0
6
+
7
+ * Create a CloudFront distro if the user wants to deliver his S3 website via the
8
+ CDN
9
+
5
10
  ## 1.2.0
6
11
 
7
12
  * Support configuring redirects on the S3 website
@@ -0,0 +1,134 @@
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
+ - Sat, 18 May 2013 20:07:54 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
+ - vB5gARfon866SjZUI2QIDzjxJjParAzDxo8vLjDC+SBujim60dH0XzrF9uPIlj7u
28
+ X-Amz-Request-Id:
29
+ - 16950B9C61E9FF87
30
+ Date:
31
+ - Sat, 18 May 2013 20:07:59 GMT
32
+ Content-Length:
33
+ - '0'
34
+ Server:
35
+ - AmazonS3
36
+ body:
37
+ encoding: US-ASCII
38
+ string: ''
39
+ http_version:
40
+ recorded_at: Sat, 18 May 2013 20:07:55 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
+ - Sat, 18 May 2013 20:07:55 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
+ - v7kj1pp5cB/kl2WbrQeDfrog3f7gVx17znu+S0bvUsf0CxaZtg+1Lk650LalVyQH
66
+ X-Amz-Request-Id:
67
+ - 25D798662824A94C
68
+ Date:
69
+ - Sat, 18 May 2013 20:08:00 GMT
70
+ Server:
71
+ - AmazonS3
72
+ body:
73
+ encoding: US-ASCII
74
+ string: ''
75
+ http_version:
76
+ recorded_at: Sat, 18 May 2013 20:07:56 GMT
77
+ - request:
78
+ method: post
79
+ uri: https://cloudfront.amazonaws.com/2012-07-01/distribution
80
+ body:
81
+ encoding: US-ASCII
82
+ string: ! "\n <DistributionConfig xmlns=\"http://cloudfront.amazonaws.com/doc/2012-07-01/\">\n
83
+ \ <Origins>\n <Quantity>1</Quantity>\n <Items>\n
84
+ \ <Origin>\n <Id>website-via-cf-S3-origin</Id>\n
85
+ \ <DomainName>website-via-cf.s3.amazonaws.com</DomainName>\n
86
+ \ <S3OriginConfig>\n <OriginAccessIdentity></OriginAccessIdentity>\n
87
+ \ </S3OriginConfig>\n </Origin>\n </Items>\n
88
+ \ </Origins>\n \n<CallerReference>configure-s3-website gem
89
+ 2013-05-18 23:07:56 +0300</CallerReference>\n<DefaultRootObject>index.html</DefaultRootObject>\n<Logging>\n
90
+ \ <Enabled>false</Enabled>\n <IncludeCookies>false</IncludeCookies>\n <Bucket></Bucket>\n
91
+ \ <Prefix></Prefix></Logging>\n<Enabled>true</Enabled>\n<Comment>Created by
92
+ the configure-s3-website gem</Comment>\n<Aliases>\n <Quantity>0</Quantity></Aliases>\n<DefaultCacheBehavior>\n
93
+ \ <TargetOriginId>website-via-cf-S3-origin</TargetOriginId>\n <TrustedSigners>\n
94
+ \ <Enabled>false</Enabled>\n <Quantity>0</Quantity></TrustedSigners>\n
95
+ \ <ForwardedValues>\n <QueryString>true</QueryString>\n <Cookies>\n
96
+ \ <Forward>all</Forward></Cookies></ForwardedValues>\n <ViewerProtocolPolicy>allow-all</ViewerProtocolPolicy>\n
97
+ \ <MinTTL>86400</MinTTL></DefaultCacheBehavior>\n<CacheBehaviors>\n <Quantity>0</Quantity></CacheBehaviors>\n<PriceClass>PriceClass_All</PriceClass>\n
98
+ \ </DistributionConfig>\n "
99
+ headers:
100
+ Date:
101
+ - Sat, 18 May 2013 20:07:56 UTC
102
+ Content-Type:
103
+ - ''
104
+ Content-Length:
105
+ - '1451'
106
+ Authorization:
107
+ - AWS foo:foo
108
+ response:
109
+ status:
110
+ code: 201
111
+ message: Created
112
+ headers:
113
+ X-Amzn-Requestid:
114
+ - a3ab794f-bff6-11e2-abb8-3518185a092c
115
+ Etag:
116
+ - E1N3VW3E5YJ58U
117
+ Location:
118
+ - https://cloudfront.amazonaws.com/2012-07-01/distribution/E45H2VN49KPDU
119
+ Content-Type:
120
+ - text/xml
121
+ Content-Length:
122
+ - '1536'
123
+ Date:
124
+ - Sat, 18 May 2013 20:08:00 GMT
125
+ body:
126
+ encoding: US-ASCII
127
+ string: ! '<?xml version="1.0"?>
128
+
129
+ <Distribution xmlns="http://cloudfront.amazonaws.com/doc/2012-07-01/"><Id>E45H2VN49KPDU</Id><Status>InProgress</Status><LastModifiedTime>2013-05-18T20:08:00.350Z</LastModifiedTime><InProgressInvalidationBatches>0</InProgressInvalidationBatches><DomainName>d3feoe9t5ufu01.cloudfront.net</DomainName><ActiveTrustedSigners><Enabled>false</Enabled><Quantity>0</Quantity></ActiveTrustedSigners><DistributionConfig><CallerReference>configure-s3-website
130
+ gem 2013-05-18 23:07:56 +0300</CallerReference><Aliases><Quantity>0</Quantity></Aliases><DefaultRootObject>index.html</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>86400</MinTTL></DefaultCacheBehavior><CacheBehaviors><Quantity>0</Quantity></CacheBehaviors><Comment>Created
131
+ 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>'
132
+ http_version:
133
+ recorded_at: Sat, 18 May 2013 20:07:57 GMT
134
+ recorded_with: VCR 2.3.0
@@ -2,21 +2,24 @@ Feature: configure an S3 bucket to function as a website
2
2
 
3
3
  @bucket-does-not-exist
4
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
5
+ When I run the configure-s3-website command with parameters
6
+ | option | value |
7
+ | --config-file | features/support/sample_config_files/s3_config_with_non-existing_bucket.yml |
7
8
  Then the output should be
8
9
  """
9
10
  Created bucket name-of-a-new-bucket in the US Standard Region
10
11
  Bucket name-of-a-new-bucket now functions as a website
11
12
  Bucket name-of-a-new-bucket is now readable to the whole world
12
13
  No redirects to configure for name-of-a-new-bucket bucket
14
+ Do you want to deliver your website via CloudFront, the CDN of Amazon? [y/N]
13
15
 
14
16
  """
15
17
 
16
18
  @bucket-does-not-exist
17
19
  Scenario: The configuration does not contain the 's3_endpoint' setting
18
- Given my config file is in "features/support/sample_config_files/s3_config_with_non-existing_bucket.yml"
19
- When I run the configure-s3-website command
20
+ When I run the configure-s3-website command with parameters
21
+ | option | value |
22
+ | --config-file | features/support/sample_config_files/s3_config_with_non-existing_bucket.yml |
20
23
  Then the output should include
21
24
  """
22
25
  Created bucket name-of-a-new-bucket in the US Standard Region
@@ -24,8 +27,9 @@ Feature: configure an S3 bucket to function as a website
24
27
 
25
28
  @bucket-does-not-exist-in-tokyo
26
29
  Scenario: Create bucket into the Tokyo region
27
- Given my config file is in "features/support/sample_config_files/endpoint_tokyo.yml"
28
- When I run the configure-s3-website command
30
+ When I run the configure-s3-website command with parameters
31
+ | option | value |
32
+ | --config-file | features/support/sample_config_files/endpoint_tokyo.yml |
29
33
  Then the output should include
30
34
  """
31
35
  Created bucket name-of-a-new-bucket in the Asia Pacific (Tokyo) Region
@@ -33,9 +37,10 @@ Feature: configure an S3 bucket to function as a website
33
37
 
34
38
  @bucket-exists
35
39
  Scenario: The bucket already exists
36
- Given my config file is in "features/support/sample_config_files/s3_config_with_existing_bucket.yml"
37
- When I run the configure-s3-website command
38
- Then the output should be
40
+ When I run the configure-s3-website command with parameters
41
+ | option | value |
42
+ | --config-file | features/support/sample_config_files/s3_config_with_existing_bucket.yml |
43
+ Then the output should include
39
44
  """
40
45
  Bucket name-of-an-existing-bucket now functions as a website
41
46
  Bucket name-of-an-existing-bucket is now readable to the whole world
@@ -45,13 +50,15 @@ Feature: configure an S3 bucket to function as a website
45
50
 
46
51
  @redirects
47
52
  Scenario: The user wants to configure redirects for the S3 website
48
- Given my config file is in "features/support/sample_config_files/redirects.yml"
49
- When I run the configure-s3-website command
53
+ And I run the configure-s3-website command with parameters
54
+ | option | value |
55
+ | --config-file | features/support/sample_config_files/redirects.yml |
50
56
  Then the output should be
51
57
  """
52
58
  Created bucket website-with-redirects in the US Standard Region
53
59
  Bucket website-with-redirects now functions as a website
54
60
  Bucket website-with-redirects is now readable to the whole world
55
61
  1 redirects configured for website-with-redirects bucket
62
+ Do you want to deliver your website via CloudFront, the CDN of Amazon? [y/N]
56
63
 
57
64
  """
@@ -0,0 +1,164 @@
1
+ Feature: Create CloudFront distribution
2
+
3
+ @create-cf-dist
4
+ Scenario: The user wants to deliver his website via CloudFront
5
+ Given I answer 'yes' to 'do you want to use CloudFront'
6
+ When I run the configure-s3-website command with parameters
7
+ | option | value |
8
+ | --config-file | features/support/sample_config_files/create_cf_dist.yml |
9
+ Then the output should be
10
+ """
11
+ Bucket website-via-cf now functions as a website
12
+ Bucket website-via-cf is now readable to the whole world
13
+ No redirects to configure for website-via-cf bucket
14
+ Do you want to deliver your website via CloudFront, the CDN of Amazon? [y/N]
15
+ The distribution E45H2VN49KPDU at d3feoe9t5ufu01.cloudfront.net now delivers the bucket website-via-cf
16
+ Please allow up to 15 minutes for the distribution to initialise
17
+ For more information on the distribution, see https://console.aws.amazon.com/cloudfront
18
+ Added setting 'cloudfront_distribution_id: E45H2VN49KPDU' into features/support/sample_config_files/create_cf_dist.yml
19
+
20
+ """
21
+
22
+ @create-cf-dist
23
+ Scenario: The user wants to deliver his website via CloudFront and see details on the new distribution
24
+ Given I answer 'yes' to 'do you want to use CloudFront'
25
+ When I run the configure-s3-website command with parameters
26
+ | option | value |
27
+ | --config-file | features/support/sample_config_files/create_cf_dist.yml |
28
+ | --verbose | |
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
+ Below is the response from the CloudFront API:
39
+ <?xml version='1.0'?>
40
+ <Distribution xmlns='http://cloudfront.amazonaws.com/doc/2012-07-01/'>
41
+ <Id>
42
+ E45H2VN49KPDU
43
+ </Id>
44
+ <Status>
45
+ InProgress
46
+ </Status>
47
+ <LastModifiedTime>
48
+ 2013-05-18T20:08:00.350Z
49
+ </LastModifiedTime>
50
+ <InProgressInvalidationBatches>
51
+ 0
52
+ </InProgressInvalidationBatches>
53
+ <DomainName>
54
+ d3feoe9t5ufu01.cloudfront.net
55
+ </DomainName>
56
+ <ActiveTrustedSigners>
57
+ <Enabled>
58
+ false
59
+ </Enabled>
60
+ <Quantity>
61
+ 0
62
+ </Quantity>
63
+ </ActiveTrustedSigners>
64
+ <DistributionConfig>
65
+ <CallerReference>
66
+ configure-s3-website gem 2013-05-18 23:07:56 +0300
67
+ </CallerReference>
68
+ <Aliases>
69
+ <Quantity>
70
+ 0
71
+ </Quantity>
72
+ </Aliases>
73
+ <DefaultRootObject>
74
+ index.html
75
+ </DefaultRootObject>
76
+ <Origins>
77
+ <Quantity>
78
+ 1
79
+ </Quantity>
80
+ <Items>
81
+ <Origin>
82
+ <Id>
83
+ website-via-cf-S3-origin
84
+ </Id>
85
+ <DomainName>
86
+ website-via-cf.s3.amazonaws.com
87
+ </DomainName>
88
+ <S3OriginConfig>
89
+ <OriginAccessIdentity/>
90
+ </S3OriginConfig>
91
+ </Origin>
92
+ </Items>
93
+ </Origins>
94
+ <DefaultCacheBehavior>
95
+ <TargetOriginId>
96
+ website-via-cf-S3-origin
97
+ </TargetOriginId>
98
+ <ForwardedValues>
99
+ <QueryString>
100
+ true
101
+ </QueryString>
102
+ <Cookies>
103
+ <Forward>
104
+ all
105
+ </Forward>
106
+ </Cookies>
107
+ </ForwardedValues>
108
+ <TrustedSigners>
109
+ <Enabled>
110
+ false
111
+ </Enabled>
112
+ <Quantity>
113
+ 0
114
+ </Quantity>
115
+ </TrustedSigners>
116
+ <ViewerProtocolPolicy>
117
+ allow-all
118
+ </ViewerProtocolPolicy>
119
+ <MinTTL>
120
+ 86400
121
+ </MinTTL>
122
+ </DefaultCacheBehavior>
123
+ <CacheBehaviors>
124
+ <Quantity>
125
+ 0
126
+ </Quantity>
127
+ </CacheBehaviors>
128
+ <Comment>
129
+ Created by the configure-s3-website gem
130
+ </Comment>
131
+ <Logging>
132
+ <Enabled>
133
+ false
134
+ </Enabled>
135
+ <IncludeCookies>
136
+ false
137
+ </IncludeCookies>
138
+ <Bucket/>
139
+ <Prefix/>
140
+ </Logging>
141
+ <PriceClass>
142
+ PriceClass_All
143
+ </PriceClass>
144
+ <Enabled>
145
+ true
146
+ </Enabled>
147
+ </DistributionConfig>
148
+ </Distribution>
149
+ Added setting 'cloudfront_distribution_id: E45H2VN49KPDU' into features/support/sample_config_files/create_cf_dist.yml
150
+
151
+ """
152
+
153
+ @bucket-exists
154
+ Scenario: The user already has CloudFront configured in his configuration file
155
+ When I run the configure-s3-website command with parameters
156
+ | option | value |
157
+ | --config-file | features/support/sample_config_files/config_with_cloudfront_distribution_id.yml |
158
+ Then the output should be
159
+ """
160
+ Bucket name-of-an-existing-bucket now functions as a website
161
+ Bucket name-of-an-existing-bucket is now readable to the whole world
162
+ No redirects to configure for name-of-an-existing-bucket bucket
163
+
164
+ """