Browse code

* s3cmd, S3/Config.py: [rb] Allow removal of non-empty buckets with --force. [mb, rb] Allow multiple arguments, i.e. create or remove multiple buckets at once. [del] Perform recursive removal with --recursive (or -r).

git-svn-id: https://s3tools.svn.sourceforge.net/svnroot/s3tools/s3cmd/trunk@223 830e0280-6d2a-0410-9c65-932aecc39d9d

Michal Ludvig authored on 2008/09/03 10:48:55
Showing 3 changed files
... ...
@@ -1,5 +1,13 @@
1 1
 2008-09-01  Michal Ludvig  <michal@logix.cz>
2 2
 
3
+	* s3cmd, S3/Config.py: [rb] Allow removal of non-empty buckets
4
+	  with --force.
5
+	  [mb, rb] Allow multiple arguments, i.e. create or remove
6
+	  multiple buckets at once.
7
+	  [del] Perform recursive removal with --recursive (or -r).
8
+
9
+2008-09-01  Michal Ludvig  <michal@logix.cz>
10
+
3 11
 	* s3cmd: Refuse 'sync' together with '--encrypt'.
4 12
 	* S3/S3.py: removed object_{get,put,delete}_uri() functions
5 13
 	  and made object_{get,put,delete}() accept URI instead of 
... ...
@@ -22,6 +22,7 @@ class Config(object):
22 22
 	recv_chunk = 4096
23 23
 	human_readable_sizes = False
24 24
 	force = False
25
+	recursive = False
25 26
 	acl_public = False
26 27
 	proxy_host = ""
27 28
 	proxy_port = 3128
... ...
@@ -125,34 +125,43 @@ def subcmd_bucket_list(s3, uri):
125 125
 			))
126 126
 
127 127
 def cmd_bucket_create(args):
128
-	uri = S3Uri(args[0])
129
-	if not uri.type == "s3" or not uri.has_bucket() or uri.has_object():
130
-		raise ParameterError("Expecting S3 URI with just the bucket name set instead of '%s'" % args[0])
131
-	try:
132
-		s3 = S3(Config())
133
-		response = s3.bucket_create(uri.bucket(), cfg.bucket_location)
134
-	except S3Error, e:
135
-		if S3.codes.has_key(e.info["Code"]):
136
-			error(S3.codes[e.info["Code"]] % uri.bucket())
137
-			return
138
-		else:
139
-			raise
140
-	output("Bucket '%s' created" % uri.bucket())
128
+	s3 = S3(Config())
129
+	for arg in args:
130
+		uri = S3Uri(arg)
131
+		if not uri.type == "s3" or not uri.has_bucket() or uri.has_object():
132
+			raise ParameterError("Expecting S3 URI with just the bucket name set instead of '%s'" % arg)
133
+		try:
134
+			response = s3.bucket_create(uri.bucket(), cfg.bucket_location)
135
+			output("Bucket '%s' created" % uri.uri())
136
+		except S3Error, e:
137
+			if S3.codes.has_key(e.info["Code"]):
138
+				error(S3.codes[e.info["Code"]] % uri.bucket())
139
+				return
140
+			else:
141
+				raise
141 142
 
142 143
 def cmd_bucket_delete(args):
143
-	uri = S3Uri(args[0])
144
-	if not uri.type == "s3" or not uri.has_bucket() or uri.has_object():
145
-		raise ParameterError("Expecting S3 URI with just the bucket name set instead of '%s'" % args[0])
146
-	try:
147
-		s3 = S3(Config())
148
-		response = s3.bucket_delete(uri.bucket())
149
-	except S3Error, e:
150
-		if S3.codes.has_key(e.info["Code"]):
151
-			error(S3.codes[e.info["Code"]] % uri.bucket())
152
-			return
153
-		else:
154
-			raise
155
-	output("Bucket '%s' removed" % uri.bucket())
144
+	def _bucket_delete_one(uri):
145
+		try:
146
+			response = s3.bucket_delete(uri.bucket())
147
+		except S3Error, e:
148
+			if e.info['Code'] == 'BucketNotEmpty' and (cfg.force or cfg.recursive):
149
+				warning("Bucket is not empty. Removing all the objects from it first. This may take some time...")
150
+				subcmd_object_del_uri(uri, recursive = True)
151
+				return _bucket_delete_one(uri)
152
+			elif S3.codes.has_key(e.info["Code"]):
153
+				error(S3.codes[e.info["Code"]] % uri.bucket())
154
+				return
155
+			else:
156
+				raise
157
+		
158
+	s3 = S3(Config())
159
+	for arg in args:
160
+		uri = S3Uri(arg)
161
+		if not uri.type == "s3" or not uri.has_bucket() or uri.has_object():
162
+			raise ParameterError("Expecting S3 URI with just the bucket name set instead of '%s'" % arg)
163
+		_bucket_delete_one(uri)
164
+		output("Bucket '%s' removed" % uri.uri())
156 165
 
157 166
 def cmd_object_put(args):
158 167
 	s3 = S3(Config())
... ...
@@ -260,16 +269,31 @@ def cmd_object_get(args):
260 260
 				(uri, destination, response["size"], response["elapsed"], speed_fmt[0], speed_fmt[1]))
261 261
 
262 262
 def cmd_object_del(args):
263
-	s3 = S3(Config())
264
-
265 263
 	while (len(args)):
266 264
 		uri_arg = args.pop(0)
267 265
 		uri = S3Uri(uri_arg)
268 266
 		if uri.type != "s3" or not uri.has_object():
269 267
 			raise ParameterError("Expecting S3 URI instead of '%s'" % uri_arg)
270 268
 
271
-		response = s3.object_delete(uri)
272
-		output("Object %s deleted" % uri)
269
+		subcmd_object_del_uri(uri)
270
+
271
+def subcmd_object_del_uri(uri, recursive = None):
272
+	s3 = S3(Config())
273
+	if recursive is None:
274
+		recursive = cfg.recursive
275
+	uri_list = []
276
+	if recursive:
277
+		filelist = _get_filelist_remote(uri)
278
+		uri_base = 's3://' + uri.bucket() + "/"
279
+		for idx in filelist:
280
+			object = filelist[idx]
281
+			debug("Adding URI " + uri_base + object['object_key'])
282
+			uri_list.append(S3Uri(uri_base + object['object_key']))
283
+	else:
284
+		uri_list.append(uri)
285
+	for _uri in uri_list:
286
+		response = s3.object_delete(_uri)
287
+		info("Object %s deleted" % _uri)
273 288
 
274 289
 def cmd_info(args):
275 290
 	s3 = S3(Config())
... ...
@@ -885,6 +909,7 @@ def main():
885 885
 	optparser.add_option("-e", "--encrypt", dest="encrypt", action="store_true", help="Encrypt files before uploading to S3.")
886 886
 	optparser.add_option(      "--no-encrypt", dest="encrypt", action="store_false", help="Don't encrypt files.")
887 887
 	optparser.add_option("-f", "--force", dest="force", action="store_true", help="Force overwrite and other dangerous operations.")
888
+	optparser.add_option("-r", "--recursive", dest="recursive", action="store_true", help="Recursive upload, download or removal.")
888 889
 	optparser.add_option("-P", "--acl-public", dest="acl_public", action="store_true", help="Store objects with ACL allowing read for anyone.")
889 890
 	optparser.add_option(      "--acl-private", dest="acl_public", action="store_false", help="Store objects with default ACL allowing access for you only.")
890 891
 	optparser.add_option(      "--delete-removed", dest="delete_removed", action="store_true", help="Delete remote objects with no corresponding local file [sync]")