aboisvert_aws 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/History.txt +329 -0
  2. data/Manifest.txt +61 -0
  3. data/README.txt +163 -0
  4. data/Rakefile +130 -0
  5. data/lib/acf/right_acf_interface.rb +549 -0
  6. data/lib/acf/right_acf_invalidations.rb +144 -0
  7. data/lib/acf/right_acf_origin_access_identities.rb +230 -0
  8. data/lib/acf/right_acf_streaming_interface.rb +229 -0
  9. data/lib/acw/right_acw_interface.rb +248 -0
  10. data/lib/as/right_as_interface.rb +698 -0
  11. data/lib/awsbase/benchmark_fix.rb +39 -0
  12. data/lib/awsbase/right_awsbase.rb +1343 -0
  13. data/lib/awsbase/support.rb +35 -0
  14. data/lib/awsbase/version.rb +9 -0
  15. data/lib/ec2/right_ec2.rb +541 -0
  16. data/lib/ec2/right_ec2_ebs.rb +481 -0
  17. data/lib/ec2/right_ec2_images.rb +444 -0
  18. data/lib/ec2/right_ec2_instances.rb +788 -0
  19. data/lib/ec2/right_ec2_monitoring.rb +70 -0
  20. data/lib/ec2/right_ec2_placement_groups.rb +108 -0
  21. data/lib/ec2/right_ec2_reserved_instances.rb +184 -0
  22. data/lib/ec2/right_ec2_security_groups.rb +491 -0
  23. data/lib/ec2/right_ec2_spot_instances.rb +422 -0
  24. data/lib/ec2/right_ec2_tags.rb +139 -0
  25. data/lib/ec2/right_ec2_vpc.rb +590 -0
  26. data/lib/ec2/right_ec2_vpc2.rb +381 -0
  27. data/lib/ec2/right_ec2_windows_mobility.rb +84 -0
  28. data/lib/elb/right_elb_interface.rb +573 -0
  29. data/lib/emr/right_emr_interface.rb +727 -0
  30. data/lib/iam/right_iam_access_keys.rb +71 -0
  31. data/lib/iam/right_iam_groups.rb +195 -0
  32. data/lib/iam/right_iam_interface.rb +341 -0
  33. data/lib/iam/right_iam_mfa_devices.rb +67 -0
  34. data/lib/iam/right_iam_users.rb +251 -0
  35. data/lib/rds/right_rds_interface.rb +1384 -0
  36. data/lib/right_aws.rb +86 -0
  37. data/lib/route_53/right_route_53_interface.rb +640 -0
  38. data/lib/s3/right_s3.rb +1138 -0
  39. data/lib/s3/right_s3_interface.rb +1278 -0
  40. data/lib/sdb/active_sdb.rb +1107 -0
  41. data/lib/sdb/right_sdb_interface.rb +762 -0
  42. data/lib/sns/right_sns_interface.rb +286 -0
  43. data/lib/sqs/right_sqs.rb +387 -0
  44. data/lib/sqs/right_sqs_gen2.rb +342 -0
  45. data/lib/sqs/right_sqs_gen2_interface.rb +523 -0
  46. data/lib/sqs/right_sqs_interface.rb +593 -0
  47. data/right_aws.gemspec +90 -0
  48. data/test/README.mdown +39 -0
  49. data/test/acf/test_helper.rb +2 -0
  50. data/test/acf/test_right_acf.rb +138 -0
  51. data/test/awsbase/test_helper.rb +2 -0
  52. data/test/awsbase/test_right_awsbase.rb +11 -0
  53. data/test/ec2/test_helper.rb +2 -0
  54. data/test/ec2/test_right_ec2.rb +107 -0
  55. data/test/elb/test_helper.rb +2 -0
  56. data/test/elb/test_right_elb.rb +43 -0
  57. data/test/http_connection.rb +87 -0
  58. data/test/rds/test_helper.rb +2 -0
  59. data/test/rds/test_right_rds.rb +120 -0
  60. data/test/route_53/fixtures/a_record.xml +18 -0
  61. data/test/route_53/fixtures/alias_record.xml +18 -0
  62. data/test/route_53/test_helper.rb +2 -0
  63. data/test/route_53/test_right_route_53.rb +141 -0
  64. data/test/s3/test_helper.rb +2 -0
  65. data/test/s3/test_right_s3.rb +528 -0
  66. data/test/s3/test_right_s3_stubbed.rb +97 -0
  67. data/test/sdb/test_active_sdb.rb +357 -0
  68. data/test/sdb/test_batch_put_attributes.rb +54 -0
  69. data/test/sdb/test_helper.rb +3 -0
  70. data/test/sdb/test_right_sdb.rb +253 -0
  71. data/test/sns/test_helper.rb +2 -0
  72. data/test/sns/test_right_sns.rb +153 -0
  73. data/test/sqs/test_helper.rb +2 -0
  74. data/test/sqs/test_right_sqs.rb +285 -0
  75. data/test/sqs/test_right_sqs_gen2.rb +264 -0
  76. data/test/test_credentials.rb +37 -0
  77. data/test/ts_right_aws.rb +15 -0
  78. metadata +257 -0
@@ -0,0 +1,1138 @@
1
+ #
2
+ # Copyright (c) 2007-2008 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+
24
+ module RightAws
25
+
26
+ # = RightAws::S3 -- RightScale's Amazon S3 interface
27
+ # The RightAws::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 RightAws::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 RightAws::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
+ # :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
63
+ def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
64
+ @interface = S3Interface.new(aws_access_key_id, aws_secret_access_key, params)
65
+ end
66
+
67
+ # Retrieve a list of buckets.
68
+ # Returns an array of RightAws::S3::Bucket instances.
69
+ # # Create handle to S3 account
70
+ # s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)
71
+ # my_buckets_names = s3.buckets.map{|b| b.name}
72
+ # puts "Buckets on S3: #{my_bucket_names.join(', ')}"
73
+ def buckets
74
+ @interface.list_all_my_buckets.map! do |entry|
75
+ owner = Owner.new(entry[:owner_id], entry[:owner_display_name])
76
+ Bucket.new(self, entry[:name], entry[:creation_date], owner)
77
+ end
78
+ end
79
+
80
+ # Retrieve an individual bucket.
81
+ # If the bucket does not exist and +create+ is set, a new bucket
82
+ # is created on S3. Launching this method with +create+=+true+ may
83
+ # affect on the bucket's ACL if the bucket already exists.
84
+ # Returns a RightAws::S3::Bucket instance or +nil+ if the bucket does not exist
85
+ # and +create+ is not set.
86
+ #
87
+ # s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)
88
+ # bucket1 = s3.bucket('my_awesome_bucket_1')
89
+ # bucket1.keys #=> exception here if the bucket does not exists
90
+ # ...
91
+ # bucket2 = s3.bucket('my_awesome_bucket_2', true)
92
+ # bucket2.keys #=> list of keys
93
+ # # create a bucket at the European location with public read access
94
+ # bucket3 = s3.bucket('my-awesome-bucket-3', true, 'public-read', :location => :eu)
95
+ #
96
+ # see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html
97
+ # (section: Canned Access Policies)
98
+ #
99
+ def bucket(name, create=false, perms=nil, headers={})
100
+ result = nil
101
+ if create
102
+ headers['x-amz-acl'] = perms if perms
103
+ @interface.create_bucket(name, headers)
104
+ end
105
+ begin
106
+ buckets.each do |bucket|
107
+ if bucket.name == name
108
+ result = bucket
109
+ break
110
+ end
111
+ end
112
+ rescue RightAws::AwsError => e
113
+ # With non root creds one can use bucket(s) but can't list them
114
+ raise e unless e.message['AccessDenied']
115
+ result = Bucket::new(self, name)
116
+ end
117
+ result
118
+ end
119
+
120
+
121
+ class Bucket
122
+ attr_reader :s3, :name, :owner, :creation_date
123
+
124
+ # Create a Bucket instance.
125
+ # If the bucket does not exist and +create+ is set, a new bucket
126
+ # is created on S3. Launching this method with +create+=+true+ may
127
+ # affect on the bucket's ACL if the bucket already exists.
128
+ # Returns Bucket instance or +nil+ if the bucket does not exist
129
+ # and +create+ is not set.
130
+ #
131
+ # s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)
132
+ # ...
133
+ # bucket1 = RightAws::S3::Bucket.create(s3, 'my_awesome_bucket_1')
134
+ # bucket1.keys #=> exception here if the bucket does not exists
135
+ # ...
136
+ # bucket2 = RightAws::S3::Bucket.create(s3, 'my_awesome_bucket_2', true)
137
+ # bucket2.keys #=> list of keys
138
+ # # create a bucket at the European location with public read access
139
+ # bucket3 = RightAws::S3::Bucket.create(s3,'my-awesome-bucket-3', true, 'public-read', :location => :eu)
140
+ #
141
+ # see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html
142
+ # (section: Canned Access Policies)
143
+ #
144
+ def self.create(s3, name, create=false, perms=nil, headers={})
145
+ s3.bucket(name, create, perms, headers)
146
+ end
147
+
148
+
149
+ # Create a bucket instance. In normal use this method should
150
+ # not be called directly.
151
+ # Use RightAws::S3::Bucket.create or RightAws::S3.bucket instead.
152
+ def initialize(s3, name, creation_date=nil, owner=nil)
153
+ @s3 = s3
154
+ @name = name
155
+ @owner = owner
156
+ @creation_date = creation_date
157
+ if @creation_date && !@creation_date.is_a?(Time)
158
+ @creation_date = Time.parse(@creation_date)
159
+ end
160
+ end
161
+
162
+ # Return bucket name as a String.
163
+ #
164
+ # bucket = RightAws::S3.bucket('my_awesome_bucket')
165
+ # puts bucket #=> 'my_awesome_bucket'
166
+ #
167
+ def to_s
168
+ @name.to_s
169
+ end
170
+ alias_method :full_name, :to_s
171
+
172
+ # Return a public link to bucket.
173
+ #
174
+ # bucket.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket'
175
+ #
176
+ def public_link
177
+ params = @s3.interface.params
178
+ "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name}"
179
+ end
180
+
181
+ # Returns the bucket location
182
+ def location
183
+ @location ||= @s3.interface.bucket_location(@name)
184
+ end
185
+
186
+ # Retrieves the logging configuration for a bucket.
187
+ # Returns a hash of {:enabled, :targetbucket, :targetprefix}
188
+ #
189
+ # bucket.logging_info()
190
+ # => {:enabled=>true, :targetbucket=>"mylogbucket", :targetprefix=>"loggylogs/"}
191
+ def logging_info
192
+ @s3.interface.get_logging_parse(:bucket => @name)
193
+ end
194
+
195
+ # Enables S3 server access logging on a bucket. The target bucket must have been properly configured to receive server
196
+ # access logs.
197
+ # Params:
198
+ # :targetbucket - either the target bucket object or the name of the target bucket
199
+ # :targetprefix - the prefix under which all logs should be stored
200
+ #
201
+ # bucket.enable_logging(:targetbucket=>"mylogbucket", :targetprefix=>"loggylogs/")
202
+ # => true
203
+ def enable_logging(params)
204
+ AwsUtils.mandatory_arguments([:targetbucket, :targetprefix], params)
205
+ AwsUtils.allow_only([:targetbucket, :targetprefix], params)
206
+ 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>"
207
+ @s3.interface.put_logging(:bucket => @name, :xmldoc => xmldoc)
208
+ end
209
+
210
+ # Disables S3 server access logging on a bucket. Takes no arguments.
211
+ def disable_logging
212
+ xmldoc = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><BucketLoggingStatus xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\"></BucketLoggingStatus>"
213
+ @s3.interface.put_logging(:bucket => @name, :xmldoc => xmldoc)
214
+ end
215
+
216
+ # Retrieve a group of keys from Amazon.
217
+ # +options+ is a hash: { 'prefix'=>'', 'marker'=>'', 'max-keys'=>5, 'delimiter'=>'' }).
218
+ # Retrieves meta-headers information if +head+ it +true+.
219
+ # Returns an array of Key instances.
220
+ #
221
+ # bucket.keys #=> # returns all keys from bucket
222
+ # bucket.keys('prefix' => 'logs') #=> # returns all keys that starts with 'logs'
223
+ #
224
+ def keys(options={}, head=false)
225
+ keys_and_service(options, head)[0]
226
+ end
227
+
228
+ # Same as +keys+ method but return an array of [keys, service_data].
229
+ # where +service_data+ is a hash with additional output information.
230
+ #
231
+ # keys, service = bucket.keys_and_service({'max-keys'=> 2, 'prefix' => 'logs'})
232
+ # p keys #=> # 2 keys array
233
+ # p service #=> {"max-keys"=>"2", "prefix"=>"logs", "name"=>"my_awesome_bucket", "marker"=>"", "is_truncated"=>true, :common_prefixes=>[]}
234
+ #
235
+ def keys_and_service(options={}, head=false)
236
+ opt = {}; options.each{ |key, value| opt[key.to_s] = value }
237
+ service = {}
238
+ keys = []
239
+ @s3.interface.incrementally_list_bucket(@name, opt) do |_service|
240
+ service = _service
241
+ service[:contents].each do |entry|
242
+ owner = Owner.new(entry[:owner_id], entry[:owner_display_name])
243
+ key = Key.new(self, entry[:key], nil, {}, {}, entry[:last_modified], entry[:e_tag], entry[:size], entry[:storage_class], owner)
244
+ key.head if head
245
+ keys << key
246
+ end
247
+ end
248
+ service.delete(:contents)
249
+ [keys, service]
250
+ end
251
+
252
+ # Retrieve key information from Amazon.
253
+ # The +key_name+ is a +String+ or Key instance.
254
+ # Retrieves meta-header information if +head+ is +true+.
255
+ # Returns new Key instance.
256
+ #
257
+ # key = bucket.key('logs/today/1.log', true) #=> #<RightAws::S3::Key:0xb7b1e240 ... >
258
+ # # is the same as:
259
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/1.log')
260
+ # key.head
261
+ #
262
+ def key(key_name, head=false)
263
+ raise 'Key name can not be empty.' if key_name.right_blank?
264
+ key_instance = nil
265
+ # if this key exists - find it ....
266
+ keys({'prefix'=>key_name}, head).each do |key|
267
+ if key.name == key_name.to_s
268
+ key_instance = key
269
+ break
270
+ end
271
+ end
272
+ # .... else this key is unknown
273
+ unless key_instance
274
+ key_instance = Key.create(self, key_name.to_s)
275
+ end
276
+ key_instance
277
+ end
278
+
279
+ # Store object data.
280
+ # The +key+ is a +String+ or Key instance.
281
+ # Returns +true+.
282
+ #
283
+ # bucket.put('logs/today/1.log', 'Olala!') #=> true
284
+ #
285
+ def put(key, data=nil, meta_headers={}, perms=nil, headers={})
286
+ key = Key.create(self, key.to_s, data, meta_headers) unless key.is_a?(Key)
287
+ key.put(data, perms, headers)
288
+ end
289
+
290
+ # Retrieve data object from Amazon.
291
+ # The +key+ is a +String+ or Key.
292
+ # Returns String instance.
293
+ #
294
+ # data = bucket.get('logs/today/1.log') #=>
295
+ # puts data #=> 'sasfasfasdf'
296
+ #
297
+ def get(key, headers={})
298
+ key = Key.create(self, key.to_s) unless key.is_a?(Key)
299
+ key.get(headers)
300
+ end
301
+
302
+ # Rename object. Returns RightAws::S3::Key instance.
303
+ #
304
+ # new_key = bucket.rename_key('logs/today/1.log','logs/today/2.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
305
+ # puts key.name #=> 'logs/today/2.log'
306
+ # key.exists? #=> true
307
+ #
308
+ def rename_key(old_key_or_name, new_name)
309
+ old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)
310
+ old_key_or_name.rename(new_name)
311
+ old_key_or_name
312
+ end
313
+
314
+ # Create an object copy. Returns a destination RightAws::S3::Key instance.
315
+ #
316
+ # new_key = bucket.copy_key('logs/today/1.log','logs/today/2.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
317
+ # puts key.name #=> 'logs/today/2.log'
318
+ # key.exists? #=> true
319
+ #
320
+ def copy_key(old_key_or_name, new_key_or_name)
321
+ old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)
322
+ old_key_or_name.copy(new_key_or_name)
323
+ end
324
+
325
+ # Move an object to other location. Returns a destination RightAws::S3::Key instance.
326
+ #
327
+ # new_key = bucket.copy_key('logs/today/1.log','logs/today/2.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
328
+ # puts key.name #=> 'logs/today/2.log'
329
+ # key.exists? #=> true
330
+ #
331
+ def move_key(old_key_or_name, new_key_or_name)
332
+ old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)
333
+ old_key_or_name.move(new_key_or_name)
334
+ end
335
+
336
+ # Remove all keys from a bucket.
337
+ # Returns +true+.
338
+ #
339
+ # bucket.clear #=> true
340
+ #
341
+ def clear
342
+ @s3.interface.clear_bucket(@name)
343
+ end
344
+
345
+ # Delete all keys where the 'folder_key' can be interpreted
346
+ # as a 'folder' name.
347
+ # Returns an array of string keys that have been deleted.
348
+ #
349
+ # bucket.keys.map{|key| key.name}.join(', ') #=> 'test, test/2/34, test/3, test1, test1/logs'
350
+ # bucket.delete_folder('test') #=> ['test','test/2/34','test/3']
351
+ #
352
+ def delete_folder(folder, separator='/')
353
+ @s3.interface.delete_folder(@name, folder, separator)
354
+ end
355
+
356
+ # Delete a bucket. Bucket must be empty.
357
+ # If +force+ is set, clears and deletes the bucket.
358
+ # Returns +true+.
359
+ #
360
+ # bucket.delete(:force => true) #=> true
361
+ #
362
+ def delete(options={})
363
+ force = options.is_a?(Hash) && options[:force]==true
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
372
+
373
+ end
374
+
375
+
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 = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::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 RightAws::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 = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::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
+ # Getter for the 'content-type' metadata
477
+ def content_type
478
+ @headers['content-type'] if @headers
479
+ end
480
+
481
+ # Helper to get and URI-decode a header metadata.
482
+ # Metadata have to be HTTP encoded (rfc2616) as we use the Amazon S3 REST api
483
+ # see http://docs.amazonwebservices.com/AmazonS3/latest/index.html?UsingMetadata.html
484
+ def decoded_meta_headers(key = nil)
485
+ if key
486
+ # Get one metadata value by its key
487
+ URI.decode(@meta_headers[key.to_s])
488
+ else
489
+ # Get a hash of all metadata with a decoded value
490
+ @decoded_meta_headers ||= begin
491
+ metadata = {}
492
+ @meta_headers.each do |key, value|
493
+ metadata[key.to_sym] = URI.decode(value)
494
+ end
495
+ metadata
496
+ end
497
+ end
498
+ end
499
+
500
+ # Retrieve object data and attributes from Amazon.
501
+ # Returns a +String+.
502
+ #
503
+ def get(headers={})
504
+ response = @bucket.s3.interface.get(@bucket.name, @name, headers)
505
+ @data = response[:object]
506
+ @headers, @meta_headers = self.class.split_meta(response[:headers])
507
+ refresh(false)
508
+ @data
509
+ end
510
+
511
+ # Store object data on S3.
512
+ # Parameter +data+ is a +String+ or S3Object instance.
513
+ # Returns +true+.
514
+ #
515
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/1.log')
516
+ # key.data = 'Qwerty'
517
+ # key.put #=> true
518
+ # ...
519
+ # key.put('Olala!') #=> true
520
+ #
521
+ def put(data=nil, perms=nil, headers={})
522
+ headers['x-amz-acl'] = perms if perms
523
+ @data = data || @data
524
+ meta = self.class.add_meta_prefix(@meta_headers)
525
+ @bucket.s3.interface.put(@bucket.name, @name, @data, meta.merge(headers))
526
+ end
527
+
528
+ # Rename an object. Returns new object name.
529
+ #
530
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
531
+ # key.rename('logs/today/2.log') #=> 'logs/today/2.log'
532
+ # puts key.name #=> 'logs/today/2.log'
533
+ # key.exists? #=> true
534
+ #
535
+ def rename(new_name)
536
+ @bucket.s3.interface.rename(@bucket.name, @name, new_name)
537
+ @name = new_name
538
+ end
539
+
540
+ # Create an object copy. Returns a destination RightAws::S3::Key instance.
541
+ #
542
+ # # Key instance as destination
543
+ # key1 = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
544
+ # key2 = RightAws::S3::Key.create(bucket, 'logs/today/2.log') #=> #<RightAws::S3::Key:0xb7b5e240 ... >
545
+ # key1.put('Olala!') #=> true
546
+ # key1.copy(key2) #=> #<RightAws::S3::Key:0xb7b5e240 ... >
547
+ # key1.exists? #=> true
548
+ # key2.exists? #=> true
549
+ # puts key2.data #=> 'Olala!'
550
+ #
551
+ # # String as destination
552
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/777.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
553
+ # key.put('Olala!') #=> true
554
+ # new_key = key.copy('logs/today/888.log') #=> #<RightAws::S3::Key:0xb7b5e240 ... >
555
+ # key.exists? #=> true
556
+ # new_key.exists? #=> true
557
+ #
558
+ def copy(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.copy(@bucket.name, @name, new_key_or_name.bucket.name, new_key_or_name.name)
561
+ new_key_or_name
562
+ end
563
+
564
+ # Move an object to other location. Returns a destination RightAws::S3::Key instance.
565
+ #
566
+ # # Key instance as destination
567
+ # key1 = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
568
+ # key2 = RightAws::S3::Key.create(bucket, 'logs/today/2.log') #=> #<RightAws::S3::Key:0xb7b5e240 ... >
569
+ # key1.put('Olala!') #=> true
570
+ # key1.move(key2) #=> #<RightAws::S3::Key:0xb7b5e240 ... >
571
+ # key1.exists? #=> false
572
+ # key2.exists? #=> true
573
+ # puts key2.data #=> 'Olala!'
574
+ #
575
+ # # String as destination
576
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/777.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
577
+ # key.put('Olala!') #=> true
578
+ # new_key = key.move('logs/today/888.log') #=> #<RightAws::S3::Key:0xb7b5e240 ... >
579
+ # key.exists? #=> false
580
+ # new_key.exists? #=> true
581
+ #
582
+ def move(new_key_or_name)
583
+ new_key_or_name = Key.create(@bucket, new_key_or_name.to_s) unless new_key_or_name.is_a?(Key)
584
+ @bucket.s3.interface.move(@bucket.name, @name, new_key_or_name.bucket.name, new_key_or_name.name)
585
+ new_key_or_name
586
+ end
587
+
588
+ # Retrieve key info from bucket and update attributes.
589
+ # Refresh meta-headers (by calling +head+ method) if +head+ is set.
590
+ # Returns +true+ if the key exists in bucket and +false+ otherwise.
591
+ #
592
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/1.log')
593
+ # key.e_tag #=> nil
594
+ # key.meta_headers #=> {}
595
+ # key.refresh #=> true
596
+ # key.e_tag #=> '12345678901234567890bf11094484b6'
597
+ # key.meta_headers #=> {"family"=>"qwerty", "name"=>"asdfg"}
598
+ #
599
+ def refresh(head=true)
600
+ new_key = @bucket.key(self)
601
+ @last_modified = new_key.last_modified
602
+ @e_tag = new_key.e_tag
603
+ @size = new_key.size
604
+ @storage_class = new_key.storage_class
605
+ @owner = new_key.owner
606
+ if @last_modified
607
+ self.head
608
+ true
609
+ else
610
+ @headers = @meta_headers = {}
611
+ false
612
+ end
613
+ end
614
+
615
+ # Updates headers and meta-headers from S3.
616
+ # Returns +true+.
617
+ #
618
+ # key.meta_headers #=> {"family"=>"qwerty"}
619
+ # key.head #=> true
620
+ # key.meta_headers #=> {"family"=>"qwerty", "name"=>"asdfg"}
621
+ #
622
+ def head
623
+ @headers, @meta_headers = self.class.split_meta(@bucket.s3.interface.head(@bucket, @name))
624
+ true
625
+ end
626
+
627
+ # Reload meta-headers only. Returns meta-headers hash.
628
+ #
629
+ # key.reload_meta #=> {"family"=>"qwerty", "name"=>"asdfg"}
630
+ #
631
+ def reload_meta
632
+ @meta_headers = self.class.split_meta(@bucket.s3.interface.head(@bucket, @name)).last
633
+ end
634
+
635
+ # Replace meta-headers by new hash at S3. Returns new meta-headers hash.
636
+ #
637
+ # key.reload_meta #=> {"family"=>"qwerty", "name"=>"asdfg"}
638
+ # key.save_meta #=> {"family"=>"oops", "race" => "troll"}
639
+ # key.reload_meta #=> {"family"=>"oops", "race" => "troll"}
640
+ #
641
+ def save_meta(meta_headers)
642
+ meta = self.class.add_meta_prefix(meta_headers)
643
+ @bucket.s3.interface.copy(@bucket.name, @name, @bucket.name, @name, :replace, meta)
644
+ @meta_headers = self.class.split_meta(meta)[1]
645
+ end
646
+
647
+ # Check for existence of the key in the given bucket.
648
+ # Returns +true+ or +false+.
649
+ #
650
+ # key = RightAws::S3::Key.create(bucket,'logs/today/1.log')
651
+ # key.exists? #=> false
652
+ # key.put('Woohoo!') #=> true
653
+ # key.exists? #=> true
654
+ #
655
+ def exists?
656
+ @bucket.key(self).last_modified ? true : false
657
+ end
658
+
659
+ # Remove key from bucket.
660
+ # Returns +true+.
661
+ #
662
+ # key.delete #=> true
663
+ #
664
+ def delete
665
+ raise 'Key name must be specified.' if @name.right_blank?
666
+ @bucket.s3.interface.delete(@bucket, @name)
667
+ end
668
+
669
+ # Return a list of grantees.
670
+ #
671
+ def grantees
672
+ Grantee::grantees(self)
673
+ end
674
+
675
+ end
676
+
677
+
678
+ class Owner
679
+ attr_reader :id, :name
680
+
681
+ def initialize(id, name)
682
+ @id = id
683
+ @name = name
684
+ end
685
+
686
+ # Return Owner name as a +String+.
687
+ def to_s
688
+ @name
689
+ end
690
+ end
691
+
692
+
693
+ # There are 2 ways to set permissions for a bucket or key (called a +thing+ below):
694
+ #
695
+ # 1 . Use +perms+ param to set 'Canned Access Policies' when calling the <tt>bucket.create</tt>,
696
+ # <tt>bucket.put</tt> and <tt>key.put</tt> methods.
697
+ # The +perms+ param can take these values: 'private', 'public-read', 'public-read-write' and
698
+ # 'authenticated-read'.
699
+ # (see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html).
700
+ #
701
+ # bucket = s3.bucket('bucket_for_kd_test_13', true, 'public-read')
702
+ # key.put('Woohoo!','public-read-write' )
703
+ #
704
+ # 2 . Use Grantee instances (the permission is a +String+ or an +Array+ of: 'READ', 'WRITE',
705
+ # 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'):
706
+ #
707
+ # bucket = s3.bucket('my_awesome_bucket', true)
708
+ # grantee1 = RightAws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL, :apply)
709
+ # grantee2 = RightAws::S3::Grantee.new(bucket, 'xy3v3...5fhp', [READ, WRITE], :apply)
710
+ #
711
+ # There is only one way to get and to remove permission (via Grantee instances):
712
+ #
713
+ # grantees = bucket.grantees # a list of Grantees that have any access for this bucket
714
+ # grantee1 = RightAws::S3::Grantee.new(bucket, 'a123b...223c')
715
+ # grantee1.perms #=> returns a list of perms for this grantee to that bucket
716
+ # ...
717
+ # grantee1.drop # remove all perms for this grantee
718
+ # grantee2.revoke('WRITE') # revoke write access only
719
+ #
720
+ class Grantee
721
+ # A bucket or a key the grantee has an access to.
722
+ attr_reader :thing
723
+ # Grantee Amazon id.
724
+ attr_reader :id
725
+ # Grantee display name.
726
+ attr_reader :name
727
+ # Array of permissions.
728
+ attr_accessor :perms
729
+
730
+ # Retrieve Owner information and a list of Grantee instances that have
731
+ # a access to this thing (bucket or key).
732
+ #
733
+ # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
734
+ # ...
735
+ # RightAws::S3::Grantee.owner_and_grantees(bucket) #=> [owner, grantees]
736
+ #
737
+ def self.owner_and_grantees(thing)
738
+ if thing.is_a?(Bucket)
739
+ bucket, key = thing, ''
740
+ else
741
+ bucket, key = thing.bucket, thing
742
+ end
743
+ hash = bucket.s3.interface.get_acl_parse(bucket.to_s, key.to_s)
744
+ owner = Owner.new(hash[:owner][:id], hash[:owner][:display_name])
745
+
746
+ grantees = []
747
+ hash[:grantees].each do |id, params|
748
+ grantees << new(thing, id, params[:permissions], nil, params[:display_name])
749
+ end
750
+ [owner, grantees]
751
+ end
752
+
753
+ # Retrieves a list of Grantees instances that have an access to this thing(bucket or key).
754
+ #
755
+ # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
756
+ # ...
757
+ # RightAws::S3::Grantee.grantees(bucket) #=> grantees
758
+ #
759
+ def self.grantees(thing)
760
+ owner_and_grantees(thing)[1]
761
+ end
762
+
763
+ def self.put_acl(thing, owner, grantees) #:nodoc:
764
+ if thing.is_a?(Bucket)
765
+ bucket, key = thing, ''
766
+ else
767
+ bucket, key = thing.bucket, thing
768
+ end
769
+ body = "<AccessControlPolicy>" +
770
+ "<Owner>" +
771
+ "<ID>#{owner.id}</ID>" +
772
+ "<DisplayName>#{owner.name}</DisplayName>" +
773
+ "</Owner>" +
774
+ "<AccessControlList>" +
775
+ grantees.map{|grantee| grantee.to_xml}.join +
776
+ "</AccessControlList>" +
777
+ "</AccessControlPolicy>"
778
+ bucket.s3.interface.put_acl(bucket.to_s, key.to_s, body)
779
+ end
780
+
781
+ # Create a new Grantee instance.
782
+ # Grantee +id+ must exist on S3. If +action+ == :refresh, then retrieve
783
+ # permissions from S3 and update @perms. If +action+ == :apply, then apply
784
+ # perms to +thing+ at S3. If +action+ == :apply_and_refresh then it performs.
785
+ # both the actions. This is used for the new grantees that had no perms to
786
+ # this thing before. The default action is :refresh.
787
+ #
788
+ # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
789
+ # grantee1 = RightAws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL)
790
+ # ...
791
+ # grantee2 = RightAws::S3::Grantee.new(bucket, 'abcde...asdf', [FULL_CONTROL, READ], :apply)
792
+ # grantee3 = RightAws::S3::Grantee.new(bucket, 'aaaaa...aaaa', 'READ', :apply_and_refresh)
793
+ #
794
+ def initialize(thing, id, perms=[], action=:refresh, name=nil)
795
+ @thing = thing
796
+ @id = id
797
+ @name = name
798
+ @perms = Array(perms)
799
+ case action
800
+ when :apply then apply
801
+ when :refresh then refresh
802
+ when :apply_and_refresh then apply; refresh
803
+ end
804
+ end
805
+
806
+ # Return +true+ if the grantee has any permissions to the thing.
807
+ def exists?
808
+ self.class.grantees(@thing).each do |grantee|
809
+ return true if @id == grantee.id
810
+ end
811
+ false
812
+ end
813
+
814
+ # Return Grantee type (+String+): "Group", "AmazonCustomerByEmail" or "CanonicalUser".
815
+ def type
816
+ case @id
817
+ when /^http:/ then "Group"
818
+ when /@/ then "AmazonCustomerByEmail"
819
+ else "CanonicalUser"
820
+ end
821
+ end
822
+
823
+ # Return a name or an id.
824
+ def to_s
825
+ @name || @id
826
+ end
827
+
828
+ # Add permissions for grantee.
829
+ # Permissions: 'READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'.
830
+ # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .
831
+ # Returns +true+.
832
+ #
833
+ # grantee.grant('FULL_CONTROL') #=> true
834
+ # grantee.grant('FULL_CONTROL','WRITE','READ') #=> true
835
+ # grantee.grant(['WRITE_ACP','READ','READ_ACP']) #=> true
836
+ #
837
+ def grant(*permissions)
838
+ permissions.flatten!
839
+ old_perms = @perms.dup
840
+ @perms += permissions
841
+ @perms.uniq!
842
+ return true if @perms == old_perms
843
+ apply
844
+ end
845
+
846
+ # Revoke permissions for grantee.
847
+ # Permissions: 'READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'
848
+ # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .
849
+ # Default value is 'FULL_CONTROL'.
850
+ # Returns +true+.
851
+ #
852
+ # grantee.revoke('READ') #=> true
853
+ # grantee.revoke('FULL_CONTROL','WRITE') #=> true
854
+ # grantee.revoke(['READ_ACP','WRITE_ACP']) #=> true
855
+ #
856
+ def revoke(*permissions)
857
+ permissions.flatten!
858
+ old_perms = @perms.dup
859
+ @perms -= permissions
860
+ @perms.uniq!
861
+ return true if @perms == old_perms
862
+ apply
863
+ end
864
+
865
+ # Revoke all permissions for this grantee.
866
+ # Returns +true+.
867
+ #
868
+ # grantee.drop #=> true
869
+ #
870
+ def drop
871
+ @perms = []
872
+ apply
873
+ end
874
+
875
+ # Refresh grantee perms for its +thing+.
876
+ # Returns +true+ if the grantee has perms for this +thing+ or
877
+ # +false+ otherwise, and updates @perms value as a side-effect.
878
+ #
879
+ # grantee.grant('FULL_CONTROL') #=> true
880
+ # grantee.refresh #=> true
881
+ # grantee.drop #=> true
882
+ # grantee.refresh #=> false
883
+ #
884
+ def refresh
885
+ @perms = []
886
+ self.class.grantees(@thing).each do |grantee|
887
+ if @id == grantee.id
888
+ @name = grantee.name
889
+ @perms = grantee.perms
890
+ return true
891
+ end
892
+ end
893
+ false
894
+ end
895
+
896
+ # Apply current grantee @perms to +thing+. This method is called internally by the +grant+
897
+ # and +revoke+ methods. In normal use this method should not
898
+ # be called directly.
899
+ #
900
+ # grantee.perms = ['FULL_CONTROL']
901
+ # grantee.apply #=> true
902
+ #
903
+ def apply
904
+ @perms.uniq!
905
+ owner, grantees = self.class.owner_and_grantees(@thing)
906
+ # walk through all the grantees and replace the data for the current one and ...
907
+ grantees.map! { |grantee| grantee.id == @id ? self : grantee }
908
+ # ... if this grantee is not known - add this bad boy to a list
909
+ grantees << self unless grantees.include?(self)
910
+ # set permissions
911
+ self.class.put_acl(@thing, owner, grantees)
912
+ end
913
+
914
+ def to_xml # :nodoc:
915
+ id_str = case @id
916
+ when /^http/ then "<URI>#{@id}</URI>"
917
+ when /@/ then "<EmailAddress>#{@id}</EmailAddress>"
918
+ else "<ID>#{@id}</ID>"
919
+ end
920
+ grants = ''
921
+ @perms.each do |perm|
922
+ grants << "<Grant>" +
923
+ "<Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
924
+ "xsi:type=\"#{type}\">#{id_str}</Grantee>" +
925
+ "<Permission>#{perm}</Permission>" +
926
+ "</Grant>"
927
+ end
928
+ grants
929
+ end
930
+
931
+ end
932
+
933
+ end
934
+
935
+ # RightAws::S3Generator and RightAws::S3Generator::Bucket methods:
936
+ #
937
+ # s3g = RightAws::S3Generator.new('1...2', 'nx...Y6') #=> #<RightAws::S3Generator:0xb7b5cc94>
938
+ #
939
+ # # List all buckets(method 'GET'):
940
+ # buckets_list = s3g.buckets #=> 'https://s3.amazonaws.com:443/?Signature=Y...D&Expires=1180941864&AWSAccessKeyId=1...2'
941
+ # # Create bucket link (method 'PUT'):
942
+ # bucket = s3g.bucket('my_awesome_bucket') #=> #<RightAws::S3Generator::Bucket:0xb7bcbda8>
943
+ # link_to_create = bucket.create_link(1.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket?Signature=4...D&Expires=1180942132&AWSAccessKeyId=1...2
944
+ # # ... or:
945
+ # bucket = RightAws::S3Generator::Bucket.create(s3g, 'my_awesome_bucket') #=> #<RightAws::S3Generator::Bucket:0xb7bcbda8>
946
+ # link_to_create = bucket.create_link(1.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket?Signature=4...D&Expires=1180942132&AWSAccessKeyId=1...2
947
+ # # ... or:
948
+ # bucket = RightAws::S3Generator::Bucket.new(s3g, 'my_awesome_bucket') #=> #<RightAws::S3Generator::Bucket:0xb7bcbda8>
949
+ # link_to_create = bucket.create_link(1.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket?Signature=4...D&Expires=1180942132&AWSAccessKeyId=1...2
950
+ # # List bucket(method 'GET'):
951
+ # bucket.keys(1.day) #=> https://s3.amazonaws.com:443/my_awesome_bucket?Signature=i...D&Expires=1180942620&AWSAccessKeyId=1...2
952
+ # # Create/put key (method 'PUT'):
953
+ # bucket.put('my_cool_key') #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=q...D&Expires=1180943094&AWSAccessKeyId=1...2
954
+ # # Get key data (method 'GET'):
955
+ # bucket.get('logs/today/1.log', 1.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=h...M%3D&Expires=1180820032&AWSAccessKeyId=1...2
956
+ # # Delete bucket (method 'DELETE'):
957
+ # bucket.delete(2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=4...D&Expires=1180820032&AWSAccessKeyId=1...2
958
+ #
959
+ # RightAws::S3Generator::Key methods:
960
+ #
961
+ # # Create Key instance:
962
+ # key = RightAws::S3Generator::Key.new(bicket, 'my_cool_key') #=> #<RightAws::S3Generator::Key:0xb7b7394c>
963
+ # # Put key data (method 'PUT'):
964
+ # key.put #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=2...D&Expires=1180943302&AWSAccessKeyId=1...2
965
+ # # Get key data (method 'GET'):
966
+ # key.get #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=a...D&Expires=1180820032&AWSAccessKeyId=1...2
967
+ # # Head key (method 'HEAD'):
968
+ # key.head #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=b...D&Expires=1180820032&AWSAccessKeyId=1...2
969
+ # # Delete key (method 'DELETE'):
970
+ # key.delete #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=x...D&Expires=1180820032&AWSAccessKeyId=1...2
971
+ #
972
+ class S3Generator
973
+ attr_reader :interface
974
+
975
+ def initialize(aws_access_key_id, aws_secret_access_key, params={})
976
+ @interface = S3Interface.new(aws_access_key_id, aws_secret_access_key, params)
977
+ end
978
+
979
+ # Generate link to list all buckets
980
+ #
981
+ # s3.buckets(1.hour)
982
+ #
983
+ def buckets(expires=nil, headers={})
984
+ @interface.list_all_my_buckets_link(expires, headers)
985
+ end
986
+
987
+ # Create new S3LinkBucket instance and generate link to create it at S3.
988
+ #
989
+ # bucket= s3.bucket('my_owesome_bucket')
990
+ #
991
+ def bucket(name, expires=nil, headers={})
992
+ Bucket.create(self, name.to_s)
993
+ end
994
+
995
+ class Bucket
996
+ attr_reader :s3, :name
997
+
998
+ def to_s
999
+ @name
1000
+ end
1001
+ alias_method :full_name, :to_s
1002
+
1003
+ # Return a public link to bucket.
1004
+ #
1005
+ # bucket.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket'
1006
+ #
1007
+ def public_link
1008
+ params = @s3.interface.params
1009
+ "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name}"
1010
+ end
1011
+
1012
+ # Create new S3LinkBucket instance and generate creation link for it.
1013
+ def self.create(s3, name, expires=nil, headers={})
1014
+ new(s3, name.to_s)
1015
+ end
1016
+
1017
+ # Create new S3LinkBucket instance.
1018
+ def initialize(s3, name)
1019
+ @s3, @name = s3, name.to_s
1020
+ end
1021
+
1022
+ # Return a link to create this bucket.
1023
+ #
1024
+ def create_link(expires=nil, headers={})
1025
+ @s3.interface.create_bucket_link(@name, expires, headers)
1026
+ end
1027
+
1028
+ # Generate link to list keys.
1029
+ #
1030
+ # bucket.keys
1031
+ # bucket.keys('prefix'=>'logs')
1032
+ #
1033
+ def keys(options=nil, expires=nil, headers={})
1034
+ @s3.interface.list_bucket_link(@name, options, expires, headers)
1035
+ end
1036
+
1037
+ # Return a S3Generator::Key instance.
1038
+ #
1039
+ # 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
1040
+ # 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
1041
+ #
1042
+ def key(name)
1043
+ Key.new(self, name)
1044
+ end
1045
+
1046
+ # Generates link to PUT key data.
1047
+ #
1048
+ # puts bucket.put('logs/today/1.log', 2.hour)
1049
+ #
1050
+ def put(key, meta_headers={}, expires=nil, headers={})
1051
+ meta = RightAws::S3::Key.add_meta_prefix(meta_headers)
1052
+ @s3.interface.put_link(@name, key.to_s, nil, expires, meta.merge(headers))
1053
+ end
1054
+
1055
+ # Generate link to GET key data.
1056
+ #
1057
+ # bucket.get('logs/today/1.log', 1.hour)
1058
+ #
1059
+ def get(key, expires=nil, headers={}, response_params={})
1060
+ @s3.interface.get_link(@name, key.to_s, expires, headers, response_params)
1061
+ end
1062
+
1063
+ # Generate link to delete bucket.
1064
+ #
1065
+ # bucket.delete(2.hour)
1066
+ #
1067
+ def delete(expires=nil, headers={})
1068
+ @s3.interface.delete_bucket_link(@name, expires, headers)
1069
+ end
1070
+ end
1071
+
1072
+
1073
+ class Key
1074
+ attr_reader :bucket, :name
1075
+
1076
+ def to_s
1077
+ @name
1078
+ end
1079
+
1080
+ # Return a full S# name (bucket/key).
1081
+ #
1082
+ # key.full_name #=> 'my_awesome_bucket/cool_key'
1083
+ #
1084
+ def full_name(separator='/')
1085
+ "#{@bucket.to_s}#{separator}#{@name}"
1086
+ end
1087
+
1088
+ # Return a public link to key.
1089
+ #
1090
+ # key.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket/cool_key'
1091
+ #
1092
+ def public_link
1093
+ params = @bucket.s3.interface.params
1094
+ "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name('/')}"
1095
+ end
1096
+
1097
+ def initialize(bucket, name, meta_headers={})
1098
+ @bucket = bucket
1099
+ @name = name.to_s
1100
+ @meta_headers = meta_headers
1101
+ raise 'Key name can not be empty.' if @name.right_blank?
1102
+ end
1103
+
1104
+ # Generate link to PUT key data.
1105
+ #
1106
+ # 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
1107
+ #
1108
+ def put(expires=nil, headers={})
1109
+ @bucket.put(@name.to_s, @meta_headers, expires, headers)
1110
+ end
1111
+
1112
+ # Generate link to GET key data.
1113
+ #
1114
+ # 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
1115
+ #
1116
+ def get(expires=nil, headers={}, response_params={})
1117
+ @bucket.s3.interface.get_link(@bucket.to_s, @name, expires, headers, response_params)
1118
+ end
1119
+
1120
+ # Generate link to delete key.
1121
+ #
1122
+ # bucket.delete(2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=4...D&Expires=1180820032&AWSAccessKeyId=1...2
1123
+ #
1124
+ def delete(expires=nil, headers={})
1125
+ @bucket.s3.interface.delete_link(@bucket.to_s, @name, expires, headers)
1126
+ end
1127
+
1128
+ # Generate link to head key.
1129
+ #
1130
+ # bucket.head(2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=4...D&Expires=1180820032&AWSAccessKeyId=1...2
1131
+ #
1132
+ def head(expires=nil, headers={})
1133
+ @bucket.s3.interface.head_link(@bucket.to_s, @name, expires, headers)
1134
+ end
1135
+ end
1136
+ end
1137
+
1138
+ end