Skip to content

Commit

Permalink
Merge pull request #1283 from jeffwang0516/tagging-support
Browse files Browse the repository at this point in the history
Support bucket/object tagging command
  • Loading branch information
fviard authored Dec 12, 2023
2 parents 00e925a + ca56e42 commit 4d97114
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 3 deletions.
6 changes: 3 additions & 3 deletions S3/Crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,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']

Expand Down
52 changes: 52 additions & 0 deletions S3/S3.py
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,58 @@ def delete_notification_policy(self, uri):
empty_config = '<NotificationConfiguration></NotificationConfiguration>'
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 = '<Tagging xmlns="http://s3.amazonaws.com/doc/2006-03-01/">'
body += '<TagSet>'
for (key, val) in tagsets:
body += '<Tag>'
body += (' <Key>%s</Key>' % key)
body += (' <Value>%s</Value>' % val)
body += '</Tag>'
body += '</TagSet>'
body += '</Tagging>'
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,
Expand Down
51 changes: 51 additions & 0 deletions s3cmd
Original file line number Diff line number Diff line change
Expand Up @@ -2476,6 +2476,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)
Expand Down Expand Up @@ -2947,6 +2993,11 @@ def get_commands_list():
{"cmd":"signurl", "label":"Sign an S3 URL to provide limited public access with expiry", "param":"s3://BUCKET/OBJECT <expiry_epoch|+expiry_offset>", "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},
Expand Down

0 comments on commit 4d97114

Please sign in to comment.