aws 2.3.21 → 2.3.22

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