From f824110ceb19148d8ab43f541cae2a9cb7e08c8b Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Wed, 20 Feb 2013 23:19:43 -0500 Subject: [PATCH] add ec2 tags --- moto/ec2/models.py | 39 ++++++++++++++- moto/ec2/responses/__init__.py | 4 +- moto/ec2/responses/tags.py | 50 +++++++++++++++++++ moto/ec2/utils.py | 13 +++++ .../{test_ec2.py => test_instances.py} | 0 tests/test_ec2/test_tags.py | 20 ++++++++ 6 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 moto/ec2/responses/tags.py rename tests/test_ec2/{test_ec2.py => test_instances.py} (100%) create mode 100644 tests/test_ec2/test_tags.py diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 54c761d35..f0c50c30b 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -1,13 +1,16 @@ +from collections import defaultdict + from boto.ec2.instance import Instance, InstanceState, Reservation from moto.core import BaseBackend from .utils import random_instance_id, random_reservation_id -class EC2Backend(BaseBackend): +class InstanceBackend(object): def __init__(self): self.reservations = {} + super(InstanceBackend, self).__init__() def get_instance(self, instance_id): for instance in self.all_instances(): @@ -73,4 +76,36 @@ class EC2Backend(BaseBackend): return self.reservations.values() -ec2_backend = EC2Backend() \ No newline at end of file +class TagBackend(object): + + def __init__(self): + self.tags = defaultdict(dict) + super(TagBackend, self).__init__() + + def create_tag(self, resource_id, key, value): + self.tags[resource_id][key] = value + return value + + def delete_tag(self, resource_id, key): + return self.tags[resource_id].pop(key) + + def describe_tags(self): + results = [] + for resource_id, tags in self.tags.iteritems(): + ami = 'ami' in resource_id + for key, value in tags.iteritems(): + result = { + 'resource_id': resource_id, + 'key': key, + 'value': value, + 'resource_type': 'image' if ami else 'instance', + } + results.append(result) + return results + + +class EC2Backend(BaseBackend, InstanceBackend, TagBackend): + pass + + +ec2_backend = EC2Backend() diff --git a/moto/ec2/responses/__init__.py b/moto/ec2/responses/__init__.py index 61769ef01..130902ef0 100644 --- a/moto/ec2/responses/__init__.py +++ b/moto/ec2/responses/__init__.py @@ -3,11 +3,12 @@ from urlparse import parse_qs from moto.ec2.utils import method_namess_from_class from .instances import InstanceResponse +from .tags import TagResponse class EC2Response(object): - sub_responses = [InstanceResponse,] + sub_responses = [InstanceResponse, TagResponse] def dispatch(self, uri, body, headers): if body: @@ -23,3 +24,4 @@ class EC2Response(object): response = sub_response(querystring) method = getattr(response, action) return method() + import pdb;pdb.set_trace() diff --git a/moto/ec2/responses/tags.py b/moto/ec2/responses/tags.py new file mode 100644 index 000000000..4914418c2 --- /dev/null +++ b/moto/ec2/responses/tags.py @@ -0,0 +1,50 @@ +from jinja2 import Template + +from moto.ec2.models import ec2_backend +from moto.ec2.utils import resource_ids_from_querystring, camelcase_to_underscores, method_namess_from_class + + +class TagResponse(object): + def __init__(self, querystring): + self.querystring = querystring + self.resource_ids = resource_ids_from_querystring(querystring) + + def CreateTags(self): + for resource_id, tag in self.resource_ids.iteritems(): + ec2_backend.create_tag(resource_id, tag[0], tag[1]) + return CREATE_RESPONSE + + def DeleteTags(self): + ec2_backend.delete_tag() + template = Template(DELETE_RESPONSE) + return template.render(reservations=ec2_backend.all_reservations()) + + def DescribeTags(self): + tags = ec2_backend.describe_tags() + template = Template(DESCRIBE_RESPONSE) + return template.render(tags=tags) + + +CREATE_RESPONSE = """ + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + true +""" + +DELETE_RESPONSE = """ + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + true +""" + +DESCRIBE_RESPONSE = """ + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + {% for tag in tags %} + + {{ tag.resource_id }} + {{ tag.resource_type }} + {{ tag.key }} + {{ tag.value }} + + {% endfor %} + +""" diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py index f63fbef72..9eaa764a2 100644 --- a/moto/ec2/utils.py +++ b/moto/ec2/utils.py @@ -26,6 +26,19 @@ def instance_ids_from_querystring(querystring_dict): return instance_ids +def resource_ids_from_querystring(querystring_dict): + prefix = 'ResourceId' + response_values = {} + for key, value in querystring_dict.iteritems(): + if prefix in key: + resource_index = key.replace(prefix + ".", "") + tag_key = querystring_dict.get("Tag.{}.Key".format(resource_index))[0] + tag_value = querystring_dict.get("Tag.{}.Value".format(resource_index))[0] + response_values[value[0]] = (tag_key, tag_value) + + return response_values + + def camelcase_to_underscores(argument): ''' Converts a camelcase param like theNewAttribute to the equivalent python underscore variable like the_new_attribute''' diff --git a/tests/test_ec2/test_ec2.py b/tests/test_ec2/test_instances.py similarity index 100% rename from tests/test_ec2/test_ec2.py rename to tests/test_ec2/test_instances.py diff --git a/tests/test_ec2/test_tags.py b/tests/test_ec2/test_tags.py new file mode 100644 index 000000000..fe39c25c2 --- /dev/null +++ b/tests/test_ec2/test_tags.py @@ -0,0 +1,20 @@ +import boto +from boto.ec2.instance import Reservation, InstanceAttribute +from sure import expect + +from moto import mock_ec2 + + + +@mock_ec2 +def test_instance_launch_and_terminate(): + conn = boto.connect_ec2('the_key', 'the_secret') + reservation = conn.run_instances('') + instance = reservation.instances[0] + + instance.add_tag("a key", "some value") + + tags = conn.get_all_tags() + tag = tags[0] + tag.name.should.equal("a key") + tag.value.should.equal("some value")