aboisvert_aws 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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