From ca56e423e935c4fe7790d2511147414dc7c9d3e5 Mon Sep 17 00:00:00 2001 From: jeffwang0516 Date: Fri, 11 Nov 2022 00:58:51 +0800 Subject: [PATCH] Support bucket/object tagging command --- S3/Crypto.py | 6 +++--- S3/S3.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ s3cmd | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 3 deletions(-) diff --git a/S3/Crypto.py b/S3/Crypto.py index 14961b40..5094aaa3 100644 --- a/S3/Crypto.py +++ b/S3/Crypto.py @@ -85,9 +85,9 @@ def sign_request_v2(method='GET', canonical_uri='/', params=None, cur_headers=No # valid sub-resources to be included in sign v2: SUBRESOURCES_TO_INCLUDE = ['acl', 'lifecycle', 'location', 'logging', 'notification', 'partNumber', 'policy', - 'requestPayment', 'torrent', 'uploadId', - 'uploads', 'versionId', 'versioning', - 'versions', 'website', + 'requestPayment', 'tagging', 'torrent', + 'uploadId', 'uploads', 'versionId', + 'versioning', 'versions', 'website', # Missing of aws s3 doc but needed 'delete', 'cors', 'restore'] diff --git a/S3/S3.py b/S3/S3.py index 9bf2e924..31ecb347 100644 --- a/S3/S3.py +++ b/S3/S3.py @@ -1213,6 +1213,58 @@ def delete_notification_policy(self, uri): empty_config = '' return self.set_notification_policy(uri, empty_config) + def set_tagging(self, uri, tagsets): + if uri.type != "s3": + raise ValueError("Expected URI type 's3', got '%s'" % uri.type) + body = '' + body += '' + for (key, val) in tagsets: + body += '' + body += (' %s' % key) + body += (' %s' % val) + body += '' + body += '' + body += '' + headers = SortedDict(ignore_case=True) + headers['content-md5'] = generate_content_md5(body) + if uri.has_object(): + request = self.create_request("OBJECT_PUT", uri=uri, + headers=headers, body=body, + uri_params={'tagging': None}) + else: + request = self.create_request("BUCKET_CREATE", bucket=uri.bucket(), + headers=headers, body=body, + uri_params={'tagging': None}) + debug(u"set_tagging(%s): tagset-xml: %s" % (uri, body)) + response = self.send_request(request) + return response + + def get_tagging(self, uri): + if uri.has_object(): + request = self.create_request("OBJECT_GET", uri=uri, + uri_params={'tagging': None}) + else: + request = self.create_request("BUCKET_LIST", bucket=uri.bucket(), + uri_params={'tagging': None}) + debug(u"get_tagging(%s)" % uri) + response = self.send_request(request) + xml_data = response["data"] + # extract list of tag sets + tagsets = getListFromXml(xml_data, "Tag") + debug(u"%s: Got object tagging" % response['status']) + return tagsets + + def delete_tagging(self, uri): + if uri.has_object(): + request = self.create_request("OBJECT_DELETE", uri=uri, + uri_params={'tagging': None}) + else: + request = self.create_request("BUCKET_DELETE", bucket=uri.bucket(), + uri_params={'tagging': None}) + debug(u"delete_tagging(%s)" % uri) + response = self.send_request(request) + return response + def get_multipart(self, uri, uri_params=None, limit=-1): upload_list = [] for truncated, uploads in self.get_multipart_streaming(uri, diff --git a/s3cmd b/s3cmd index 81680590..cfd72d47 100755 --- a/s3cmd +++ b/s3cmd @@ -2371,6 +2371,52 @@ def cmd_delnotification(args): output(u"%s: Notification Policy deleted" % uri) return EX_OK +def cmd_settagging(args): + s3 = S3(Config()) + uri = S3Uri(args[0]) + tag_set_string = args[1] + + tagsets = [ + tuple(tagset.split("=")) + for tagset in tag_set_string.split("&") + ] + debug(tagsets) + response = s3.set_tagging(uri, tagsets) + + debug(u"response - %s" % response['status']) + if response['status'] in [200, 204]: + output(u"%s: Tagging updated" % uri) + return EX_OK + +def cmd_gettagging(args): + s3 = S3(Config()) + uri = S3Uri(args[0]) + + tagsets = s3.get_tagging(uri) + if uri.has_object(): + output(u"%s (object):" % uri) + else: + output(u"%s (bucket):" % uri) + debug(tagsets) + for tag in tagsets: + try: + output(u"\t%s:\t%s" % ( + tag['Key'], + tag['Value'])) + except KeyError: + pass + return EX_OK + +def cmd_deltagging(args): + s3 = S3(Config()) + uri = S3Uri(args[0]) + + response = s3.delete_tagging(uri) + + debug(u"response - %s" % response['status']) + output(u"%s: Tagging deleted" % uri) + return EX_OK + def cmd_multipart(args): cfg = Config() s3 = S3(cfg) @@ -2837,6 +2883,11 @@ def get_commands_list(): {"cmd":"signurl", "label":"Sign an S3 URL to provide limited public access with expiry", "param":"s3://BUCKET/OBJECT ", "func":cmd_signurl, "argc":2}, {"cmd":"fixbucket", "label":"Fix invalid file names in a bucket", "param":"s3://BUCKET[/PREFIX]", "func":cmd_fixbucket, "argc":1}, + ## Tagging commands + {"cmd":"settagging", "label":"Modify tagging for Bucket or Files", "param":"s3://BUCKET[/OBJECT] \"KEY=VALUE[&KEY=VALUE ...]\"", "func":cmd_settagging, "argc":2}, + {"cmd":"gettagging", "label":"Get tagging for Bucket or Files", "param":"s3://BUCKET[/OBJECT]", "func":cmd_gettagging, "argc":1}, + {"cmd":"deltagging", "label":"Delete tagging for Bucket or Files", "param":"s3://BUCKET[/OBJECT]", "func":cmd_deltagging, "argc":1}, + ## Website commands {"cmd":"ws-create", "label":"Create Website from bucket", "param":"s3://BUCKET", "func":cmd_website_create, "argc":1}, {"cmd":"ws-delete", "label":"Delete Website", "param":"s3://BUCKET", "func":cmd_website_delete, "argc":1},