aws 2.3.21 → 2.3.22

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.
@@ -69,9 +69,10 @@ The RightScale AWS gems comprise:
69
69
 
70
70
  ## THREADING:
71
71
 
72
- All RightScale AWS interfaces offer two threading options:
73
- 1. Use a single persistent HTTP connection per process.
74
- 2. Use a persistent HTTP connection per Ruby thread.
72
+ All AWS interfaces offer three threading options:
73
+ 1. Use a single persistent HTTP connection per process. :single
74
+ 2. Use a persistent HTTP connection per Ruby thread. :per_thread
75
+ 3. Open a new connection for each request. :per_request
75
76
 
76
77
  Either way, it doesn't matter how many (for example) Aws::S3 objects you create,
77
78
  they all use the same per-program or per-thread
@@ -91,7 +92,7 @@ Note that due to limitations in the I/O of the Ruby interpreter you
91
92
  may not get the degree of parallelism you may expect with the multi-threaded setting.
92
93
 
93
94
  By default, EC2/S3/SQS/SDB/ACF interface instances are created in single-threaded mode. Set
94
- params[:connection_mode] to :multi_thread in the initialization arguments to use
95
+ params[:connection_mode] to :per_thread in the initialization arguments to use
95
96
  multithreaded mode.
96
97
 
97
98
  ## GETTING STARTED:
@@ -76,10 +76,10 @@ module Aws
76
76
 
77
77
  include AwsBaseInterface
78
78
 
79
- API_VERSION = "2008-06-30"
79
+ API_VERSION = "2010-08-01"
80
80
  DEFAULT_HOST = 'cloudfront.amazonaws.com'
81
- DEFAULT_PORT = 80
82
- DEFAULT_PROTOCOL = 'http'
81
+ DEFAULT_PORT = 443
82
+ DEFAULT_PROTOCOL = 'https'
83
83
  DEFAULT_PATH = '/'
84
84
 
85
85
  @@bench = AwsBenchmarkingBlock.new
@@ -68,7 +68,7 @@ module Aws
68
68
  include AwsBaseInterface
69
69
 
70
70
  # Amazon EC2 API version being used
71
- API_VERSION = "2009-08-15"
71
+ API_VERSION = "2010-08-31"
72
72
  DEFAULT_HOST = "ec2.amazonaws.com"
73
73
  DEFAULT_PATH = '/'
74
74
  DEFAULT_PROTOCOL = 'https'
@@ -1252,6 +1252,59 @@ module Aws
1252
1252
  rescue Exception
1253
1253
  on_exception
1254
1254
  end
1255
+
1256
+ # Add/replace one tag to a resource
1257
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference_query_CreateTags.html
1258
+ #
1259
+ # ec2.create_tag('ami-1a2b3c4d', 'webserver') #=> true
1260
+ # ec2.create_tag('i-7f4d3a2b', 'stack', 'Production') #=> true
1261
+ #
1262
+ def create_tag(resource_id, key, value = nil)
1263
+ link = generate_request("CreateTags",
1264
+ "ResourceId.1" => resource_id.to_s,
1265
+ "Tag.1.Key" => key.to_s,
1266
+ "Tag.1.Value" => value.to_s)
1267
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
1268
+ rescue Exception
1269
+ on_exception
1270
+ end
1271
+
1272
+ # Describe tags
1273
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference_query_DescribeTags.html
1274
+ #
1275
+ # ec2.describe_tags
1276
+ # ec2.describe_tags(
1277
+ # 'Filter.1.Name' => 'resource-type', 'Filter.1.Value.1' => 'instance',
1278
+ # 'Filter.2.Name' => 'value', 'Filter.2.Value.1' => 'Test', 'Filter.2.Value.2' => 'Production'
1279
+ # )
1280
+ #
1281
+ def describe_tags(filters = {})
1282
+ link = generate_request("DescribeTags", filters)
1283
+ request_info(link, QEc2DescribeTagsParser.new(:logger => @logger))
1284
+ rescue Exception
1285
+ on_exception
1286
+ end
1287
+
1288
+ # Delete one or all tags from a resource
1289
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference_query_DeleteTags.html
1290
+ #
1291
+ # ec2.delete_tag('i-7f4d3a2b', 'stack') #=> true
1292
+ # ec2.delete_tag('i-7f4d3a2b', 'stack', 'Production') #=> true
1293
+ #
1294
+ # "If you omit Tag.n.Value, we delete the tag regardless of its value. If
1295
+ # you specify this parameter with an empty string as the value, we delete the
1296
+ # key only if its value is an empty string."
1297
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference_query_DeleteTags.html
1298
+ #
1299
+ def delete_tag(resource_id, key, value = nil)
1300
+ request_args = {"ResourceId.1" => resource_id.to_s, "Tag.1.Key" => key.to_s}
1301
+ request_args["Tag.1.Value"] = value.to_s if value
1302
+
1303
+ link = generate_request("DeleteTags", request_args)
1304
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
1305
+ rescue Exception
1306
+ on_exception
1307
+ end
1255
1308
 
1256
1309
  #-----------------------------------------------------------------
1257
1310
  # PARSERS: Boolean Response Parser
@@ -1792,6 +1845,28 @@ module Aws
1792
1845
  end
1793
1846
  end
1794
1847
 
1848
+ #-----------------------------------------------------------------
1849
+ # PARSERS: Tags
1850
+ #-----------------------------------------------------------------
1851
+
1852
+ class QEc2DescribeTagsParser < AwsParser #:nodoc:
1853
+ def tagstart(name, attributes)
1854
+ @tag = {} if name == 'item'
1855
+ end
1856
+ def tagend(name)
1857
+ case name
1858
+ when 'resourceId' then @tag[:aws_resource_id] = @text
1859
+ when 'resourceType' then @tag[:aws_resource_type] = @text
1860
+ when 'key' then @tag[:aws_key] = @text
1861
+ when 'value' then @tag[:aws_value] = @text
1862
+ when 'item' then @result << @tag
1863
+ end
1864
+ end
1865
+ def reset
1866
+ @result = []
1867
+ end
1868
+ end
1869
+
1795
1870
  end
1796
1871
 
1797
1872
  end
@@ -23,878 +23,886 @@
23
23
 
24
24
  module Aws
25
25
 
26
- # = Aws::S3 -- RightScale's Amazon S3 interface
27
- # The Aws::S3 class provides a complete interface to Amazon's Simple
28
- # Storage Service.
29
- # For explanations of the semantics
30
- # of each call, please refer to Amazon's documentation at
31
- # http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=48
32
- #
33
- # See examples below for the bucket and buckets methods.
34
- #
35
- # Error handling: all operations raise an Aws::AwsError in case
36
- # of problems. Note that transient errors are automatically retried.
37
- #
38
- # It is a good way to use domain naming style getting a name for the buckets.
39
- # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingBucket.html
40
- # about the naming convention for the buckets. This case they can be accessed using a virtual domains.
41
- #
42
- # Let assume you have 3 buckets: 'awesome-bucket', 'awesome_bucket' and 'AWEsomE-bucket'.
43
- # The first ones objects can be accessed as: http:// awesome-bucket.s3.amazonaws.com/key/object
44
- #
45
- # But the rest have to be accessed as:
46
- # http:// s3.amazonaws.com/awesome_bucket/key/object and http:// s3.amazonaws.com/AWEsomE-bucket/key/object
47
- #
48
- # See: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/VirtualHosting.html for better explanation.
49
- #
50
- class S3
51
- attr_reader :interface
52
-
53
- # Create a new handle to an S3 account. All handles share the same per process or per thread
54
- # HTTP connection to Amazon S3. Each handle is for a specific account.
55
- # The +params+ are passed through as-is to Aws::S3Interface.new
26
+ # = Aws::S3 -- RightScale's Amazon S3 interface
27
+ # The Aws::S3 class provides a complete interface to Amazon's Simple
28
+ # Storage Service.
29
+ # For explanations of the semantics
30
+ # of each call, please refer to Amazon's documentation at
31
+ # http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=48
56
32
  #
57
- # Params is a hash:
33
+ # See examples below for the bucket and buckets methods.
58
34
  #
59
- # {:server => 's3.amazonaws.com' # Amazon service host: 's3.amazonaws.com'(default)
60
- # :port => 443 # Amazon service port: 80 or 443(default)
61
- # :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
62
- # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
63
- # :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
64
- def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
65
- @interface = S3Interface.new(aws_access_key_id, aws_secret_access_key, params)
66
- end
67
-
68
- def close_connection
69
- @interface.close_connection
70
- end
71
-
72
- # Retrieve a list of buckets.
73
- # Returns an array of Aws::S3::Bucket instances.
74
- # # Create handle to S3 account
75
- # s3 = Aws::S3.new(aws_access_key_id, aws_secret_access_key)
76
- # my_buckets_names = s3.buckets.map{|b| b.name}
77
- # puts "Buckets on S3: #{my_bucket_names.join(', ')}"
78
- def buckets
79
- @interface.list_all_my_buckets.map! do |entry|
80
- owner = Owner.new(entry[:owner_id], entry[:owner_display_name])
81
- Bucket.new(self, entry[:name], entry[:creation_date], owner)
82
- end
83
- end
84
-
85
- # Retrieve an individual bucket.
86
- # If the bucket does not exist and +create+ is set, a new bucket
87
- # is created on S3. Launching this method with +create+=+true+ may
88
- # affect on the bucket's ACL if the bucket already exists.
89
- # Returns a Aws::S3::Bucket instance or +nil+ if the bucket does not exist
90
- # and +create+ is not set.
35
+ # Error handling: all operations raise an Aws::AwsError in case
36
+ # of problems. Note that transient errors are automatically retried.
91
37
  #
92
- # s3 = Aws::S3.new(aws_access_key_id, aws_secret_access_key)
93
- # bucket1 = s3.bucket('my_awesome_bucket_1')
94
- # bucket1.keys #=> exception here if the bucket does not exists
95
- # ...
96
- # bucket2 = s3.bucket('my_awesome_bucket_2', true)
97
- # bucket2.keys #=> list of keys
98
- # # create a bucket at the European location with public read access
99
- # bucket3 = s3.bucket('my-awesome-bucket-3', true, 'public-read', :location => :eu)
38
+ # It is a good way to use domain naming style getting a name for the buckets.
39
+ # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingBucket.html
40
+ # about the naming convention for the buckets. This case they can be accessed using a virtual domains.
100
41
  #
101
- # see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html
102
- # (section: Canned Access Policies)
42
+ # Let assume you have 3 buckets: 'awesome-bucket', 'awesome_bucket' and 'AWEsomE-bucket'.
43
+ # The first ones objects can be accessed as: http:// awesome-bucket.s3.amazonaws.com/key/object
103
44
  #
104
- def bucket(name, create=false, perms=nil, headers={})
105
- headers['x-amz-acl'] = perms if perms
106
- @interface.create_bucket(name, headers) if create
107
- return Bucket.new(self, name)
108
- # The old way below was too slow and unnecessary because it retreived all the buckets every time.
109
- # owner = Owner.new(entry[:owner_id], entry[:owner_display_name])
110
- # buckets.each { |bucket| return bucket if bucket.name == name }
111
- # nil
112
- end
113
-
114
-
115
- class Bucket
116
- attr_reader :s3, :name, :owner, :creation_date
117
-
118
- # Create a Bucket instance.
119
- # If the bucket does not exist and +create+ is set, a new bucket
120
- # is created on S3. Launching this method with +create+=+true+ may
121
- # affect on the bucket's ACL if the bucket already exists.
122
- # Returns Bucket instance or +nil+ if the bucket does not exist
123
- # and +create+ is not set.
124
- #
125
- # s3 = Aws::S3.new(aws_access_key_id, aws_secret_access_key)
126
- # ...
127
- # bucket1 = Aws::S3::Bucket.create(s3, 'my_awesome_bucket_1')
128
- # bucket1.keys #=> exception here if the bucket does not exists
129
- # ...
130
- # bucket2 = Aws::S3::Bucket.create(s3, 'my_awesome_bucket_2', true)
131
- # bucket2.keys #=> list of keys
132
- # # create a bucket at the European location with public read access
133
- # bucket3 = Aws::S3::Bucket.create(s3,'my-awesome-bucket-3', true, 'public-read', :location => :eu)
134
- #
135
- # see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html
136
- # (section: Canned Access Policies)
137
- #
138
- def self.create(s3, name, create=false, perms=nil, headers={})
139
- s3.bucket(name, create, perms, headers)
140
- end
141
-
142
-
143
- # Create a bucket instance. In normal use this method should
144
- # not be called directly.
145
- # Use Aws::S3::Bucket.create or Aws::S3.bucket instead.
146
- def initialize(s3, name, creation_date=nil, owner=nil)
147
- @s3 = s3
148
- @name = name
149
- @owner = owner
150
- @creation_date = creation_date
151
- if @creation_date && !@creation_date.is_a?(Time)
152
- @creation_date = Time.parse(@creation_date)
45
+ # But the rest have to be accessed as:
46
+ # http:// s3.amazonaws.com/awesome_bucket/key/object and http:// s3.amazonaws.com/AWEsomE-bucket/key/object
47
+ #
48
+ # See: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/VirtualHosting.html for better explanation.
49
+ #
50
+ class S3
51
+ attr_reader :interface
52
+
53
+ # Create a new handle to an S3 account. All handles share the same per process or per thread
54
+ # HTTP connection to Amazon S3. Each handle is for a specific account.
55
+ # The +params+ are passed through as-is to Aws::S3Interface.new
56
+ #
57
+ # Params is a hash:
58
+ #
59
+ # {:server => 's3.amazonaws.com' # Amazon service host: 's3.amazonaws.com'(default)
60
+ # :port => 443 # Amazon service port: 80 or 443(default)
61
+ # :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
62
+ # :connection_mode => :default # options are
63
+ # :default (will use best known safe (as in won't need explicit close) option, may change in the future)
64
+ # :per_request (opens and closes a connection on every request)
65
+ # :single (one thread across entire app)
66
+ # :per_thread (one connection per thread)
67
+ # :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
68
+ def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
69
+ @interface = S3Interface.new(aws_access_key_id, aws_secret_access_key, params)
153
70
  end
154
- end
155
-
156
- # Return bucket name as a String.
157
- #
158
- # bucket = Aws::S3.bucket('my_awesome_bucket')
159
- # puts bucket #=> 'my_awesome_bucket'
160
- #
161
- def to_s
162
- @name.to_s
163
- end
164
- alias_method :full_name, :to_s
165
71
 
166
- # Return a public link to bucket.
167
- #
168
- # bucket.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket'
169
- #
170
- def public_link
171
- params = @s3.interface.params
172
- "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name}"
173
- end
174
-
175
- # Returns the bucket location
176
- def location
177
- @location ||= @s3.interface.bucket_location(@name)
178
- end
179
-
180
- # Retrieves the logging configuration for a bucket.
181
- # Returns a hash of {:enabled, :targetbucket, :targetprefix}
182
- #
183
- # bucket.logging_info()
184
- # => {:enabled=>true, :targetbucket=>"mylogbucket", :targetprefix=>"loggylogs/"}
185
- def logging_info
186
- @s3.interface.get_logging_parse(:bucket => @name)
187
- end
188
-
189
- # Enables S3 server access logging on a bucket. The target bucket must have been properly configured to receive server
190
- # access logs.
191
- # Params:
192
- # :targetbucket - either the target bucket object or the name of the target bucket
193
- # :targetprefix - the prefix under which all logs should be stored
194
- #
195
- # bucket.enable_logging(:targetbucket=>"mylogbucket", :targetprefix=>"loggylogs/")
196
- # => true
197
- def enable_logging(params)
198
- AwsUtils.mandatory_arguments([:targetbucket, :targetprefix], params)
199
- AwsUtils.allow_only([:targetbucket, :targetprefix], params)
200
- xmldoc = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><BucketLoggingStatus xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\"><LoggingEnabled><TargetBucket>#{params[:targetbucket]}</TargetBucket><TargetPrefix>#{params[:targetprefix]}</TargetPrefix></LoggingEnabled></BucketLoggingStatus>"
201
- @s3.interface.put_logging(:bucket => @name, :xmldoc => xmldoc)
202
- end
203
-
204
- # Disables S3 server access logging on a bucket. Takes no arguments.
205
- def disable_logging
206
- xmldoc = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><BucketLoggingStatus xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\"></BucketLoggingStatus>"
207
- @s3.interface.put_logging(:bucket => @name, :xmldoc => xmldoc)
208
- end
209
-
210
- # Retrieve a group of keys from Amazon.
211
- # +options+ is a hash: { 'prefix'=>'', 'marker'=>'', 'max-keys'=>5, 'delimiter'=>'' }).
212
- # Retrieves meta-headers information if +head+ it +true+.
213
- # Returns an array of Key instances.
214
- #
215
- # bucket.keys #=> # returns all keys from bucket
216
- # bucket.keys('prefix' => 'logs') #=> # returns all keys that starts with 'logs'
217
- #
218
- def keys(options={}, head=false)
219
- keys_and_service(options, head)[0]
220
- end
221
-
222
- # Same as +keys+ method but return an array of [keys, service_data].
223
- # where +service_data+ is a hash with additional output information.
224
- #
225
- # keys, service = bucket.keys_and_service({'max-keys'=> 2, 'prefix' => 'logs'})
226
- # p keys #=> # 2 keys array
227
- # p service #=> {"max-keys"=>"2", "prefix"=>"logs", "name"=>"my_awesome_bucket", "marker"=>"", "is_truncated"=>true}
228
- #
229
- def keys_and_service(options={}, head=false)
230
- opt = {}; options.each{ |key, value| opt[key.to_s] = value }
231
- service_data = {}
232
- thislist = {}
233
- list = []
234
- @s3.interface.incrementally_list_bucket(@name, opt) do |thislist|
235
- thislist[:contents].each do |entry|
236
- owner = Owner.new(entry[:owner_id], entry[:owner_display_name])
237
- key = Key.new(self, entry[:key], nil, {}, {}, entry[:last_modified], entry[:e_tag], entry[:size], entry[:storage_class], owner)
238
- key.head if head
239
- list << key
240
- end
72
+ def close_connection
73
+ @interface.close_connection
241
74
  end
242
- thislist.each_key do |key|
243
- service_data[key] = thislist[key] unless (key == :contents || key == :common_prefixes)
244
- end
245
- [list, service_data]
246
- end
247
75
 
248
- # Retrieve key information from Amazon.
249
- # The +key_name+ is a +String+ or Key instance.
250
- # Retrieves meta-header information if +head+ is +true+.
251
- # Returns new Key instance.
252
- #
253
- # key = bucket.key('logs/today/1.log', true) #=> #<Aws::S3::Key:0xb7b1e240 ... >
254
- # # is the same as:
255
- # key = Aws::S3::Key.create(bucket, 'logs/today/1.log')
256
- # key.head
257
- #
258
- def key(key_name, head=false)
259
- raise 'Key name can not be empty.' if key_name.blank?
260
- key_instance = nil
261
- # if this key exists - find it ....
262
- keys({'prefix'=>key_name}, head).each do |key|
263
- if key.name == key_name.to_s
264
- key_instance = key
265
- break
266
- end
76
+ # Retrieve a list of buckets.
77
+ # Returns an array of Aws::S3::Bucket instances.
78
+ # # Create handle to S3 account
79
+ # s3 = Aws::S3.new(aws_access_key_id, aws_secret_access_key)
80
+ # my_buckets_names = s3.buckets.map{|b| b.name}
81
+ # puts "Buckets on S3: #{my_bucket_names.join(', ')}"
82
+ def buckets
83
+ @interface.list_all_my_buckets.map! do |entry|
84
+ owner = Owner.new(entry[:owner_id], entry[:owner_display_name])
85
+ Bucket.new(self, entry[:name], entry[:creation_date], owner)
86
+ end
267
87
  end
268
- # .... else this key is unknown
269
- unless key_instance
270
- key_instance = Key.create(self, key_name.to_s)
271
- end
272
- key_instance
273
- end
274
-
275
- # Store object data.
276
- # The +key+ is a +String+ or Key instance.
277
- # Returns +true+.
278
- #
279
- # bucket.put('logs/today/1.log', 'Olala!') #=> true
280
- #
281
- def put(key, data=nil, meta_headers={}, perms=nil, headers={})
282
- key = Key.create(self, key.to_s, data, meta_headers) unless key.is_a?(Key)
283
- key.put(data, perms, headers)
284
- end
285
-
286
- # Retrieve object data from Amazon.
287
- # The +key+ is a +String+ or Key.
288
- # Returns Key instance.
289
- #
290
- # key = bucket.get('logs/today/1.log') #=>
291
- # puts key.data #=> 'sasfasfasdf'
292
- #
293
- def get(key, headers={})
294
- key = Key.create(self, key.to_s) unless key.is_a?(Key)
295
- key.get(headers)
296
- end
297
-
298
- # Rename object. Returns Aws::S3::Key instance.
299
- #
300
- # new_key = bucket.rename_key('logs/today/1.log','logs/today/2.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
301
- # puts key.name #=> 'logs/today/2.log'
302
- # key.exists? #=> true
303
- #
304
- def rename_key(old_key_or_name, new_name)
305
- old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)
306
- old_key_or_name.rename(new_name)
307
- old_key_or_name
308
- end
309
-
310
- # Create an object copy. Returns a destination Aws::S3::Key instance.
311
- #
312
- # new_key = bucket.copy_key('logs/today/1.log','logs/today/2.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
313
- # puts key.name #=> 'logs/today/2.log'
314
- # key.exists? #=> true
315
- #
316
- def copy_key(old_key_or_name, new_key_or_name)
317
- old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)
318
- old_key_or_name.copy(new_key_or_name)
319
- end
320
-
321
- # Move an object to other location. Returns a destination Aws::S3::Key instance.
322
- #
323
- # new_key = bucket.copy_key('logs/today/1.log','logs/today/2.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
324
- # puts key.name #=> 'logs/today/2.log'
325
- # key.exists? #=> true
326
- #
327
- def move_key(old_key_or_name, new_key_or_name)
328
- old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)
329
- old_key_or_name.move(new_key_or_name)
330
- end
331
-
332
- # Remove all keys from a bucket.
333
- # Returns +true+.
334
- #
335
- # bucket.clear #=> true
336
- #
337
- def clear
338
- @s3.interface.clear_bucket(@name)
339
- end
340
88
 
341
- # Delete all keys where the 'folder_key' can be interpreted
342
- # as a 'folder' name.
343
- # Returns an array of string keys that have been deleted.
89
+ # Retrieve an individual bucket.
90
+ # If the bucket does not exist and +create+ is set, a new bucket
91
+ # is created on S3. Launching this method with +create+=+true+ may
92
+ # affect on the bucket's ACL if the bucket already exists.
93
+ # Returns a Aws::S3::Bucket instance or +nil+ if the bucket does not exist
94
+ # and +create+ is not set.
344
95
  #
345
- # bucket.keys.map{|key| key.name}.join(', ') #=> 'test, test/2/34, test/3, test1, test1/logs'
346
- # bucket.delete_folder('test') #=> ['test','test/2/34','test/3']
347
- #
348
- def delete_folder(folder, separator='/')
349
- @s3.interface.delete_folder(@name, folder, separator)
350
- end
351
-
352
- # Delete a bucket. Bucket must be empty.
353
- # If +force+ is set, clears and deletes the bucket.
354
- # Returns +true+.
355
- #
356
- # bucket.delete(true) #=> true
357
- #
358
- def delete(force=false)
359
- force ? @s3.interface.force_delete_bucket(@name) : @s3.interface.delete_bucket(@name)
360
- end
361
-
362
- # Return a list of grantees.
363
- #
364
- def grantees
365
- Grantee::grantees(self)
366
- end
367
-
368
- end
369
-
370
-
371
- class Key
372
- attr_reader :bucket, :name, :last_modified, :e_tag, :size, :storage_class, :owner
373
- attr_accessor :headers, :meta_headers
374
- attr_writer :data
375
-
376
- # Separate Amazon meta headers from other headers
377
- def self.split_meta(headers) #:nodoc:
378
- hash = headers.dup
379
- meta = {}
380
- hash.each do |key, value|
381
- if key[/^#{S3Interface::AMAZON_METADATA_PREFIX}/]
382
- meta[key.gsub(S3Interface::AMAZON_METADATA_PREFIX,'')] = value
383
- hash.delete(key)
384
- end
385
- end
386
- [hash, meta]
387
- end
388
-
389
- def self.add_meta_prefix(meta_headers, prefix=S3Interface::AMAZON_METADATA_PREFIX)
390
- meta = {}
391
- meta_headers.each do |meta_header, value|
392
- if meta_header[/#{prefix}/]
393
- meta[meta_header] = value
394
- else
395
- meta["#{S3Interface::AMAZON_METADATA_PREFIX}#{meta_header}"] = value
396
- end
96
+ # s3 = Aws::S3.new(aws_access_key_id, aws_secret_access_key)
97
+ # bucket1 = s3.bucket('my_awesome_bucket_1')
98
+ # bucket1.keys #=> exception here if the bucket does not exists
99
+ # ...
100
+ # bucket2 = s3.bucket('my_awesome_bucket_2', true)
101
+ # bucket2.keys #=> list of keys
102
+ # # create a bucket at the European location with public read access
103
+ # bucket3 = s3.bucket('my-awesome-bucket-3', true, 'public-read', :location => :eu)
104
+ #
105
+ # see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html
106
+ # (section: Canned Access Policies)
107
+ #
108
+ def bucket(name, create=false, perms=nil, headers={})
109
+ headers['x-amz-acl'] = perms if perms
110
+ @interface.create_bucket(name, headers) if create
111
+ return Bucket.new(self, name)
112
+ # The old way below was too slow and unnecessary because it retreived all the buckets every time.
113
+ # owner = Owner.new(entry[:owner_id], entry[:owner_display_name])
114
+ # buckets.each { |bucket| return bucket if bucket.name == name }
115
+ # nil
397
116
  end
398
- meta
399
- end
400
117
 
401
118
 
402
- # Create a new Key instance, but do not create the actual key.
403
- # The +name+ is a +String+.
404
- # Returns a new Key instance.
405
- #
406
- # key = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
407
- # key.exists? #=> true | false
408
- # key.put('Woohoo!') #=> true
409
- # key.exists? #=> true
410
- #
411
- def self.create(bucket, name, data=nil, meta_headers={})
412
- new(bucket, name, data, {}, meta_headers)
413
- end
119
+ class Bucket
120
+ attr_reader :s3, :name, :owner, :creation_date
121
+
122
+ # Create a Bucket instance.
123
+ # If the bucket does not exist and +create+ is set, a new bucket
124
+ # is created on S3. Launching this method with +create+=+true+ may
125
+ # affect on the bucket's ACL if the bucket already exists.
126
+ # Returns Bucket instance or +nil+ if the bucket does not exist
127
+ # and +create+ is not set.
128
+ #
129
+ # s3 = Aws::S3.new(aws_access_key_id, aws_secret_access_key)
130
+ # ...
131
+ # bucket1 = Aws::S3::Bucket.create(s3, 'my_awesome_bucket_1')
132
+ # bucket1.keys #=> exception here if the bucket does not exists
133
+ # ...
134
+ # bucket2 = Aws::S3::Bucket.create(s3, 'my_awesome_bucket_2', true)
135
+ # bucket2.keys #=> list of keys
136
+ # # create a bucket at the European location with public read access
137
+ # bucket3 = Aws::S3::Bucket.create(s3,'my-awesome-bucket-3', true, 'public-read', :location => :eu)
138
+ #
139
+ # see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html
140
+ # (section: Canned Access Policies)
141
+ #
142
+ def self.create(s3, name, create=false, perms=nil, headers={})
143
+ s3.bucket(name, create, perms, headers)
144
+ end
145
+
146
+
147
+ # Create a bucket instance. In normal use this method should
148
+ # not be called directly.
149
+ # Use Aws::S3::Bucket.create or Aws::S3.bucket instead.
150
+ def initialize(s3, name, creation_date=nil, owner=nil)
151
+ @s3 = s3
152
+ @name = name
153
+ @owner = owner
154
+ @creation_date = creation_date
155
+ if @creation_date && !@creation_date.is_a?(Time)
156
+ @creation_date = Time.parse(@creation_date)
157
+ end
158
+ end
159
+
160
+ # Return bucket name as a String.
161
+ #
162
+ # bucket = Aws::S3.bucket('my_awesome_bucket')
163
+ # puts bucket #=> 'my_awesome_bucket'
164
+ #
165
+ def to_s
166
+ @name.to_s
167
+ end
168
+
169
+ alias_method :full_name, :to_s
170
+
171
+ # Return a public link to bucket.
172
+ #
173
+ # bucket.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket'
174
+ #
175
+ def public_link
176
+ params = @s3.interface.params
177
+ "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name}"
178
+ end
179
+
180
+ # Returns the bucket location
181
+ def location
182
+ @location ||= @s3.interface.bucket_location(@name)
183
+ end
184
+
185
+ # Retrieves the logging configuration for a bucket.
186
+ # Returns a hash of {:enabled, :targetbucket, :targetprefix}
187
+ #
188
+ # bucket.logging_info()
189
+ # => {:enabled=>true, :targetbucket=>"mylogbucket", :targetprefix=>"loggylogs/"}
190
+ def logging_info
191
+ @s3.interface.get_logging_parse(:bucket => @name)
192
+ end
193
+
194
+ # Enables S3 server access logging on a bucket. The target bucket must have been properly configured to receive server
195
+ # access logs.
196
+ # Params:
197
+ # :targetbucket - either the target bucket object or the name of the target bucket
198
+ # :targetprefix - the prefix under which all logs should be stored
199
+ #
200
+ # bucket.enable_logging(:targetbucket=>"mylogbucket", :targetprefix=>"loggylogs/")
201
+ # => true
202
+ def enable_logging(params)
203
+ AwsUtils.mandatory_arguments([:targetbucket, :targetprefix], params)
204
+ AwsUtils.allow_only([:targetbucket, :targetprefix], params)
205
+ xmldoc = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><BucketLoggingStatus xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\"><LoggingEnabled><TargetBucket>#{params[:targetbucket]}</TargetBucket><TargetPrefix>#{params[:targetprefix]}</TargetPrefix></LoggingEnabled></BucketLoggingStatus>"
206
+ @s3.interface.put_logging(:bucket => @name, :xmldoc => xmldoc)
207
+ end
208
+
209
+ # Disables S3 server access logging on a bucket. Takes no arguments.
210
+ def disable_logging
211
+ xmldoc = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><BucketLoggingStatus xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\"></BucketLoggingStatus>"
212
+ @s3.interface.put_logging(:bucket => @name, :xmldoc => xmldoc)
213
+ end
214
+
215
+ # Retrieve a group of keys from Amazon.
216
+ # +options+ is a hash: { 'prefix'=>'', 'marker'=>'', 'max-keys'=>5, 'delimiter'=>'' }).
217
+ # Retrieves meta-headers information if +head+ it +true+.
218
+ # Returns an array of Key instances.
219
+ #
220
+ # bucket.keys #=> # returns all keys from bucket
221
+ # bucket.keys('prefix' => 'logs') #=> # returns all keys that starts with 'logs'
222
+ #
223
+ def keys(options={}, head=false)
224
+ keys_and_service(options, head)[0]
225
+ end
226
+
227
+ # Same as +keys+ method but return an array of [keys, service_data].
228
+ # where +service_data+ is a hash with additional output information.
229
+ #
230
+ # keys, service = bucket.keys_and_service({'max-keys'=> 2, 'prefix' => 'logs'})
231
+ # p keys #=> # 2 keys array
232
+ # p service #=> {"max-keys"=>"2", "prefix"=>"logs", "name"=>"my_awesome_bucket", "marker"=>"", "is_truncated"=>true}
233
+ #
234
+ def keys_and_service(options={}, head=false)
235
+ opt = {}; options.each { |key, value| opt[key.to_s] = value }
236
+ service_data = {}
237
+ thislist = {}
238
+ list = []
239
+ @s3.interface.incrementally_list_bucket(@name, opt) do |thislist|
240
+ thislist[:contents].each do |entry|
241
+ owner = Owner.new(entry[:owner_id], entry[:owner_display_name])
242
+ key = Key.new(self, entry[:key], nil, {}, {}, entry[:last_modified], entry[:e_tag], entry[:size], entry[:storage_class], owner)
243
+ key.head if head
244
+ list << key
245
+ end
246
+ end
247
+ thislist.each_key do |key|
248
+ service_data[key] = thislist[key] unless (key == :contents || key == :common_prefixes)
249
+ end
250
+ [list, service_data]
251
+ end
252
+
253
+ # Retrieve key information from Amazon.
254
+ # The +key_name+ is a +String+ or Key instance.
255
+ # Retrieves meta-header information if +head+ is +true+.
256
+ # Returns new Key instance.
257
+ #
258
+ # key = bucket.key('logs/today/1.log', true) #=> #<Aws::S3::Key:0xb7b1e240 ... >
259
+ # # is the same as:
260
+ # key = Aws::S3::Key.create(bucket, 'logs/today/1.log')
261
+ # key.head
262
+ #
263
+ def key(key_name, head=false)
264
+ raise 'Key name can not be empty.' if key_name.blank?
265
+ key_instance = nil
266
+ # if this key exists - find it ....
267
+ keys({'prefix'=>key_name}, head).each do |key|
268
+ if key.name == key_name.to_s
269
+ key_instance = key
270
+ break
271
+ end
272
+ end
273
+ # .... else this key is unknown
274
+ unless key_instance
275
+ key_instance = Key.create(self, key_name.to_s)
276
+ end
277
+ key_instance
278
+ end
279
+
280
+ # Store object data.
281
+ # The +key+ is a +String+ or Key instance.
282
+ # Returns +true+.
283
+ #
284
+ # bucket.put('logs/today/1.log', 'Olala!') #=> true
285
+ #
286
+ def put(key, data=nil, meta_headers={}, perms=nil, headers={})
287
+ key = Key.create(self, key.to_s, data, meta_headers) unless key.is_a?(Key)
288
+ key.put(data, perms, headers)
289
+ end
290
+
291
+ # Retrieve object data from Amazon.
292
+ # The +key+ is a +String+ or Key.
293
+ # Returns Key instance.
294
+ #
295
+ # key = bucket.get('logs/today/1.log') #=>
296
+ # puts key.data #=> 'sasfasfasdf'
297
+ #
298
+ def get(key, headers={})
299
+ key = Key.create(self, key.to_s) unless key.is_a?(Key)
300
+ key.get(headers)
301
+ end
302
+
303
+ # Rename object. Returns Aws::S3::Key instance.
304
+ #
305
+ # new_key = bucket.rename_key('logs/today/1.log','logs/today/2.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
306
+ # puts key.name #=> 'logs/today/2.log'
307
+ # key.exists? #=> true
308
+ #
309
+ def rename_key(old_key_or_name, new_name)
310
+ old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)
311
+ old_key_or_name.rename(new_name)
312
+ old_key_or_name
313
+ end
314
+
315
+ # Create an object copy. Returns a destination Aws::S3::Key instance.
316
+ #
317
+ # new_key = bucket.copy_key('logs/today/1.log','logs/today/2.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
318
+ # puts key.name #=> 'logs/today/2.log'
319
+ # key.exists? #=> true
320
+ #
321
+ def copy_key(old_key_or_name, new_key_or_name)
322
+ old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)
323
+ old_key_or_name.copy(new_key_or_name)
324
+ end
325
+
326
+ # Move an object to other location. Returns a destination Aws::S3::Key instance.
327
+ #
328
+ # new_key = bucket.copy_key('logs/today/1.log','logs/today/2.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
329
+ # puts key.name #=> 'logs/today/2.log'
330
+ # key.exists? #=> true
331
+ #
332
+ def move_key(old_key_or_name, new_key_or_name)
333
+ old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)
334
+ old_key_or_name.move(new_key_or_name)
335
+ end
336
+
337
+ # Remove all keys from a bucket.
338
+ # Returns +true+.
339
+ #
340
+ # bucket.clear #=> true
341
+ #
342
+ def clear
343
+ @s3.interface.clear_bucket(@name)
344
+ end
345
+
346
+ # Delete all keys where the 'folder_key' can be interpreted
347
+ # as a 'folder' name.
348
+ # Returns an array of string keys that have been deleted.
349
+ #
350
+ # bucket.keys.map{|key| key.name}.join(', ') #=> 'test, test/2/34, test/3, test1, test1/logs'
351
+ # bucket.delete_folder('test') #=> ['test','test/2/34','test/3']
352
+ #
353
+ def delete_folder(folder, separator='/')
354
+ @s3.interface.delete_folder(@name, folder, separator)
355
+ end
356
+
357
+ # Delete a bucket. Bucket must be empty.
358
+ # If +force+ is set, clears and deletes the bucket.
359
+ # Returns +true+.
360
+ #
361
+ # bucket.delete(true) #=> true
362
+ #
363
+ def delete(force=false)
364
+ force ? @s3.interface.force_delete_bucket(@name) : @s3.interface.delete_bucket(@name)
365
+ end
366
+
367
+ # Return a list of grantees.
368
+ #
369
+ def grantees
370
+ Grantee::grantees(self)
371
+ end
414
372
 
415
- # Create a new Key instance, but do not create the actual key.
416
- # In normal use this method should not be called directly.
417
- # Use Aws::S3::Key.create or bucket.key() instead.
418
- #
419
- def initialize(bucket, name, data=nil, headers={}, meta_headers={},
420
- last_modified=nil, e_tag=nil, size=nil, storage_class=nil, owner=nil)
421
- raise 'Bucket must be a Bucket instance.' unless bucket.is_a?(Bucket)
422
- @bucket = bucket
423
- @name = name
424
- @data = data
425
- @e_tag = e_tag
426
- @size = size.to_i
427
- @storage_class = storage_class
428
- @owner = owner
429
- @last_modified = last_modified
430
- if @last_modified && !@last_modified.is_a?(Time)
431
- @last_modified = Time.parse(@last_modified)
432
373
  end
433
- @headers, @meta_headers = self.class.split_meta(headers)
434
- @meta_headers.merge!(meta_headers)
435
- end
436
-
437
- # Return key name as a String.
438
- #
439
- # key = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
440
- # puts key #=> 'logs/today/1.log'
441
- #
442
- def to_s
443
- @name.to_s
444
- end
445
-
446
- # Return the full S3 path to this key (bucket/key).
447
- #
448
- # key.full_name #=> 'my_awesome_bucket/cool_key'
449
- #
450
- def full_name(separator='/')
451
- "#{@bucket.to_s}#{separator}#{@name}"
452
- end
453
374
 
454
- # Return a public link to a key.
455
- #
456
- # key.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket/cool_key'
457
- #
458
- def public_link
459
- params = @bucket.s3.interface.params
460
- "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name('/')}"
461
- end
462
375
 
463
- # Return Key data. Retrieve this data from Amazon if it is the first time call.
464
- # TODO TRB 6/19/07 What does the above mean? Clarify.
465
- #
466
- def data
467
- get if !@data and exists?
468
- @data
469
- end
376
+ class Key
377
+ attr_reader :bucket, :name, :last_modified, :e_tag, :size, :storage_class, :owner
378
+ attr_accessor :headers, :meta_headers
379
+ attr_writer :data
380
+
381
+ # Separate Amazon meta headers from other headers
382
+ def self.split_meta(headers) #:nodoc:
383
+ hash = headers.dup
384
+ meta = {}
385
+ hash.each do |key, value|
386
+ if key[/^#{S3Interface::AMAZON_METADATA_PREFIX}/]
387
+ meta[key.gsub(S3Interface::AMAZON_METADATA_PREFIX, '')] = value
388
+ hash.delete(key)
389
+ end
390
+ end
391
+ [hash, meta]
392
+ end
393
+
394
+ def self.add_meta_prefix(meta_headers, prefix=S3Interface::AMAZON_METADATA_PREFIX)
395
+ meta = {}
396
+ meta_headers.each do |meta_header, value|
397
+ if meta_header[/#{prefix}/]
398
+ meta[meta_header] = value
399
+ else
400
+ meta["#{S3Interface::AMAZON_METADATA_PREFIX}#{meta_header}"] = value
401
+ end
402
+ end
403
+ meta
404
+ end
405
+
406
+
407
+ # Create a new Key instance, but do not create the actual key.
408
+ # The +name+ is a +String+.
409
+ # Returns a new Key instance.
410
+ #
411
+ # key = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
412
+ # key.exists? #=> true | false
413
+ # key.put('Woohoo!') #=> true
414
+ # key.exists? #=> true
415
+ #
416
+ def self.create(bucket, name, data=nil, meta_headers={})
417
+ new(bucket, name, data, {}, meta_headers)
418
+ end
419
+
420
+ # Create a new Key instance, but do not create the actual key.
421
+ # In normal use this method should not be called directly.
422
+ # Use Aws::S3::Key.create or bucket.key() instead.
423
+ #
424
+ def initialize(bucket, name, data=nil, headers={}, meta_headers={},
425
+ last_modified=nil, e_tag=nil, size=nil, storage_class=nil, owner=nil)
426
+ raise 'Bucket must be a Bucket instance.' unless bucket.is_a?(Bucket)
427
+ @bucket = bucket
428
+ @name = name
429
+ @data = data
430
+ @e_tag = e_tag
431
+ @size = size.to_i
432
+ @storage_class = storage_class
433
+ @owner = owner
434
+ @last_modified = last_modified
435
+ if @last_modified && !@last_modified.is_a?(Time)
436
+ @last_modified = Time.parse(@last_modified)
437
+ end
438
+ @headers, @meta_headers = self.class.split_meta(headers)
439
+ @meta_headers.merge!(meta_headers)
440
+ end
441
+
442
+ # Return key name as a String.
443
+ #
444
+ # key = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
445
+ # puts key #=> 'logs/today/1.log'
446
+ #
447
+ def to_s
448
+ @name.to_s
449
+ end
450
+
451
+ # Return the full S3 path to this key (bucket/key).
452
+ #
453
+ # key.full_name #=> 'my_awesome_bucket/cool_key'
454
+ #
455
+ def full_name(separator='/')
456
+ "#{@bucket.to_s}#{separator}#{@name}"
457
+ end
458
+
459
+ # Return a public link to a key.
460
+ #
461
+ # key.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket/cool_key'
462
+ #
463
+ def public_link
464
+ params = @bucket.s3.interface.params
465
+ "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name('/')}"
466
+ end
467
+
468
+ # Return Key data. Retrieve this data from Amazon if it is the first time call.
469
+ # TODO TRB 6/19/07 What does the above mean? Clarify.
470
+ #
471
+ def data
472
+ get if !@data and exists?
473
+ @data
474
+ end
475
+
476
+ # Retrieve object data and attributes from Amazon.
477
+ # Returns a +String+.
478
+ #
479
+ def get(headers={}, &block)
480
+ response = @bucket.s3.interface.get(@bucket.name, @name, headers, &block)
481
+ @data = response[:object]
482
+ @headers, @meta_headers = self.class.split_meta(response[:headers])
483
+ # refresh(false) Holy moly, this was doing two extra hits to s3 for making 3 hits for every get!!
484
+ @data
485
+ end
486
+
487
+ # Store object data on S3.
488
+ # Parameter +data+ is a +String+ or S3Object instance.
489
+ # Returns +true+.
490
+ #
491
+ # key = Aws::S3::Key.create(bucket, 'logs/today/1.log')
492
+ # key.data = 'Qwerty'
493
+ # key.put #=> true
494
+ # ...
495
+ # key.put('Olala!') #=> true
496
+ #
497
+ def put(data=nil, perms=nil, headers={})
498
+ headers['x-amz-acl'] = perms if perms
499
+ @data = data || @data
500
+ meta = self.class.add_meta_prefix(@meta_headers)
501
+ @bucket.s3.interface.put(@bucket.name, @name, @data, meta.merge(headers))
502
+ end
503
+
504
+ # Rename an object. Returns new object name.
505
+ #
506
+ # key = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
507
+ # key.rename('logs/today/2.log') #=> 'logs/today/2.log'
508
+ # puts key.name #=> 'logs/today/2.log'
509
+ # key.exists? #=> true
510
+ #
511
+ def rename(new_name)
512
+ @bucket.s3.interface.rename(@bucket.name, @name, new_name)
513
+ @name = new_name
514
+ end
515
+
516
+ # Create an object copy. Returns a destination Aws::S3::Key instance.
517
+ #
518
+ # # Key instance as destination
519
+ # key1 = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
520
+ # key2 = Aws::S3::Key.create(bucket, 'logs/today/2.log') #=> #<Aws::S3::Key:0xb7b5e240 ... >
521
+ # key1.put('Olala!') #=> true
522
+ # key1.copy(key2) #=> #<Aws::S3::Key:0xb7b5e240 ... >
523
+ # key1.exists? #=> true
524
+ # key2.exists? #=> true
525
+ # puts key2.data #=> 'Olala!'
526
+ #
527
+ # # String as destination
528
+ # key = Aws::S3::Key.create(bucket, 'logs/today/777.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
529
+ # key.put('Olala!') #=> true
530
+ # new_key = key.copy('logs/today/888.log') #=> #<Aws::S3::Key:0xb7b5e240 ... >
531
+ # key.exists? #=> true
532
+ # new_key.exists? #=> true
533
+ #
534
+ def copy(new_key_or_name)
535
+ new_key_or_name = Key.create(@bucket, new_key_or_name.to_s) unless new_key_or_name.is_a?(Key)
536
+ @bucket.s3.interface.copy(@bucket.name, @name, new_key_or_name.bucket.name, new_key_or_name.name)
537
+ new_key_or_name
538
+ end
539
+
540
+ # Move an object to other location. Returns a destination Aws::S3::Key instance.
541
+ #
542
+ # # Key instance as destination
543
+ # key1 = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
544
+ # key2 = Aws::S3::Key.create(bucket, 'logs/today/2.log') #=> #<Aws::S3::Key:0xb7b5e240 ... >
545
+ # key1.put('Olala!') #=> true
546
+ # key1.move(key2) #=> #<Aws::S3::Key:0xb7b5e240 ... >
547
+ # key1.exists? #=> false
548
+ # key2.exists? #=> true
549
+ # puts key2.data #=> 'Olala!'
550
+ #
551
+ # # String as destination
552
+ # key = Aws::S3::Key.create(bucket, 'logs/today/777.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
553
+ # key.put('Olala!') #=> true
554
+ # new_key = key.move('logs/today/888.log') #=> #<Aws::S3::Key:0xb7b5e240 ... >
555
+ # key.exists? #=> false
556
+ # new_key.exists? #=> true
557
+ #
558
+ def move(new_key_or_name)
559
+ new_key_or_name = Key.create(@bucket, new_key_or_name.to_s) unless new_key_or_name.is_a?(Key)
560
+ @bucket.s3.interface.move(@bucket.name, @name, new_key_or_name.bucket.name, new_key_or_name.name)
561
+ new_key_or_name
562
+ end
563
+
564
+ # Retrieve key info from bucket and update attributes.
565
+ # Refresh meta-headers (by calling +head+ method) if +head+ is set.
566
+ # Returns +true+ if the key exists in bucket and +false+ otherwise.
567
+ #
568
+ # key = Aws::S3::Key.create(bucket, 'logs/today/1.log')
569
+ # key.e_tag #=> nil
570
+ # key.meta_headers #=> {}
571
+ # key.refresh #=> true
572
+ # key.e_tag #=> '12345678901234567890bf11094484b6'
573
+ # key.meta_headers #=> {"family"=>"qwerty", "name"=>"asdfg"}
574
+ #
575
+ def refresh(head=true)
576
+ new_key = @bucket.key(self)
577
+ @last_modified = new_key.last_modified
578
+ @e_tag = new_key.e_tag
579
+ @size = new_key.size
580
+ @storage_class = new_key.storage_class
581
+ @owner = new_key.owner
582
+ if @last_modified
583
+ self.head
584
+ true
585
+ else
586
+ @headers = @meta_headers = {}
587
+ false
588
+ end
589
+ end
590
+
591
+ # Updates headers and meta-headers from S3.
592
+ # Returns +true+.
593
+ #
594
+ # key.meta_headers #=> {"family"=>"qwerty"}
595
+ # key.head #=> true
596
+ # key.meta_headers #=> {"family"=>"qwerty", "name"=>"asdfg"}
597
+ #
598
+ def head
599
+ @headers, @meta_headers = self.class.split_meta(@bucket.s3.interface.head(@bucket, @name))
600
+ true
601
+ end
602
+
603
+ # Reload meta-headers only. Returns meta-headers hash.
604
+ #
605
+ # key.reload_meta #=> {"family"=>"qwerty", "name"=>"asdfg"}
606
+ #
607
+ def reload_meta
608
+ @meta_headers = self.class.split_meta(@bucket.s3.interface.head(@bucket, @name)).last
609
+ end
610
+
611
+ # Replace meta-headers by new hash at S3. Returns new meta-headers hash.
612
+ #
613
+ # key.reload_meta #=> {"family"=>"qwerty", "name"=>"asdfg"}
614
+ # key.save_meta #=> {"family"=>"oops", "race" => "troll"}
615
+ # key.reload_meta #=> {"family"=>"oops", "race" => "troll"}
616
+ #
617
+ def save_meta(meta_headers)
618
+ meta = self.class.add_meta_prefix(meta_headers)
619
+ @bucket.s3.interface.copy(@bucket.name, @name, @bucket.name, @name, :replace, meta)
620
+ @meta_headers = self.class.split_meta(meta)[1]
621
+ end
622
+
623
+ # Check for existence of the key in the given bucket.
624
+ # Returns +true+ or +false+.
625
+ #
626
+ # key = Aws::S3::Key.create(bucket,'logs/today/1.log')
627
+ # key.exists? #=> false
628
+ # key.put('Woohoo!') #=> true
629
+ # key.exists? #=> true
630
+ #
631
+ def exists?
632
+ @bucket.key(self).last_modified ? true : false
633
+ end
634
+
635
+ # Remove key from bucket.
636
+ # Returns +true+.
637
+ #
638
+ # key.delete #=> true
639
+ #
640
+ def delete
641
+ raise 'Key name must be specified.' if @name.blank?
642
+ @bucket.s3.interface.delete(@bucket, @name)
643
+ end
644
+
645
+ # Return a list of grantees.
646
+ #
647
+ def grantees
648
+ Grantee::grantees(self)
649
+ end
470
650
 
471
- # Retrieve object data and attributes from Amazon.
472
- # Returns a +String+.
473
- #
474
- def get(headers={}, &block)
475
- response = @bucket.s3.interface.get(@bucket.name, @name, headers, &block)
476
- @data = response[:object]
477
- @headers, @meta_headers = self.class.split_meta(response[:headers])
478
- refresh(false)
479
- @data
480
- end
481
-
482
- # Store object data on S3.
483
- # Parameter +data+ is a +String+ or S3Object instance.
484
- # Returns +true+.
485
- #
486
- # key = Aws::S3::Key.create(bucket, 'logs/today/1.log')
487
- # key.data = 'Qwerty'
488
- # key.put #=> true
489
- # ...
490
- # key.put('Olala!') #=> true
491
- #
492
- def put(data=nil, perms=nil, headers={})
493
- headers['x-amz-acl'] = perms if perms
494
- @data = data || @data
495
- meta = self.class.add_meta_prefix(@meta_headers)
496
- @bucket.s3.interface.put(@bucket.name, @name, @data, meta.merge(headers))
497
- end
498
-
499
- # Rename an object. Returns new object name.
500
- #
501
- # key = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
502
- # key.rename('logs/today/2.log') #=> 'logs/today/2.log'
503
- # puts key.name #=> 'logs/today/2.log'
504
- # key.exists? #=> true
505
- #
506
- def rename(new_name)
507
- @bucket.s3.interface.rename(@bucket.name, @name, new_name)
508
- @name = new_name
509
- end
510
-
511
- # Create an object copy. Returns a destination Aws::S3::Key instance.
512
- #
513
- # # Key instance as destination
514
- # key1 = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
515
- # key2 = Aws::S3::Key.create(bucket, 'logs/today/2.log') #=> #<Aws::S3::Key:0xb7b5e240 ... >
516
- # key1.put('Olala!') #=> true
517
- # key1.copy(key2) #=> #<Aws::S3::Key:0xb7b5e240 ... >
518
- # key1.exists? #=> true
519
- # key2.exists? #=> true
520
- # puts key2.data #=> 'Olala!'
521
- #
522
- # # String as destination
523
- # key = Aws::S3::Key.create(bucket, 'logs/today/777.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
524
- # key.put('Olala!') #=> true
525
- # new_key = key.copy('logs/today/888.log') #=> #<Aws::S3::Key:0xb7b5e240 ... >
526
- # key.exists? #=> true
527
- # new_key.exists? #=> true
528
- #
529
- def copy(new_key_or_name)
530
- new_key_or_name = Key.create(@bucket, new_key_or_name.to_s) unless new_key_or_name.is_a?(Key)
531
- @bucket.s3.interface.copy(@bucket.name, @name, new_key_or_name.bucket.name, new_key_or_name.name)
532
- new_key_or_name
533
- end
534
-
535
- # Move an object to other location. Returns a destination Aws::S3::Key instance.
536
- #
537
- # # Key instance as destination
538
- # key1 = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
539
- # key2 = Aws::S3::Key.create(bucket, 'logs/today/2.log') #=> #<Aws::S3::Key:0xb7b5e240 ... >
540
- # key1.put('Olala!') #=> true
541
- # key1.move(key2) #=> #<Aws::S3::Key:0xb7b5e240 ... >
542
- # key1.exists? #=> false
543
- # key2.exists? #=> true
544
- # puts key2.data #=> 'Olala!'
545
- #
546
- # # String as destination
547
- # key = Aws::S3::Key.create(bucket, 'logs/today/777.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
548
- # key.put('Olala!') #=> true
549
- # new_key = key.move('logs/today/888.log') #=> #<Aws::S3::Key:0xb7b5e240 ... >
550
- # key.exists? #=> false
551
- # new_key.exists? #=> true
552
- #
553
- def move(new_key_or_name)
554
- new_key_or_name = Key.create(@bucket, new_key_or_name.to_s) unless new_key_or_name.is_a?(Key)
555
- @bucket.s3.interface.move(@bucket.name, @name, new_key_or_name.bucket.name, new_key_or_name.name)
556
- new_key_or_name
557
- end
558
-
559
- # Retrieve key info from bucket and update attributes.
560
- # Refresh meta-headers (by calling +head+ method) if +head+ is set.
561
- # Returns +true+ if the key exists in bucket and +false+ otherwise.
562
- #
563
- # key = Aws::S3::Key.create(bucket, 'logs/today/1.log')
564
- # key.e_tag #=> nil
565
- # key.meta_headers #=> {}
566
- # key.refresh #=> true
567
- # key.e_tag #=> '12345678901234567890bf11094484b6'
568
- # key.meta_headers #=> {"family"=>"qwerty", "name"=>"asdfg"}
569
- #
570
- def refresh(head=true)
571
- new_key = @bucket.key(self)
572
- @last_modified = new_key.last_modified
573
- @e_tag = new_key.e_tag
574
- @size = new_key.size
575
- @storage_class = new_key.storage_class
576
- @owner = new_key.owner
577
- if @last_modified
578
- self.head
579
- true
580
- else
581
- @headers = @meta_headers = {}
582
- false
583
651
  end
584
- end
585
652
 
586
- # Updates headers and meta-headers from S3.
587
- # Returns +true+.
588
- #
589
- # key.meta_headers #=> {"family"=>"qwerty"}
590
- # key.head #=> true
591
- # key.meta_headers #=> {"family"=>"qwerty", "name"=>"asdfg"}
592
- #
593
- def head
594
- @headers, @meta_headers = self.class.split_meta(@bucket.s3.interface.head(@bucket, @name))
595
- true
596
- end
597
-
598
- # Reload meta-headers only. Returns meta-headers hash.
599
- #
600
- # key.reload_meta #=> {"family"=>"qwerty", "name"=>"asdfg"}
601
- #
602
- def reload_meta
603
- @meta_headers = self.class.split_meta(@bucket.s3.interface.head(@bucket, @name)).last
604
- end
605
-
606
- # Replace meta-headers by new hash at S3. Returns new meta-headers hash.
607
- #
608
- # key.reload_meta #=> {"family"=>"qwerty", "name"=>"asdfg"}
609
- # key.save_meta #=> {"family"=>"oops", "race" => "troll"}
610
- # key.reload_meta #=> {"family"=>"oops", "race" => "troll"}
611
- #
612
- def save_meta(meta_headers)
613
- meta = self.class.add_meta_prefix(meta_headers)
614
- @bucket.s3.interface.copy(@bucket.name, @name, @bucket.name, @name, :replace, meta)
615
- @meta_headers = self.class.split_meta(meta)[1]
616
- end
617
-
618
- # Check for existence of the key in the given bucket.
619
- # Returns +true+ or +false+.
620
- #
621
- # key = Aws::S3::Key.create(bucket,'logs/today/1.log')
622
- # key.exists? #=> false
623
- # key.put('Woohoo!') #=> true
624
- # key.exists? #=> true
625
- #
626
- def exists?
627
- @bucket.key(self).last_modified ? true : false
628
- end
629
-
630
- # Remove key from bucket.
631
- # Returns +true+.
632
- #
633
- # key.delete #=> true
634
- #
635
- def delete
636
- raise 'Key name must be specified.' if @name.blank?
637
- @bucket.s3.interface.delete(@bucket, @name)
638
- end
639
-
640
- # Return a list of grantees.
641
- #
642
- def grantees
643
- Grantee::grantees(self)
644
- end
645
-
646
- end
647
653
 
654
+ class Owner
655
+ attr_reader :id, :name
648
656
 
649
- class Owner
650
- attr_reader :id, :name
657
+ def initialize(id, name)
658
+ @id = id
659
+ @name = name
660
+ end
651
661
 
652
- def initialize(id, name)
653
- @id = id
654
- @name = name
655
- end
656
-
657
- # Return Owner name as a +String+.
658
- def to_s
659
- @name
660
- end
661
- end
662
-
663
-
664
- # There are 2 ways to set permissions for a bucket or key (called a +thing+ below):
665
- #
666
- # 1 . Use +perms+ param to set 'Canned Access Policies' when calling the <tt>bucket.create</tt>,
667
- # <tt>bucket.put</tt> and <tt>key.put</tt> methods.
668
- # The +perms+ param can take these values: 'private', 'public-read', 'public-read-write' and
669
- # 'authenticated-read'.
670
- # (see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html).
671
- #
672
- # bucket = s3.bucket('bucket_for_kd_test_13', true, 'public-read')
673
- # key.put('Woohoo!','public-read-write' )
674
- #
675
- # 2 . Use Grantee instances (the permission is a +String+ or an +Array+ of: 'READ', 'WRITE',
676
- # 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'):
677
- #
678
- # bucket = s3.bucket('my_awesome_bucket', true)
679
- # grantee1 = Aws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL, :apply)
680
- # grantee2 = Aws::S3::Grantee.new(bucket, 'xy3v3...5fhp', [READ, WRITE], :apply)
681
- #
682
- # There is only one way to get and to remove permission (via Grantee instances):
683
- #
684
- # grantees = bucket.grantees # a list of Grantees that have any access for this bucket
685
- # grantee1 = Aws::S3::Grantee.new(bucket, 'a123b...223c')
686
- # grantee1.perms #=> returns a list of perms for this grantee to that bucket
687
- # ...
688
- # grantee1.drop # remove all perms for this grantee
689
- # grantee2.revoke('WRITE') # revoke write access only
690
- #
691
- class Grantee
692
- # A bucket or a key the grantee has an access to.
693
- attr_reader :thing
694
- # Grantee Amazon id.
695
- attr_reader :id
696
- # Grantee display name.
697
- attr_reader :name
698
- # Array of permissions.
699
- attr_accessor :perms
700
-
701
- # Retrieve Owner information and a list of Grantee instances that have
702
- # a access to this thing (bucket or key).
703
- #
704
- # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
705
- # ...
706
- # Aws::S3::Grantee.owner_and_grantees(bucket) #=> [owner, grantees]
707
- #
708
- def self.owner_and_grantees(thing)
709
- if thing.is_a?(Bucket)
710
- bucket, key = thing, ''
711
- else
712
- bucket, key = thing.bucket, thing
662
+ # Return Owner name as a +String+.
663
+ def to_s
664
+ @name
665
+ end
713
666
  end
714
- hash = bucket.s3.interface.get_acl_parse(bucket.to_s, key.to_s)
715
- owner = Owner.new(hash[:owner][:id], hash[:owner][:display_name])
716
667
 
717
- grantees = []
718
- hash[:grantees].each do |id, params|
719
- grantees << new(thing, id, params[:permissions], nil, params[:display_name])
720
- end
721
- [owner, grantees]
722
- end
723
668
 
724
- # Retrieves a list of Grantees instances that have an access to this thing(bucket or key).
669
+ # There are 2 ways to set permissions for a bucket or key (called a +thing+ below):
725
670
  #
726
- # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
727
- # ...
728
- # Aws::S3::Grantee.grantees(bucket) #=> grantees
729
- #
730
- def self.grantees(thing)
731
- owner_and_grantees(thing)[1]
732
- end
733
-
734
- def self.put_acl(thing, owner, grantees) #:nodoc:
735
- if thing.is_a?(Bucket)
736
- bucket, key = thing, ''
737
- else
738
- bucket, key = thing.bucket, thing
739
- end
740
- body = "<AccessControlPolicy>" +
741
- "<Owner>" +
742
- "<ID>#{owner.id}</ID>" +
743
- "<DisplayName>#{owner.name}</DisplayName>" +
744
- "</Owner>" +
745
- "<AccessControlList>" +
746
- grantees.map{|grantee| grantee.to_xml}.join +
747
- "</AccessControlList>" +
748
- "</AccessControlPolicy>"
749
- bucket.s3.interface.put_acl(bucket.to_s, key.to_s, body)
750
- end
751
-
752
- # Create a new Grantee instance.
753
- # Grantee +id+ must exist on S3. If +action+ == :refresh, then retrieve
754
- # permissions from S3 and update @perms. If +action+ == :apply, then apply
755
- # perms to +thing+ at S3. If +action+ == :apply_and_refresh then it performs.
756
- # both the actions. This is used for the new grantees that had no perms to
757
- # this thing before. The default action is :refresh.
671
+ # 1 . Use +perms+ param to set 'Canned Access Policies' when calling the <tt>bucket.create</tt>,
672
+ # <tt>bucket.put</tt> and <tt>key.put</tt> methods.
673
+ # The +perms+ param can take these values: 'private', 'public-read', 'public-read-write' and
674
+ # 'authenticated-read'.
675
+ # (see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html).
758
676
  #
759
- # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
760
- # grantee1 = Aws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL)
761
- # ...
762
- # grantee2 = Aws::S3::Grantee.new(bucket, 'abcde...asdf', [FULL_CONTROL, READ], :apply)
763
- # grantee3 = Aws::S3::Grantee.new(bucket, 'aaaaa...aaaa', 'READ', :apply_and_refresh)
764
- #
765
- def initialize(thing, id, perms=[], action=:refresh, name=nil)
766
- @thing = thing
767
- @id = id
768
- @name = name
769
- @perms = perms.to_a
770
- case action
771
- when :apply then apply
772
- when :refresh then refresh
773
- when :apply_and_refresh then apply; refresh
774
- end
775
- end
776
-
777
- # Return +true+ if the grantee has any permissions to the thing.
778
- def exists?
779
- self.class.grantees(@thing).each do |grantee|
780
- return true if @id == grantee.id
781
- end
782
- false
783
- end
784
-
785
- # Return Grantee type (+String+): "Group" or "CanonicalUser".
786
- def type
787
- @id[/^http:/] ? "Group" : "CanonicalUser"
788
- end
789
-
790
- # Return a name or an id.
791
- def to_s
792
- @name || @id
793
- end
794
-
795
- # Add permissions for grantee.
796
- # Permissions: 'READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'.
797
- # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .
798
- # Returns +true+.
677
+ # bucket = s3.bucket('bucket_for_kd_test_13', true, 'public-read')
678
+ # key.put('Woohoo!','public-read-write' )
799
679
  #
800
- # grantee.grant('FULL_CONTROL') #=> true
801
- # grantee.grant('FULL_CONTROL','WRITE','READ') #=> true
802
- # grantee.grant(['WRITE_ACP','READ','READ_ACP']) #=> true
680
+ # 2 . Use Grantee instances (the permission is a +String+ or an +Array+ of: 'READ', 'WRITE',
681
+ # 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'):
803
682
  #
804
- def grant(*permissions)
805
- permissions.flatten!
806
- old_perms = @perms.dup
807
- @perms += permissions
808
- @perms.uniq!
809
- return true if @perms == old_perms
810
- apply
811
- end
812
-
813
- # Revoke permissions for grantee.
814
- # Permissions: 'READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'
815
- # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .
816
- # Default value is 'FULL_CONTROL'.
817
- # Returns +true+.
683
+ # bucket = s3.bucket('my_awesome_bucket', true)
684
+ # grantee1 = Aws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL, :apply)
685
+ # grantee2 = Aws::S3::Grantee.new(bucket, 'xy3v3...5fhp', [READ, WRITE], :apply)
818
686
  #
819
- # grantee.revoke('READ') #=> true
820
- # grantee.revoke('FULL_CONTROL','WRITE') #=> true
821
- # grantee.revoke(['READ_ACP','WRITE_ACP']) #=> true
687
+ # There is only one way to get and to remove permission (via Grantee instances):
822
688
  #
823
- def revoke(*permissions)
824
- permissions.flatten!
825
- old_perms = @perms.dup
826
- @perms -= permissions
827
- @perms.uniq!
828
- return true if @perms == old_perms
829
- apply
830
- end
831
-
832
- # Revoke all permissions for this grantee.
833
- # Returns +true+.
834
- #
835
- # grantee.drop #=> true
836
- #
837
- def drop
838
- @perms = []
839
- apply
840
- end
841
-
842
- # Refresh grantee perms for its +thing+.
843
- # Returns +true+ if the grantee has perms for this +thing+ or
844
- # +false+ otherwise, and updates @perms value as a side-effect.
845
- #
846
- # grantee.grant('FULL_CONTROL') #=> true
847
- # grantee.refresh #=> true
848
- # grantee.drop #=> true
849
- # grantee.refresh #=> false
850
- #
851
- def refresh
852
- @perms = []
853
- self.class.grantees(@thing).each do |grantee|
854
- if @id == grantee.id
855
- @name = grantee.name
856
- @perms = grantee.perms
857
- return true
858
- end
859
- end
860
- false
861
- end
689
+ # grantees = bucket.grantees # a list of Grantees that have any access for this bucket
690
+ # grantee1 = Aws::S3::Grantee.new(bucket, 'a123b...223c')
691
+ # grantee1.perms #=> returns a list of perms for this grantee to that bucket
692
+ # ...
693
+ # grantee1.drop # remove all perms for this grantee
694
+ # grantee2.revoke('WRITE') # revoke write access only
695
+ #
696
+ class Grantee
697
+ # A bucket or a key the grantee has an access to.
698
+ attr_reader :thing
699
+ # Grantee Amazon id.
700
+ attr_reader :id
701
+ # Grantee display name.
702
+ attr_reader :name
703
+ # Array of permissions.
704
+ attr_accessor :perms
705
+
706
+ # Retrieve Owner information and a list of Grantee instances that have
707
+ # a access to this thing (bucket or key).
708
+ #
709
+ # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
710
+ # ...
711
+ # Aws::S3::Grantee.owner_and_grantees(bucket) #=> [owner, grantees]
712
+ #
713
+ def self.owner_and_grantees(thing)
714
+ if thing.is_a?(Bucket)
715
+ bucket, key = thing, ''
716
+ else
717
+ bucket, key = thing.bucket, thing
718
+ end
719
+ hash = bucket.s3.interface.get_acl_parse(bucket.to_s, key.to_s)
720
+ owner = Owner.new(hash[:owner][:id], hash[:owner][:display_name])
721
+
722
+ grantees = []
723
+ hash[:grantees].each do |id, params|
724
+ grantees << new(thing, id, params[:permissions], nil, params[:display_name])
725
+ end
726
+ [owner, grantees]
727
+ end
728
+
729
+ # Retrieves a list of Grantees instances that have an access to this thing(bucket or key).
730
+ #
731
+ # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
732
+ # ...
733
+ # Aws::S3::Grantee.grantees(bucket) #=> grantees
734
+ #
735
+ def self.grantees(thing)
736
+ owner_and_grantees(thing)[1]
737
+ end
738
+
739
+ def self.put_acl(thing, owner, grantees) #:nodoc:
740
+ if thing.is_a?(Bucket)
741
+ bucket, key = thing, ''
742
+ else
743
+ bucket, key = thing.bucket, thing
744
+ end
745
+ body = "<AccessControlPolicy>" +
746
+ "<Owner>" +
747
+ "<ID>#{owner.id}</ID>" +
748
+ "<DisplayName>#{owner.name}</DisplayName>" +
749
+ "</Owner>" +
750
+ "<AccessControlList>" +
751
+ grantees.map { |grantee| grantee.to_xml }.join +
752
+ "</AccessControlList>" +
753
+ "</AccessControlPolicy>"
754
+ bucket.s3.interface.put_acl(bucket.to_s, key.to_s, body)
755
+ end
756
+
757
+ # Create a new Grantee instance.
758
+ # Grantee +id+ must exist on S3. If +action+ == :refresh, then retrieve
759
+ # permissions from S3 and update @perms. If +action+ == :apply, then apply
760
+ # perms to +thing+ at S3. If +action+ == :apply_and_refresh then it performs.
761
+ # both the actions. This is used for the new grantees that had no perms to
762
+ # this thing before. The default action is :refresh.
763
+ #
764
+ # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
765
+ # grantee1 = Aws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL)
766
+ # ...
767
+ # grantee2 = Aws::S3::Grantee.new(bucket, 'abcde...asdf', [FULL_CONTROL, READ], :apply)
768
+ # grantee3 = Aws::S3::Grantee.new(bucket, 'aaaaa...aaaa', 'READ', :apply_and_refresh)
769
+ #
770
+ def initialize(thing, id, perms=[], action=:refresh, name=nil)
771
+ @thing = thing
772
+ @id = id
773
+ @name = name
774
+ @perms = perms.to_a
775
+ case action
776
+ when :apply then
777
+ apply
778
+ when :refresh then
779
+ refresh
780
+ when :apply_and_refresh then
781
+ apply; refresh
782
+ end
783
+ end
784
+
785
+ # Return +true+ if the grantee has any permissions to the thing.
786
+ def exists?
787
+ self.class.grantees(@thing).each do |grantee|
788
+ return true if @id == grantee.id
789
+ end
790
+ false
791
+ end
792
+
793
+ # Return Grantee type (+String+): "Group" or "CanonicalUser".
794
+ def type
795
+ @id[/^http:/] ? "Group" : "CanonicalUser"
796
+ end
797
+
798
+ # Return a name or an id.
799
+ def to_s
800
+ @name || @id
801
+ end
802
+
803
+ # Add permissions for grantee.
804
+ # Permissions: 'READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'.
805
+ # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .
806
+ # Returns +true+.
807
+ #
808
+ # grantee.grant('FULL_CONTROL') #=> true
809
+ # grantee.grant('FULL_CONTROL','WRITE','READ') #=> true
810
+ # grantee.grant(['WRITE_ACP','READ','READ_ACP']) #=> true
811
+ #
812
+ def grant(*permissions)
813
+ permissions.flatten!
814
+ old_perms = @perms.dup
815
+ @perms += permissions
816
+ @perms.uniq!
817
+ return true if @perms == old_perms
818
+ apply
819
+ end
820
+
821
+ # Revoke permissions for grantee.
822
+ # Permissions: 'READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'
823
+ # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .
824
+ # Default value is 'FULL_CONTROL'.
825
+ # Returns +true+.
826
+ #
827
+ # grantee.revoke('READ') #=> true
828
+ # grantee.revoke('FULL_CONTROL','WRITE') #=> true
829
+ # grantee.revoke(['READ_ACP','WRITE_ACP']) #=> true
830
+ #
831
+ def revoke(*permissions)
832
+ permissions.flatten!
833
+ old_perms = @perms.dup
834
+ @perms -= permissions
835
+ @perms.uniq!
836
+ return true if @perms == old_perms
837
+ apply
838
+ end
839
+
840
+ # Revoke all permissions for this grantee.
841
+ # Returns +true+.
842
+ #
843
+ # grantee.drop #=> true
844
+ #
845
+ def drop
846
+ @perms = []
847
+ apply
848
+ end
849
+
850
+ # Refresh grantee perms for its +thing+.
851
+ # Returns +true+ if the grantee has perms for this +thing+ or
852
+ # +false+ otherwise, and updates @perms value as a side-effect.
853
+ #
854
+ # grantee.grant('FULL_CONTROL') #=> true
855
+ # grantee.refresh #=> true
856
+ # grantee.drop #=> true
857
+ # grantee.refresh #=> false
858
+ #
859
+ def refresh
860
+ @perms = []
861
+ self.class.grantees(@thing).each do |grantee|
862
+ if @id == grantee.id
863
+ @name = grantee.name
864
+ @perms = grantee.perms
865
+ return true
866
+ end
867
+ end
868
+ false
869
+ end
870
+
871
+ # Apply current grantee @perms to +thing+. This method is called internally by the +grant+
872
+ # and +revoke+ methods. In normal use this method should not
873
+ # be called directly.
874
+ #
875
+ # grantee.perms = ['FULL_CONTROL']
876
+ # grantee.apply #=> true
877
+ #
878
+ def apply
879
+ @perms.uniq!
880
+ owner, grantees = self.class.owner_and_grantees(@thing)
881
+ # walk through all the grantees and replace the data for the current one and ...
882
+ grantees.map! { |grantee| grantee.id == @id ? self : grantee }
883
+ # ... if this grantee is not known - add this bad boy to a list
884
+ grantees << self unless grantees.include?(self)
885
+ # set permissions
886
+ self.class.put_acl(@thing, owner, grantees)
887
+ end
888
+
889
+ def to_xml # :nodoc:
890
+ id_str = @id[/^http/] ? "<URI>#{@id}</URI>" : "<ID>#{@id}</ID>"
891
+ grants = ''
892
+ @perms.each do |perm|
893
+ grants << "<Grant>" +
894
+ "<Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
895
+ "xsi:type=\"#{type}\">#{id_str}</Grantee>" +
896
+ "<Permission>#{perm}</Permission>" +
897
+ "</Grant>"
898
+ end
899
+ grants
900
+ end
862
901
 
863
- # Apply current grantee @perms to +thing+. This method is called internally by the +grant+
864
- # and +revoke+ methods. In normal use this method should not
865
- # be called directly.
866
- #
867
- # grantee.perms = ['FULL_CONTROL']
868
- # grantee.apply #=> true
869
- #
870
- def apply
871
- @perms.uniq!
872
- owner, grantees = self.class.owner_and_grantees(@thing)
873
- # walk through all the grantees and replace the data for the current one and ...
874
- grantees.map! { |grantee| grantee.id == @id ? self : grantee }
875
- # ... if this grantee is not known - add this bad boy to a list
876
- grantees << self unless grantees.include?(self)
877
- # set permissions
878
- self.class.put_acl(@thing, owner, grantees)
879
- end
880
-
881
- def to_xml # :nodoc:
882
- id_str = @id[/^http/] ? "<URI>#{@id}</URI>" : "<ID>#{@id}</ID>"
883
- grants = ''
884
- @perms.each do |perm|
885
- grants << "<Grant>" +
886
- "<Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
887
- "xsi:type=\"#{type}\">#{id_str}</Grantee>" +
888
- "<Permission>#{perm}</Permission>" +
889
- "</Grant>"
890
902
  end
891
- grants
892
- end
893
903
 
894
904
  end
895
905
 
896
- end
897
-
898
906
  # Aws::S3Generator and Aws::S3Generator::Bucket methods:
899
907
  #
900
908
  # s3g = Aws::S3Generator.new('1...2', 'nx...Y6') #=> #<Aws::S3Generator:0xb7b5cc94>
@@ -932,170 +940,171 @@ module Aws
932
940
  # # Delete key (method 'DELETE'):
933
941
  # key.delete #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=x...D&Expires=1180820032&AWSAccessKeyId=1...2
934
942
  #
935
- class S3Generator
936
- attr_reader :interface
943
+ class S3Generator
944
+ attr_reader :interface
937
945
 
938
- def initialize(aws_access_key_id, aws_secret_access_key, params={})
939
- @interface = S3Interface.new(aws_access_key_id, aws_secret_access_key, params)
940
- end
941
-
942
- # Generate link to list all buckets
943
- #
944
- # s3.buckets(1.hour)
945
- #
946
- def buckets(expires=nil, headers={})
947
- @interface.list_all_my_buckets_link(expires, headers)
948
- end
949
-
950
- # Create new S3LinkBucket instance and generate link to create it at S3.
951
- #
952
- # bucket= s3.bucket('my_owesome_bucket')
953
- #
954
- def bucket(name, expires=nil, headers={})
955
- Bucket.create(self, name.to_s)
956
- end
957
-
958
- class Bucket
959
- attr_reader :s3, :name
960
-
961
- def to_s
962
- @name
963
- end
964
- alias_method :full_name, :to_s
965
-
966
- # Return a public link to bucket.
967
- #
968
- # bucket.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket'
969
- #
970
- def public_link
971
- params = @s3.interface.params
972
- "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name}"
973
- end
974
-
975
- # Create new S3LinkBucket instance and generate creation link for it.
976
- def self.create(s3, name, expires=nil, headers={})
977
- new(s3, name.to_s)
978
- end
979
-
980
- # Create new S3LinkBucket instance.
981
- def initialize(s3, name)
982
- @s3, @name = s3, name.to_s
983
- end
984
-
985
- # Return a link to create this bucket.
986
- #
987
- def create_link(expires=nil, headers={})
988
- @s3.interface.create_bucket_link(@name, expires, headers)
989
- end
990
-
991
- # Generate link to list keys.
992
- #
993
- # bucket.keys
994
- # bucket.keys('prefix'=>'logs')
995
- #
996
- def keys(options=nil, expires=nil, headers={})
997
- @s3.interface.list_bucket_link(@name, options, expires, headers)
998
- end
999
-
1000
- # Return a S3Generator::Key instance.
1001
- #
1002
- # bucket.key('my_cool_key').get #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=B...D&Expires=1180820032&AWSAccessKeyId=1...2
1003
- # bucket.key('my_cool_key').delete #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=B...D&Expires=1180820098&AWSAccessKeyId=1...2
1004
- #
1005
- def key(name)
1006
- Key.new(self, name)
1007
- end
1008
-
1009
- # Generates link to PUT key data.
1010
- #
1011
- # puts bucket.put('logs/today/1.log', 2.hour)
1012
- #
1013
- def put(key, meta_headers={}, expires=nil, headers={})
1014
- meta = Aws::S3::Key.add_meta_prefix(meta_headers)
1015
- @s3.interface.put_link(@name, key.to_s, nil, expires, meta.merge(headers))
1016
- end
1017
-
1018
- # Generate link to GET key data.
1019
- #
1020
- # bucket.get('logs/today/1.log', 1.hour)
1021
- #
1022
- def get(key, expires=nil, headers={})
1023
- @s3.interface.get_link(@name, key.to_s, expires, headers)
1024
- end
1025
-
1026
- # Generate link to delete bucket.
1027
- #
1028
- # bucket.delete(2.hour)
1029
- #
1030
- def delete(expires=nil, headers={})
1031
- @s3.interface.delete_bucket_link(@name, expires, headers)
1032
- end
1033
- end
1034
-
1035
-
1036
- class Key
1037
- attr_reader :bucket, :name
1038
-
1039
- def to_s
1040
- @name
1041
- end
946
+ def initialize(aws_access_key_id, aws_secret_access_key, params={})
947
+ @interface = S3Interface.new(aws_access_key_id, aws_secret_access_key, params)
948
+ end
1042
949
 
1043
- # Return a full S# name (bucket/key).
950
+ # Generate link to list all buckets
1044
951
  #
1045
- # key.full_name #=> 'my_awesome_bucket/cool_key'
952
+ # s3.buckets(1.hour)
1046
953
  #
1047
- def full_name(separator='/')
1048
- "#{@bucket.to_s}#{separator}#{@name}"
1049
- end
954
+ def buckets(expires=nil, headers={})
955
+ @interface.list_all_my_buckets_link(expires, headers)
956
+ end
1050
957
 
1051
- # Return a public link to key.
1052
- #
1053
- # key.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket/cool_key'
958
+ # Create new S3LinkBucket instance and generate link to create it at S3.
1054
959
  #
1055
- def public_link
1056
- params = @bucket.s3.interface.params
1057
- "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name('/')}"
1058
- end
1059
-
1060
- def initialize(bucket, name, meta_headers={})
1061
- @bucket = bucket
1062
- @name = name.to_s
1063
- @meta_headers = meta_headers
1064
- raise 'Key name can not be empty.' if @name.blank?
1065
- end
1066
-
1067
- # Generate link to PUT key data.
960
+ # bucket= s3.bucket('my_owesome_bucket')
1068
961
  #
1069
- # puts bucket.put('logs/today/1.log', '123', 2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=B...D&Expires=1180820032&AWSAccessKeyId=1...2
1070
- #
1071
- def put(expires=nil, headers={})
1072
- @bucket.put(@name.to_s, @meta_headers, expires, headers)
1073
- end
962
+ def bucket(name, expires=nil, headers={})
963
+ Bucket.create(self, name.to_s)
964
+ end
1074
965
 
1075
- # Generate link to GET key data.
1076
- #
1077
- # bucket.get('logs/today/1.log', 1.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=h...M%3D&Expires=1180820032&AWSAccessKeyId=1...2
1078
- #
1079
- def get(expires=nil, headers={})
1080
- @bucket.s3.interface.get_link(@bucket.to_s, @name, expires, headers)
1081
- end
966
+ class Bucket
967
+ attr_reader :s3, :name
968
+
969
+ def to_s
970
+ @name
971
+ end
972
+
973
+ alias_method :full_name, :to_s
974
+
975
+ # Return a public link to bucket.
976
+ #
977
+ # bucket.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket'
978
+ #
979
+ def public_link
980
+ params = @s3.interface.params
981
+ "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name}"
982
+ end
983
+
984
+ # Create new S3LinkBucket instance and generate creation link for it.
985
+ def self.create(s3, name, expires=nil, headers={})
986
+ new(s3, name.to_s)
987
+ end
988
+
989
+ # Create new S3LinkBucket instance.
990
+ def initialize(s3, name)
991
+ @s3, @name = s3, name.to_s
992
+ end
993
+
994
+ # Return a link to create this bucket.
995
+ #
996
+ def create_link(expires=nil, headers={})
997
+ @s3.interface.create_bucket_link(@name, expires, headers)
998
+ end
999
+
1000
+ # Generate link to list keys.
1001
+ #
1002
+ # bucket.keys
1003
+ # bucket.keys('prefix'=>'logs')
1004
+ #
1005
+ def keys(options=nil, expires=nil, headers={})
1006
+ @s3.interface.list_bucket_link(@name, options, expires, headers)
1007
+ end
1008
+
1009
+ # Return a S3Generator::Key instance.
1010
+ #
1011
+ # bucket.key('my_cool_key').get #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=B...D&Expires=1180820032&AWSAccessKeyId=1...2
1012
+ # bucket.key('my_cool_key').delete #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=B...D&Expires=1180820098&AWSAccessKeyId=1...2
1013
+ #
1014
+ def key(name)
1015
+ Key.new(self, name)
1016
+ end
1017
+
1018
+ # Generates link to PUT key data.
1019
+ #
1020
+ # puts bucket.put('logs/today/1.log', 2.hour)
1021
+ #
1022
+ def put(key, meta_headers={}, expires=nil, headers={})
1023
+ meta = Aws::S3::Key.add_meta_prefix(meta_headers)
1024
+ @s3.interface.put_link(@name, key.to_s, nil, expires, meta.merge(headers))
1025
+ end
1026
+
1027
+ # Generate link to GET key data.
1028
+ #
1029
+ # bucket.get('logs/today/1.log', 1.hour)
1030
+ #
1031
+ def get(key, expires=nil, headers={})
1032
+ @s3.interface.get_link(@name, key.to_s, expires, headers)
1033
+ end
1034
+
1035
+ # Generate link to delete bucket.
1036
+ #
1037
+ # bucket.delete(2.hour)
1038
+ #
1039
+ def delete(expires=nil, headers={})
1040
+ @s3.interface.delete_bucket_link(@name, expires, headers)
1041
+ end
1042
+ end
1082
1043
 
1083
- # Generate link to delete key.
1084
- #
1085
- # bucket.delete(2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=4...D&Expires=1180820032&AWSAccessKeyId=1...2
1086
- #
1087
- def delete(expires=nil, headers={})
1088
- @bucket.s3.interface.delete_link(@bucket.to_s, @name, expires, headers)
1089
- end
1090
1044
 
1091
- # Generate link to head key.
1092
- #
1093
- # bucket.head(2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=4...D&Expires=1180820032&AWSAccessKeyId=1...2
1094
- #
1095
- def head(expires=nil, headers={})
1096
- @bucket.s3.interface.head_link(@bucket.to_s, @name, expires, headers)
1097
- end
1045
+ class Key
1046
+ attr_reader :bucket, :name
1047
+
1048
+ def to_s
1049
+ @name
1050
+ end
1051
+
1052
+ # Return a full S# name (bucket/key).
1053
+ #
1054
+ # key.full_name #=> 'my_awesome_bucket/cool_key'
1055
+ #
1056
+ def full_name(separator='/')
1057
+ "#{@bucket.to_s}#{separator}#{@name}"
1058
+ end
1059
+
1060
+ # Return a public link to key.
1061
+ #
1062
+ # key.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket/cool_key'
1063
+ #
1064
+ def public_link
1065
+ params = @bucket.s3.interface.params
1066
+ "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name('/')}"
1067
+ end
1068
+
1069
+ def initialize(bucket, name, meta_headers={})
1070
+ @bucket = bucket
1071
+ @name = name.to_s
1072
+ @meta_headers = meta_headers
1073
+ raise 'Key name can not be empty.' if @name.blank?
1074
+ end
1075
+
1076
+ # Generate link to PUT key data.
1077
+ #
1078
+ # puts bucket.put('logs/today/1.log', '123', 2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=B...D&Expires=1180820032&AWSAccessKeyId=1...2
1079
+ #
1080
+ def put(expires=nil, headers={})
1081
+ @bucket.put(@name.to_s, @meta_headers, expires, headers)
1082
+ end
1083
+
1084
+ # Generate link to GET key data.
1085
+ #
1086
+ # bucket.get('logs/today/1.log', 1.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=h...M%3D&Expires=1180820032&AWSAccessKeyId=1...2
1087
+ #
1088
+ def get(expires=nil, headers={})
1089
+ @bucket.s3.interface.get_link(@bucket.to_s, @name, expires, headers)
1090
+ end
1091
+
1092
+ # Generate link to delete key.
1093
+ #
1094
+ # bucket.delete(2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=4...D&Expires=1180820032&AWSAccessKeyId=1...2
1095
+ #
1096
+ def delete(expires=nil, headers={})
1097
+ @bucket.s3.interface.delete_link(@bucket.to_s, @name, expires, headers)
1098
+ end
1099
+
1100
+ # Generate link to head key.
1101
+ #
1102
+ # bucket.head(2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=4...D&Expires=1180820032&AWSAccessKeyId=1...2
1103
+ #
1104
+ def head(expires=nil, headers={})
1105
+ @bucket.s3.interface.head_link(@bucket.to_s, @name, expires, headers)
1106
+ end
1107
+ end
1098
1108
  end
1099
- end
1100
1109
 
1101
1110
  end