Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Add AddTags support for EMR connections. #1920

Merged
merged 2 commits into from Dec 17, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 32 additions & 0 deletions boto/emr/connection.py
Expand Up @@ -267,6 +267,26 @@ def list_steps(self, cluster_id, step_states=None, marker=None):

self.get_object('ListSteps', params, StepSummaryList)

def add_tags(self, resource_id, tags):
"""
Create new metadata tags for the specified resource ids.

:type resource_ids: str
:param resource_ids: The cluster id

:type tags: dict
:param tags: A dictionary containing the name/value pairs.
If you want to create only a tag name, the
value for that tag should be the empty string
(e.g. '') or None.
"""
assert isinstance(resource_id, basestring)
params = {
'ResourceId': resource_id,
}
params.update(self._build_tag_list(tags))
return self.get_status('AddTags', params, verb='POST')

def terminate_jobflow(self, jobflow_id):
"""
Terminate an Elastic MapReduce job flow
Expand Down Expand Up @@ -623,6 +643,18 @@ def _build_step_list(self, steps):
params['Steps.member.%s.%s' % (i+1, key)] = value
return params

def _build_tag_list(self, tags):
assert isinstance(tags, dict)

params = {}
for i, key_value in enumerate(sorted(tags.iteritems()), start=1):
key, value = key_value
current_prefix = 'Tags.member.%s' % i
params['%s.Key' % current_prefix] = key
if value:
params['%s.Value' % current_prefix] = value
return params

def _build_instance_common_args(self, ec2_keyname, availability_zone,
keep_alive, hadoop_version):
"""
Expand Down
77 changes: 77 additions & 0 deletions tests/unit/emr/test_connection.py
Expand Up @@ -291,3 +291,80 @@ def test_add_jobflow_steps(self):
self.assertTrue(isinstance(response, JobFlowStepList))
self.assertEqual(response.stepids[0].value, 'Foo')
self.assertEqual(response.stepids[1].value, 'Bar')


class TestBuildTagList(AWSMockServiceTestCase):
connection_class = EmrConnection

def test_key_without_value_encoding(self):
input_dict = {
'KeyWithNoValue': '',
'AnotherKeyWithNoValue': None
}
res = self.service_connection._build_tag_list(input_dict)
# Keys are outputted in ascending key order.
expected = {
'Tags.member.1.Key': 'AnotherKeyWithNoValue',
'Tags.member.2.Key': 'KeyWithNoValue'
}
self.assertEqual(expected, res)

def test_key_full_key_value_encoding(self):
input_dict = {
'FirstKey': 'One',
'SecondKey': 'Two'
}
res = self.service_connection._build_tag_list(input_dict)
# Keys are outputted in ascending key order.
expected = {
'Tags.member.1.Key': 'FirstKey',
'Tags.member.1.Value': 'One',
'Tags.member.2.Key': 'SecondKey',
'Tags.member.2.Value': 'Two'
}
self.assertEqual(expected, res)


class TestAddTag(AWSMockServiceTestCase):
connection_class = EmrConnection

def default_body(self):
return """<AddTagsResponse
xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
<AddTagsResult/>
<ResponseMetadata>
<RequestId>88888888-8888-8888-8888-888888888888</RequestId>
</ResponseMetadata>
</AddTagsResponse>
"""

def test_add_mix_of_tags_with_without_values(self):
input_tags = {
'FirstKey': 'One',
'SecondKey': 'Two',
'ZzzNoValue': ''
}
self.set_http_response(200)

with self.assertRaises(TypeError):
self.service_connection.add_tags()

with self.assertRaises(TypeError):
self.service_connection.add_tags('j-123')

with self.assertRaises(AssertionError):
self.service_connection.add_tags('j-123', [])

response = self.service_connection.add_tags('j-123', input_tags)

self.assertTrue(response)
self.assert_request_parameters({
'Action': 'AddTags',
'ResourceId': 'j-123',
'Tags.member.1.Key': 'FirstKey',
'Tags.member.1.Value': 'One',
'Tags.member.2.Key': 'SecondKey',
'Tags.member.2.Value': 'Two',
'Tags.member.3.Key': 'ZzzNoValue',
'Version': '2009-03-31'
})