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")