commit
2e10f46e96
@ -19,6 +19,7 @@ from .utils import (
|
|||||||
random_eip_association_id,
|
random_eip_association_id,
|
||||||
random_eip_allocation_id,
|
random_eip_allocation_id,
|
||||||
random_ip,
|
random_ip,
|
||||||
|
random_key_pair,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -161,6 +162,33 @@ class InstanceBackend(object):
|
|||||||
return [reservation for reservation in self.reservations.values()]
|
return [reservation for reservation in self.reservations.values()]
|
||||||
|
|
||||||
|
|
||||||
|
class KeyPairBackend(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.keypairs = defaultdict(dict)
|
||||||
|
super(KeyPairBackend, self).__init__()
|
||||||
|
|
||||||
|
def create_key_pair(self, name):
|
||||||
|
if name in self.keypairs:
|
||||||
|
raise InvalidIdError(name)
|
||||||
|
self.keypairs[name] = keypair = random_key_pair()
|
||||||
|
keypair['name'] = name
|
||||||
|
return keypair
|
||||||
|
|
||||||
|
def delete_key_pair(self, name):
|
||||||
|
if name in self.keypairs:
|
||||||
|
self.keypairs.pop(name)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def describe_key_pairs(self, filter_names=None):
|
||||||
|
results = []
|
||||||
|
for name, keypair in self.keypairs.iteritems():
|
||||||
|
if not filter_names or name in filter_names:
|
||||||
|
keypair['name'] = name
|
||||||
|
results.append(keypair)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
class TagBackend(object):
|
class TagBackend(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -675,7 +703,8 @@ class ElasticAddressBackend(object):
|
|||||||
|
|
||||||
class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
|
class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
|
||||||
RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend,
|
RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend,
|
||||||
VPCBackend, SubnetBackend, SpotRequestBackend, ElasticAddressBackend):
|
VPCBackend, SubnetBackend, SpotRequestBackend, ElasticAddressBackend,
|
||||||
|
KeyPairBackend):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,15 +1,81 @@
|
|||||||
|
from jinja2 import Template
|
||||||
from moto.core.responses import BaseResponse
|
from moto.core.responses import BaseResponse
|
||||||
|
from moto.ec2.models import ec2_backend
|
||||||
|
from moto.ec2.exceptions import InvalidIdError
|
||||||
|
from moto.ec2.utils import keypair_names_from_querystring, filters_from_querystring
|
||||||
|
|
||||||
|
|
||||||
class KeyPairs(BaseResponse):
|
class KeyPairs(BaseResponse):
|
||||||
|
|
||||||
def create_key_pair(self):
|
def create_key_pair(self):
|
||||||
raise NotImplementedError('KeyPairs.create_key_pair is not yet implemented')
|
try:
|
||||||
|
name = self.querystring.get('KeyName')[0]
|
||||||
|
keypair = ec2_backend.create_key_pair(name)
|
||||||
|
except InvalidIdError as exc:
|
||||||
|
template = Template(CREATE_KEY_PAIR_INVALID_NAME)
|
||||||
|
return template.render(keypair_id=exc.id), dict(status=400)
|
||||||
|
else:
|
||||||
|
template = Template(CREATE_KEY_PAIR_RESPONSE)
|
||||||
|
return template.render(**keypair)
|
||||||
|
|
||||||
def delete_key_pair(self):
|
def delete_key_pair(self):
|
||||||
raise NotImplementedError('KeyPairs.delete_key_pair is not yet implemented')
|
name = self.querystring.get('KeyName')[0]
|
||||||
|
success = str(ec2_backend.delete_key_pair(name)).lower()
|
||||||
|
return Template(DELETE_KEY_PAIR_RESPONSE).render(success=success)
|
||||||
|
|
||||||
def describe_key_pairs(self):
|
def describe_key_pairs(self):
|
||||||
raise NotImplementedError('KeyPairs.describe_key_pairs is not yet implemented')
|
names = keypair_names_from_querystring(self.querystring)
|
||||||
|
filters = filters_from_querystring(self.querystring)
|
||||||
|
if len(filters) > 0:
|
||||||
|
raise NotImplementedError('Using filters in KeyPairs.describe_key_pairs is not yet implemented')
|
||||||
|
|
||||||
|
try:
|
||||||
|
keypairs = ec2_backend.describe_key_pairs(names)
|
||||||
|
except InvalidIdError as exc:
|
||||||
|
template = Template(CREATE_KEY_PAIR_NOT_FOUND)
|
||||||
|
return template.render(keypair_id=exc.id), dict(status=400)
|
||||||
|
else:
|
||||||
|
template = Template(DESCRIBE_KEY_PAIRS_RESPONSE)
|
||||||
|
return template.render(keypairs=keypairs)
|
||||||
|
|
||||||
def import_key_pair(self):
|
def import_key_pair(self):
|
||||||
raise NotImplementedError('KeyPairs.import_key_pair is not yet implemented')
|
raise NotImplementedError('KeyPairs.import_key_pair is not yet implemented')
|
||||||
|
|
||||||
|
|
||||||
|
DESCRIBE_KEY_PAIRS_RESPONSE = """<DescribeKeyPairsResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
|
||||||
|
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||||
|
<keySet>
|
||||||
|
{% for keypair in keypairs %}
|
||||||
|
<item>
|
||||||
|
<keyName>{{ keypair.name }}</keyName>
|
||||||
|
<keyFingerprint>{{ keypair.fingerprint }}</keyFingerprint>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</keySet>
|
||||||
|
</DescribeKeyPairsResponse>"""
|
||||||
|
|
||||||
|
|
||||||
|
CREATE_KEY_PAIR_RESPONSE = """<CreateKeyPairResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
|
||||||
|
<keyName>{{ name }}</keyName>
|
||||||
|
<keyFingerprint>
|
||||||
|
{{ fingerprint }}
|
||||||
|
</keyFingerprint>
|
||||||
|
<keyMaterial>{{ material }}
|
||||||
|
</keyMaterial>
|
||||||
|
</CreateKeyPairResponse>"""
|
||||||
|
|
||||||
|
|
||||||
|
CREATE_KEY_PAIR_INVALID_NAME = """<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Response><Errors><Error><Code>InvalidKeyPair.Duplicate</Code><Message>The keypair '{{ keypair_id }}' already exists.</Message></Error></Errors><RequestID>f4f76e81-8ca5-4e61-a6d5-a4a96EXAMPLE</RequestID></Response>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
CREATE_KEY_PAIR_NOT_FOUND = """<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Response><Errors><Error><Code>InvalidKeyPair.NotFound</Code><Message>The keypair '{{ keypair_id }}' does not exist.</Message></Error></Errors><RequestID>f4f76e81-8ca5-4e61-a6d5-a4a96EXAMPLE</RequestID></Response>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
DELETE_KEY_PAIR_RESPONSE = """<DeleteKeyPairResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
|
||||||
|
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||||
|
<return>{{ success }}</return>
|
||||||
|
</DeleteKeyPairResponse>"""
|
||||||
|
@ -116,6 +116,15 @@ def filters_from_querystring(querystring_dict):
|
|||||||
return response_values
|
return response_values
|
||||||
|
|
||||||
|
|
||||||
|
def keypair_names_from_querystring(querystring_dict):
|
||||||
|
keypair_names = []
|
||||||
|
for key, value in querystring_dict.iteritems():
|
||||||
|
if 'KeyName' in key:
|
||||||
|
keypair_names.append(value[0])
|
||||||
|
return keypair_names
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
filter_dict_attribute_mapping = {
|
filter_dict_attribute_mapping = {
|
||||||
'instance-state-name': 'state'
|
'instance-state-name': 'state'
|
||||||
}
|
}
|
||||||
@ -144,3 +153,27 @@ def filter_reservations(reservations, filter_dict):
|
|||||||
reservation.instances = new_instances
|
reservation.instances = new_instances
|
||||||
result.append(reservation)
|
result.append(reservation)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
# not really random
|
||||||
|
def random_key_pair():
|
||||||
|
return {
|
||||||
|
'fingerprint': ('1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:'
|
||||||
|
'7d:b8:ca:9f:f5:f1:6f'),
|
||||||
|
'material': """---- BEGIN RSA PRIVATE KEY ----
|
||||||
|
MIICiTCCAfICCQD6m7oRw0uXOjANBgkqhkiG9w0BAQUFADCBiDELMAkGA1UEBhMC
|
||||||
|
VVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6
|
||||||
|
b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAd
|
||||||
|
BgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wHhcNMTEwNDI1MjA0NTIxWhcN
|
||||||
|
MTIwNDI0MjA0NTIxWjCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYD
|
||||||
|
VQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25z
|
||||||
|
b2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFt
|
||||||
|
YXpvbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMaK0dn+a4GmWIWJ
|
||||||
|
21uUSfwfEvySWtC2XADZ4nB+BLYgVIk60CpiwsZ3G93vUEIO3IyNoH/f0wYK8m9T
|
||||||
|
rDHudUZg3qX4waLG5M43q7Wgc/MbQITxOUSQv7c7ugFFDzQGBzZswY6786m86gpE
|
||||||
|
Ibb3OhjZnzcvQAaRHhdlQWIMm2nrAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAtCu4
|
||||||
|
nUhVVxYUntneD9+h8Mg9q6q+auNKyExzyLwaxlAoo7TJHidbtS4J5iNmZgXL0Fkb
|
||||||
|
FFBjvSfpJIlJ00zbhNYS5f6GuoEDmFJl0ZxBHjJnyp378OD8uTs7fLvjx79LjSTb
|
||||||
|
NYiytVbZPQUQ5Yaxu2jXnimvw3rrszlaEXAMPLE
|
||||||
|
-----END RSA PRIVATE KEY-----"""
|
||||||
|
}
|
||||||
|
@ -1,9 +1,65 @@
|
|||||||
import boto
|
import boto
|
||||||
import sure # noqa
|
import sure # noqa
|
||||||
|
|
||||||
|
from boto.exception import EC2ResponseError
|
||||||
from moto import mock_ec2
|
from moto import mock_ec2
|
||||||
|
|
||||||
|
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
def test_key_pairs():
|
def test_key_pairs_empty():
|
||||||
pass
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
assert len(conn.get_all_key_pairs()) == 0
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_key_pairs_create():
|
||||||
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
kp = conn.create_key_pair('foo')
|
||||||
|
assert kp.material.startswith('---- BEGIN RSA PRIVATE KEY ----')
|
||||||
|
kps = conn.get_all_key_pairs()
|
||||||
|
assert len(kps) == 1
|
||||||
|
assert kps[0].name == 'foo'
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_key_pairs_create_two():
|
||||||
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
kp = conn.create_key_pair('foo')
|
||||||
|
kp = conn.create_key_pair('bar')
|
||||||
|
assert kp.material.startswith('---- BEGIN RSA PRIVATE KEY ----')
|
||||||
|
kps = conn.get_all_key_pairs()
|
||||||
|
assert len(kps) == 2
|
||||||
|
assert kps[0].name == 'foo'
|
||||||
|
assert kps[1].name == 'bar'
|
||||||
|
kps = conn.get_all_key_pairs('foo')
|
||||||
|
assert len(kps) == 1
|
||||||
|
assert kps[0].name == 'foo'
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_key_pairs_create_exist():
|
||||||
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
kp = conn.create_key_pair('foo')
|
||||||
|
assert kp.material.startswith('---- BEGIN RSA PRIVATE KEY ----')
|
||||||
|
assert len(conn.get_all_key_pairs()) == 1
|
||||||
|
conn.create_key_pair.when.called_with('foo').should.throw(
|
||||||
|
EC2ResponseError,
|
||||||
|
"The keypair 'foo' already exists."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_key_pairs_delete_no_exist():
|
||||||
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
assert len(conn.get_all_key_pairs()) == 0
|
||||||
|
r = conn.delete_key_pair('foo')
|
||||||
|
r.should.be.ok
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_key_pairs_delete_exist():
|
||||||
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
conn.create_key_pair('foo')
|
||||||
|
r = conn.delete_key_pair('foo')
|
||||||
|
r.should.be.ok
|
||||||
|
assert len(conn.get_all_key_pairs()) == 0
|
||||||
|
Loading…
Reference in New Issue
Block a user