s33r 0.3.1 → 0.4

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 (120) hide show
  1. data/examples/cli/acl_x.rb +41 -0
  2. data/examples/cli/logging_x.rb +20 -0
  3. data/{bin → examples/cli}/s3cli.rb +13 -20
  4. data/examples/fores33r/README +183 -0
  5. data/examples/fores33r/Rakefile +10 -0
  6. data/examples/fores33r/app/controllers/application.rb +4 -0
  7. data/examples/fores33r/app/controllers/browser_controller.rb +68 -0
  8. data/examples/fores33r/app/helpers/application_helper.rb +8 -0
  9. data/examples/fores33r/app/views/browser/index.rhtml +14 -0
  10. data/examples/fores33r/app/views/browser/show_bucket.rhtml +14 -0
  11. data/examples/fores33r/app/views/layouts/application.rhtml +29 -0
  12. data/examples/fores33r/config/boot.rb +44 -0
  13. data/examples/fores33r/config/database.yml +35 -0
  14. data/examples/fores33r/config/environment.rb +64 -0
  15. data/examples/fores33r/config/environments/development.rb +21 -0
  16. data/examples/fores33r/config/environments/production.rb +18 -0
  17. data/examples/fores33r/config/environments/test.rb +19 -0
  18. data/examples/fores33r/config/routes.rb +23 -0
  19. data/examples/fores33r/doc/README_FOR_APP +2 -0
  20. data/examples/fores33r/log/development.log +5507 -0
  21. data/examples/fores33r/log/production.log +0 -0
  22. data/examples/fores33r/log/server.log +0 -0
  23. data/examples/fores33r/log/test.log +0 -0
  24. data/examples/fores33r/public/404.html +8 -0
  25. data/examples/fores33r/public/500.html +8 -0
  26. data/examples/fores33r/public/dispatch.cgi +10 -0
  27. data/examples/fores33r/public/dispatch.fcgi +24 -0
  28. data/examples/fores33r/public/dispatch.rb +10 -0
  29. data/examples/fores33r/public/favicon.ico +0 -0
  30. data/examples/fores33r/public/images/rails.png +0 -0
  31. data/examples/fores33r/public/javascripts/application.js +2 -0
  32. data/examples/fores33r/public/javascripts/controls.js +815 -0
  33. data/examples/fores33r/public/javascripts/dragdrop.js +913 -0
  34. data/examples/fores33r/public/javascripts/effects.js +958 -0
  35. data/examples/fores33r/public/javascripts/prototype.js +2006 -0
  36. data/examples/fores33r/public/robots.txt +1 -0
  37. data/examples/fores33r/public/stylesheets/core.css +37 -0
  38. data/examples/fores33r/script/about +3 -0
  39. data/examples/fores33r/script/breakpointer +3 -0
  40. data/examples/fores33r/script/console +3 -0
  41. data/examples/fores33r/script/destroy +3 -0
  42. data/examples/fores33r/script/generate +3 -0
  43. data/examples/fores33r/script/performance/benchmarker +3 -0
  44. data/examples/fores33r/script/performance/profiler +3 -0
  45. data/examples/fores33r/script/plugin +3 -0
  46. data/examples/fores33r/script/process/reaper +3 -0
  47. data/examples/fores33r/script/process/spawner +3 -0
  48. data/examples/fores33r/script/runner +3 -0
  49. data/examples/fores33r/script/server +3 -0
  50. data/examples/fores33r/test/test_helper.rb +28 -0
  51. data/examples/fores33r/tmp/sessions/ruby_sess.39d37e054d21d545 +0 -0
  52. data/examples/fores33r/tmp/sessions/ruby_sess.acf71fc73aa74983 +0 -0
  53. data/examples/fores33r/tmp/sessions/ruby_sess.c1697b7d6670f3cd +0 -0
  54. data/examples/s3.yaml +11 -0
  55. data/html/classes/Net/HTTPGenericRequest.html +32 -32
  56. data/html/classes/Net/HTTPResponse.html +20 -19
  57. data/html/classes/S33r.html +422 -190
  58. data/html/classes/S33r/BucketListing.html +107 -70
  59. data/html/classes/S33r/Client.html +888 -414
  60. data/html/classes/S33r/LoggingResource.html +222 -0
  61. data/html/classes/S33r/NamedBucket.html +149 -150
  62. data/html/classes/S33r/OrderlyXmlMarkup.html +165 -0
  63. data/html/classes/S33r/S33rException.html +3 -0
  64. data/html/classes/S33r/S33rException/BucketNotLogTargetable.html +119 -0
  65. data/html/classes/S33r/S33rException/InvalidPermission.html +111 -0
  66. data/html/classes/S33r/S33rException/InvalidS3GroupType.html +111 -0
  67. data/html/classes/S33r/S3ACL.html +125 -0
  68. data/html/classes/S33r/S3ACL/ACLDoc.html +521 -0
  69. data/html/classes/S33r/{S3User.html → S3ACL/AmazonCustomer.html} +27 -30
  70. data/html/classes/S33r/S3ACL/CanonicalUser.html +212 -0
  71. data/html/classes/S33r/S3ACL/Grant.html +403 -0
  72. data/html/classes/S33r/S3ACL/Grantee.html +239 -0
  73. data/html/classes/S33r/S3ACL/Group.html +178 -0
  74. data/html/classes/S33r/S3Object.html +53 -50
  75. data/html/classes/S33r/Sync.html +6 -6
  76. data/html/classes/XML.html +4 -2
  77. data/html/created.rid +1 -1
  78. data/html/files/README_txt.html +82 -28
  79. data/html/files/lib/s33r/bucket_listing_rb.html +1 -8
  80. data/html/files/lib/s33r/builder_rb.html +108 -0
  81. data/html/files/lib/s33r/client_rb.html +2 -1
  82. data/html/files/lib/s33r/core_rb.html +2 -1
  83. data/html/files/lib/s33r/libxml_extensions_rb.html +1 -1
  84. data/html/files/lib/s33r/logging_rb.html +109 -0
  85. data/html/files/lib/s33r/named_bucket_rb.html +1 -1
  86. data/html/files/lib/s33r/s33r_exception_rb.html +1 -1
  87. data/html/files/lib/s33r/s33r_http_rb.html +1 -1
  88. data/html/files/lib/s33r/s3_acl_rb.html +109 -0
  89. data/html/fr_class_index.html +12 -1
  90. data/html/fr_file_index.html +3 -0
  91. data/html/fr_method_index.html +101 -57
  92. data/lib/s33r/bucket_listing.rb +21 -22
  93. data/lib/s33r/builder.rb +20 -0
  94. data/lib/s33r/client.rb +240 -42
  95. data/lib/s33r/core.rb +106 -36
  96. data/lib/s33r/libxml_extensions.rb +2 -2
  97. data/lib/s33r/logging.rb +43 -0
  98. data/lib/s33r/named_bucket.rb +16 -17
  99. data/lib/s33r/s33r_exception.rb +11 -0
  100. data/lib/s33r/s33r_http.rb +2 -1
  101. data/lib/s33r/s3_acl.rb +337 -0
  102. data/test/cases/spec_acl.rb +146 -0
  103. data/test/cases/spec_all_buckets.rb +28 -0
  104. data/test/cases/spec_bucket_listing.rb +2 -2
  105. data/test/cases/spec_client.rb +45 -18
  106. data/test/cases/spec_core.rb +0 -9
  107. data/test/cases/spec_namedbucket.rb +3 -3
  108. data/test/files/acl.xml +47 -0
  109. data/test/files/acl_grant1.xml +7 -0
  110. data/test/files/acl_grant2.xml +6 -0
  111. data/test/files/acl_grant3.xml +6 -0
  112. data/test/files/acl_grant4.xml +6 -0
  113. data/test/files/all_buckets.xml +21 -0
  114. data/test/files/bucket_listing.xml +138 -1
  115. data/test/files/client_config.yml +0 -1
  116. data/test/files/logging_acl.xml +34 -0
  117. data/test/files/namedbucket_config.yml +1 -5
  118. data/test/files/namedbucket_config2.yml +1 -5
  119. data/test/test_setup.rb +1 -0
  120. metadata +132 -7
@@ -0,0 +1,20 @@
1
+ require 'builder'
2
+
3
+ module S33r
4
+ # Variant of XmlMarkup which explicitly orders attributes.
5
+ class OrderlyXmlMarkup < Builder::XmlMarkup
6
+
7
+ # Override Builder _insert_attributes so attributes are ordered.
8
+ def _insert_attributes(attrs, order=[])
9
+ return if attrs.nil?
10
+ order.each do |k|
11
+ v = attrs[k]
12
+ @target << %{ #{k}="#{_attr_value(v)}"} if v
13
+ end
14
+ attrs_sorted = attrs.sort { |x,y| x.to_s <=> y.to_s }
15
+ attrs_sorted.each do |k, v|
16
+ @target << %{ #{k}="#{_attr_value(v)}"} unless order.member?(k)
17
+ end
18
+ end
19
+ end
20
+ end
data/lib/s33r/client.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  require 'net/https'
2
2
  require 'cgi'
3
+ require 'erb'
4
+ require 'yaml'
5
+ require File.join(File.dirname(__FILE__), 's3_acl')
6
+ require File.join(File.dirname(__FILE__), 's33r_exception')
3
7
 
4
8
  module S33r
5
9
  include Net
@@ -15,6 +19,7 @@ module S33r
15
19
  class Client
16
20
  include S33r
17
21
 
22
+ # S3 keys.
18
23
  attr_accessor :aws_access_key, :aws_secret_access_key
19
24
 
20
25
  # Size of data chunk to be sent per request when putting data.
@@ -22,6 +27,15 @@ module S33r
22
27
 
23
28
  # Headers which should be sent with every request by default (unless overridden).
24
29
  attr_accessor :client_headers
30
+
31
+ # Whether client should use SSL.
32
+ attr_accessor :use_ssl
33
+
34
+ # Whether client dumps headers from requests.
35
+ attr_accessor :dump_requests
36
+
37
+ # Default log bucket location.
38
+ attr_accessor :log_bucket
25
39
 
26
40
  # Configure either an SSL-enabled or plain HTTP client.
27
41
  # (If using SSL, no verification of server certificate is performed.)
@@ -30,20 +44,15 @@ module S33r
30
44
  # * <tt>:use_ssl => false</tt>: only use plain HTTP for connections
31
45
  # * <tt>:dump_requests => true</tt>: dump each request's initial line and headers to STDOUT
32
46
  def initialize(aws_access_key, aws_secret_access_key, options={})
33
- if false == options[:use_ssl]
34
- @client = HTTP.new(HOST, NON_SSL_PORT)
35
- @client.use_ssl = false
36
- else
37
- @client = HTTP.new(HOST, PORT)
38
- # turn off SSL certificate verification
39
- @client.verify_mode = OpenSSL::SSL::VERIFY_NONE
40
- @client.use_ssl = true
41
- end
47
+ @use_ssl = true
48
+ @use_ssl = false if (false == options[:use_ssl])
42
49
 
43
50
  @dump_requests = (true == options[:dump_requests])
44
51
 
45
52
  # set default chunk size for streaming request body
46
53
  @chunk_size = DEFAULT_CHUNK_SIZE
54
+
55
+ @log_bucket = options[:log_bucket]
47
56
 
48
57
  # Amazon S3 developer keys
49
58
  @aws_access_key = aws_access_key
@@ -53,10 +62,29 @@ module S33r
53
62
  @client_headers = {}
54
63
  end
55
64
 
65
+ # Get an HTTP client instance.
66
+ #
67
+ # NB this has been moved here so that client instances are
68
+ # only instantiated when needed (so Client can be used
69
+ # as an empty shell when list_buckets is called).
70
+ def get_client
71
+ if @use_ssl
72
+ client = HTTP.new(HOST, PORT)
73
+ # turn off SSL certificate verification
74
+ client.verify_mode = OpenSSL::SSL::VERIFY_NONE
75
+ client.use_ssl = true
76
+ else
77
+ client = HTTP.new(HOST, NON_SSL_PORT)
78
+ client.use_ssl = false
79
+ end
80
+
81
+ client
82
+ end
83
+
56
84
  # Initialise client from YAML configuration file
57
85
  # (see load_config method for details of acceptable format).
58
86
  def Client.init(config_file)
59
- aws_access_key, aws_secret_access_key, options, _ = load_config(config_file)
87
+ aws_access_key, aws_secret_access_key, options = load_config(config_file)
60
88
  Client.new(aws_access_key, aws_secret_access_key, options)
61
89
  end
62
90
 
@@ -64,56 +92,56 @@ module S33r
64
92
  #
65
93
  # :include: test/files/namedbucket_config.yml
66
94
  #
95
+ # Note that the loader also runs the config. file through ERB, so you can
96
+ # add dynamic blocks of ERB (Ruby) code into your files.
97
+ #
67
98
  # The +options+ section contains settings specific to Client and NamedClient instances; +custom+
68
99
  # contains extra settings specific to your application.
69
100
  # +options+ and +custom+ sections can be omitted, but settings for AWS keys are required.
70
101
  #
71
- # Returns an array <tt>[aws_access_key, aws_secret_access_key, options, custom]</tt>, where +options+
72
- # and +custom+ are hashes.
102
+ # Returns an array <tt>[aws_access_key, aws_secret_access_key, options]</tt>, where +options+
103
+ # is a hash.
73
104
  def Client.load_config(config_file)
74
- require 'yaml'
75
- config = YAML::load_file(config_file)
105
+ config = YAML::load(ERB.new(IO.read(config_file)).result)
76
106
  aws_access_key = config['aws_access_key']
77
107
  aws_secret_access_key = config['aws_secret_access_key']
78
108
 
79
109
  options = {}
80
110
  options = S33r.keys_to_symbols(config['options']) if config['options']
81
111
 
82
- custom = {}
83
- custom = S33r.keys_to_symbols(config['custom']) if config['custom']
84
-
85
- [aws_access_key, aws_secret_access_key, options, custom]
86
- end
87
-
88
- # Wrapper round embedded client +use_ssl+ accessor.
89
- def use_ssl?
90
- @client.use_ssl
112
+ [aws_access_key, aws_secret_access_key, options]
91
113
  end
92
114
 
93
115
  # Send a request over the wire.
94
116
  #
95
117
  # This method streams +data+ if it responds to the +stat+ method
96
118
  # (as files do).
119
+ #
120
+ # Returns a Net::HTTPResponse instance.
97
121
  def do_request(method, path, data=nil, headers={})
98
122
  req = get_requester(method, path)
99
123
  req.chunk_size = @chunk_size
100
124
 
101
- # add the S3 headers which are always required
125
+ # Add the S3 headers which are always required.
102
126
  headers = add_default_headers(headers)
103
127
 
104
- # add any client-specific default headers
128
+ # Add any client-specific default headers.
105
129
  headers = add_client_headers(headers)
106
130
 
131
+ # Generate the S3 authorization header.
107
132
  headers['Authorization'] = generate_auth_header_value(method, path, headers,
108
133
  @aws_access_key, @aws_secret_access_key)
109
134
 
135
+ # Insert the headers into the request object.
110
136
  headers.each do |key, value|
111
137
  req[key] = value
112
138
  end
113
139
 
140
+ # Add data to the request as a stream.
114
141
  if req.request_body_permitted?
115
- # for streaming files; NB Content-Length will be set by Net::HTTP
116
- # for character-based body content
142
+ # For streaming files; NB Content-Length will be set by Net::HTTP
143
+ # for character-based data: this section of code is only used
144
+ # when reading directly from a file.
117
145
  if data.respond_to?(:stat)
118
146
  req.body_stream = data
119
147
  req['Content-Length'] = data.stat.size.to_s
@@ -127,9 +155,14 @@ module S33r
127
155
  puts req.to_s
128
156
  end
129
157
 
130
- @client.start do
131
- response = @client.request(req, data)
158
+ # Run the request.
159
+ client = get_client
160
+ client.start do
161
+ response = client.request(req, data)
162
+
163
+ # Check the response to see whether S3 is down.
132
164
  response.check_s3_availability
165
+
133
166
  response
134
167
  end
135
168
  end
@@ -141,8 +174,26 @@ module S33r
141
174
  end
142
175
 
143
176
  # List all buckets.
177
+ #
178
+ # Returns an Array of NamedBucket instances.
144
179
  def list_buckets
145
- do_get('/')
180
+ bucket_list_xml = do_get('/').body
181
+ doc = XML.get_xml_doc(S33r.remove_namespace(bucket_list_xml))
182
+
183
+ named_buckets = []
184
+
185
+ doc.find("//Bucket").to_a.each do |node|
186
+ bucket_name = node.xget('Name')
187
+ named_buckets << NamedBucket.new(@aws_access_key, @aws_secret_access_key,
188
+ {:default_bucket => bucket_name, :dump_request => self.dump_requests})
189
+ end
190
+
191
+ named_buckets
192
+ end
193
+
194
+ # List just bucket names.
195
+ def list
196
+ list_buckets.map {|bucket| bucket.name}
146
197
  end
147
198
 
148
199
  # List entries in a bucket.
@@ -154,25 +205,42 @@ module S33r
154
205
  # * <tt>:max_keys => 1000</tt>: return at most this number of keys (maximum possible value is 1000)
155
206
  # * <tt>:delimiter => 'some_string'</tt>: keys containing the same string between prefix and the delimiter
156
207
  # are rolled up into a CommonPrefixes element inside the response
208
+ #
209
+ # NB if you pass a :marker, this takes up one of your :max_keys; so if you are fetching page
210
+ # two from a bucket, and you want 10 items, you need to set :max_keys to 11.
211
+ #
212
+ # To page through a bucket 10 keys at a time, you can do:
213
+ #
214
+ # resp, listing = list_bucket('mybucket', :max_keys => 10)
215
+ # resp, listing = list_bucket('mybucket', :max_keys => 11, :marker => listing.last_key)
216
+ # resp, listing = list_bucket('mybucket', :max_keys => 11, :marker => listing.last_key)
217
+ # etc.
218
+ #
219
+ # Note in the example code, +listing+ is a BucketListing instance; call its contents method
220
+ # to get a hash of the keys in the bucket, along with associated objects.
221
+ #
222
+ # Returns [raw_response, BucketListing instance].
157
223
  def list_bucket(bucket_name, query_params={})
158
224
  if query_params[:max_keys]
159
225
  max_keys = query_params[:max_keys].to_i
160
226
  raise S33rException::BucketListingMaxKeysError, "max_keys option to list bucket cannot be > #{BUCKET_LIST_MAX_MAX_KEYS}" \
161
227
  if max_keys > BUCKET_LIST_MAX_MAX_KEYS
162
228
 
163
- # take out the max_keys parameter and move it to max-keys
229
+ # convert max_keys parameter to :max-keys parameter
164
230
  query_params['max-keys'] = query_params.delete(:max_keys)
165
231
  end
166
232
 
167
233
  resp = do_get("/#{bucket_name}" + generate_querystring(query_params))
168
- bucket_listing = BucketListing.new(resp.body)
169
234
 
170
- [resp, bucket_listing]
235
+ [resp, BucketListing.new(resp.body)]
171
236
  end
172
237
 
173
238
  # Create a bucket.
239
+ #
240
+ # Returns true if response returned a 200 code; false otherwise.
174
241
  def create_bucket(bucket_name, headers={})
175
- do_put("/#{bucket_name}", nil, headers)
242
+ resp = do_put("/#{bucket_name}", nil, headers)
243
+ resp.ok?
176
244
  end
177
245
 
178
246
  # Delete a bucket.
@@ -180,6 +248,7 @@ module S33r
180
248
  # +options+ hash can contain the following:
181
249
  # * <tt>:force => true</tt>: delete all keys within the bucket then delete the bucket itself
182
250
  #-- TODO: maybe delete keys matching a partial path
251
+ #-- TODO: if multiple pages of keys in buckets, need to get them by page.
183
252
  def delete_bucket(bucket_name, headers={}, options={})
184
253
  if true == options[:force]
185
254
  _, bucket_listing = list_bucket(bucket_name)
@@ -191,14 +260,11 @@ module S33r
191
260
  do_delete("/#{bucket_name}", headers)
192
261
  end
193
262
 
263
+ # Check whether a bucket exists or not.
264
+ #
194
265
  # Returns true if bucket exists.
195
266
  def bucket_exists?(bucket_name)
196
- do_head("/#{bucket_name}").ok?
197
- end
198
-
199
- # Fetch head info for a key in a bucket.
200
- def head_resource(bucket_name, resource_key, headers={})
201
- do_head("/#{bucket_name}/#{resource_key}", headers)
267
+ resource_exists?(bucket_name)
202
268
  end
203
269
 
204
270
  # Fetch a resource.
@@ -206,10 +272,139 @@ module S33r
206
272
  do_get("/#{bucket_name}/#{resource_key}", headers)
207
273
  end
208
274
 
275
+ # Check whether a bucket contains a key.
276
+ #
277
+ # Returns true if resource_key exists inside bucket_name exists.
278
+ def resource_exists?(bucket_name, resource_key=nil)
279
+ path = "/#{bucket_name}"
280
+ path += "/#{resource_key}" unless resource_key.nil?
281
+ do_head(path).ok?
282
+ end
283
+
284
+ # Fetch an object.
285
+ #
209
286
  # TODO: return S3Object
210
287
  def get_object(bucket_name, resource_key, headers)
211
288
  response = get_resource(bucket_name, resource_key, headers)
212
289
  end
290
+
291
+ # Fetch the ACL document for a resource.
292
+ #
293
+ # Returns nil if there is a problem with the resource
294
+ # (e.g. it doesn't exist).
295
+ def get_acl(bucket_name, resource_key='')
296
+ path = s3_acl_path(bucket_name, resource_key)
297
+ response = do_get(path)
298
+ if response.ok?
299
+ S3ACL::ACLDoc.from_xml(response.body)
300
+ else
301
+ raise S33rException::MissingResource, "Tried to get an ACL from a non-existent resource [#{path}]"
302
+ end
303
+ end
304
+
305
+ # Put the ACL document for a resource.
306
+ #
307
+ # +acl_doc+ is an S33r::S3ACL::ACLDoc instance.
308
+ #
309
+ # Returns true if response had a 200 code, false otherwise.
310
+ # If you get a 400 Bad Request back, it means a CanonicalUser
311
+ # could not be identified from the email address.
312
+ def set_acl(acl_doc, bucket_name, resource_key='')
313
+ path = s3_acl_path(bucket_name, resource_key)
314
+ response = do_put(path, acl_doc.to_xml)
315
+ response.ok?
316
+ end
317
+
318
+ # Set up logging for a bucket and resource key.
319
+ #
320
+ # +logging_resource+ = a LoggingResource instance.
321
+ # +bucket_name+ = a bucket to log.
322
+ # +resource_key+ = a resource to log (if empty, logging
323
+ # gets added to the bucket).
324
+ def set_logging(logging_resource, bucket_name, resource_key='')
325
+ path = s3_logging_path(bucket_name, resource_key)
326
+ response = do_put(path, logging_resource.to_xml)
327
+ end
328
+
329
+ # Make a resource public (i.e. grant READ permissions
330
+ # to the AllUsers group type). NB separate method is used
331
+ # on buckets, to make all of their content public too.
332
+ #
333
+ # Returns nil if resource does not exist.
334
+ def make_public(bucket_name, resource_key='')
335
+ acl = get_acl(bucket_name, resource_key)
336
+ if !acl.nil? and acl.add_public_read_grants
337
+ set_acl(acl, bucket_name, resource_key)
338
+ end
339
+ end
340
+
341
+ # TODO
342
+ def make_private
343
+ end
344
+
345
+ # Make a bucket capable of being a target for access logging.
346
+ #
347
+ # Returns true if the bucket is now a possible log target;
348
+ # false otherwise.
349
+ #
350
+ #-- TODO: tests
351
+ def enable_log_target(bucket_name)
352
+ acl = get_acl(bucket_name)
353
+ if acl.add_log_target_grants
354
+ set_acl(acl, bucket_name)
355
+ end
356
+ acl.log_targetable?
357
+ end
358
+
359
+ # Disable permissions for access logging into a bucket.
360
+ #
361
+ # Returns true if the bucket is no longer log targetable;
362
+ # false if it remains a log target.
363
+ #
364
+ #-- TODO: tests
365
+ def disable_log_target(bucket_name)
366
+ acl = get_acl(bucket_name)
367
+ acl.remove_log_target
368
+ set_acl(acl, bucket_name)
369
+ !acl.log_targetable?
370
+ end
371
+
372
+ # Enable logging for a resource (bucket or key).
373
+ #
374
+ # +log_prefix+ is the prefix for the logs.
375
+ # +bucket_name+ is the bucket to log.
376
+ # +log_bucket+ is the bucket to put logs into.
377
+ #
378
+ # options:
379
+ # +:for_key => 'key'+ is the (optional) resource to log in the bucket
380
+ # (NB this is not currently supported by S3).
381
+ # +:log_prefix => 'prefix'+ is the (optional) log file prefix
382
+ # (defaults to bucket_name + '-')
383
+ #
384
+ def enable_logging(bucket_name, log_bucket=nil, options={})
385
+ log_bucket ||= @log_bucket
386
+
387
+ resource_key = options[:for_key]
388
+ resource_key ||= ''
389
+
390
+ log_prefix = options[:prefix]
391
+ log_prefix ||= bucket_name + '-'
392
+
393
+ log_bucket_acl = get_acl(log_bucket)
394
+ if !(log_bucket_acl.log_targetable?)
395
+ raise BucketNotLogTargetable, "The bucket #{log_bucket} cannot be specified as a log target"
396
+ end
397
+ logging_resource = LoggingResource.new(log_bucket, log_prefix)
398
+ set_logging(logging_resource, bucket_name, resource_key)
399
+ end
400
+
401
+ # TODO
402
+ def disable_logging
403
+ end
404
+
405
+ # TODO
406
+ def get_logging
407
+ end
213
408
 
214
409
  # Put some generic resource onto S3.
215
410
  def put_resource(bucket_name, resource_key, data, headers={})
@@ -266,6 +461,10 @@ module S33r
266
461
  end
267
462
 
268
463
  # Delete a resource from S3.
464
+ #
465
+ # Note that S3 returns the same response code () regardless
466
+ # of whether the resource was successfully deleted, or didn't exist
467
+ # in the first place.
269
468
  def delete_resource(bucket_name, resource_key, headers={})
270
469
  do_delete("/#{bucket_name}/#{resource_key}", headers)
271
470
  end
@@ -280,9 +479,8 @@ module S33r
280
479
  headers.merge!(client_headers) { |key, arg, default| arg }
281
480
  end
282
481
 
283
- protected
284
482
  def do_get(path='/', headers={})
285
- do_request('GET', path, headers)
483
+ do_request('GET', path, nil, headers)
286
484
  end
287
485
 
288
486
  def do_head(path='/', headers={})