opsworks: impl start_instance, describe_instances
This commit is contained in:
parent
09ca1b6e0c
commit
1ce22068ea
@ -105,7 +105,6 @@ class BaseResponse(_TemplateEnvironmentMixin):
|
|||||||
# FIXME: At least in Flask==0.10.1, request.data is an empty string
|
# FIXME: At least in Flask==0.10.1, request.data is an empty string
|
||||||
# and the information we want is in request.form. Keeping self.body
|
# and the information we want is in request.form. Keeping self.body
|
||||||
# definition for back-compatibility
|
# definition for back-compatibility
|
||||||
#if request.headers.get("content-type") == "application/x-amz-json-1.1":
|
|
||||||
self.body = request.data
|
self.body = request.data
|
||||||
|
|
||||||
querystring = {}
|
querystring = {}
|
||||||
|
@ -49,6 +49,9 @@ class OpsworkInstance(object):
|
|||||||
|
|
||||||
# may not be totally accurate defaults; instance-type dependent
|
# may not be totally accurate defaults; instance-type dependent
|
||||||
self.root_device_type = root_device_type
|
self.root_device_type = root_device_type
|
||||||
|
# todo: refactor how we track block_device_mappings to use
|
||||||
|
# boto.ec2.blockdevicemapping.BlockDeviceType and standardize
|
||||||
|
# formatting in to_dict()
|
||||||
self.block_device_mappings = block_device_mappings
|
self.block_device_mappings = block_device_mappings
|
||||||
if self.block_device_mappings is None:
|
if self.block_device_mappings is None:
|
||||||
self.block_device_mappings = [{
|
self.block_device_mappings = [{
|
||||||
@ -83,7 +86,7 @@ class OpsworkInstance(object):
|
|||||||
start_instance. Update instance attributes to the newly created instance
|
start_instance. Update instance attributes to the newly created instance
|
||||||
attributes
|
attributes
|
||||||
"""
|
"""
|
||||||
if self.instances is None:
|
if self.instance is None:
|
||||||
reservation = self.ec2_backend.add_instances(
|
reservation = self.ec2_backend.add_instances(
|
||||||
image_id=self.ami_id,
|
image_id=self.ami_id,
|
||||||
count=1,
|
count=1,
|
||||||
@ -107,6 +110,7 @@ class OpsworkInstance(object):
|
|||||||
self.architecture = self.instance.architecture
|
self.architecture = self.instance.architecture
|
||||||
self.virtualization_type = self.instance.virtualization_type
|
self.virtualization_type = self.instance.virtualization_type
|
||||||
self.subnet_id = self.instance.subnet_id
|
self.subnet_id = self.instance.subnet_id
|
||||||
|
self.root_device_type = self.instance.root_device_type
|
||||||
|
|
||||||
self.ec2_backend.start_instances([self.instance.id])
|
self.ec2_backend.start_instances([self.instance.id])
|
||||||
|
|
||||||
@ -128,6 +132,7 @@ class OpsworkInstance(object):
|
|||||||
"Hostname": self.hostname,
|
"Hostname": self.hostname,
|
||||||
"InfrastructureClass": self.infrastructure_class,
|
"InfrastructureClass": self.infrastructure_class,
|
||||||
"InstallUpdatesOnBoot": self.install_updates_on_boot,
|
"InstallUpdatesOnBoot": self.install_updates_on_boot,
|
||||||
|
"InstanceProfileArn": self.instance_profile_arn,
|
||||||
"InstanceType": self.instance_type,
|
"InstanceType": self.instance_type,
|
||||||
"LayerIds": self.layer_ids,
|
"LayerIds": self.layer_ids,
|
||||||
"Os": self.os,
|
"Os": self.os,
|
||||||
@ -145,13 +150,16 @@ class OpsworkInstance(object):
|
|||||||
d.update({"AutoScaleType": self.auto_scale_type})
|
d.update({"AutoScaleType": self.auto_scale_type})
|
||||||
|
|
||||||
if self.instance is not None:
|
if self.instance is not None:
|
||||||
del d['AmiId']
|
|
||||||
d.update({"Ec2InstanceId": self.instance.id})
|
d.update({"Ec2InstanceId": self.instance.id})
|
||||||
d.update({"ReportedAgentVersion": "2425-20160406102508 (fixed)"})
|
d.update({"ReportedAgentVersion": "2425-20160406102508 (fixed)"})
|
||||||
d.update({"RootDeviceVolumeId": "vol-a20e450a (fixed)"})
|
d.update({"RootDeviceVolumeId": "vol-a20e450a (fixed)"})
|
||||||
if self.ssh_keyname is not None:
|
if self.ssh_keyname is not None:
|
||||||
d.update({"SshHostDsaKeyFingerprint": "24:36:32:fe:d8:5f:9c:18:b1:ad:37:e9:eb:e8:69:58 (fixed)"})
|
d.update({"SshHostDsaKeyFingerprint": "24:36:32:fe:d8:5f:9c:18:b1:ad:37:e9:eb:e8:69:58 (fixed)"})
|
||||||
d.update({"SshHostRsaKeyFingerprint": "3c:bd:37:52:d7:ca:67:e1:6e:4b:ac:31:86:79:f5:6c (fixed)"})
|
d.update({"SshHostRsaKeyFingerprint": "3c:bd:37:52:d7:ca:67:e1:6e:4b:ac:31:86:79:f5:6c (fixed)"})
|
||||||
|
d.update({"PrivateDns": self.instance.private_dns})
|
||||||
|
d.update({"PrivateIp": self.instance.private_ip})
|
||||||
|
d.update({"PublicDns": getattr(self.instance, 'public_dns', None)})
|
||||||
|
d.update({"PublicIp": getattr(self.instance, 'public_ip', None)})
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
@ -343,7 +351,7 @@ class Stack(object):
|
|||||||
# this doesn't match amazon's implementation
|
# this doesn't match amazon's implementation
|
||||||
return "{theme}-{rand}-(moto)".format(
|
return "{theme}-{rand}-(moto)".format(
|
||||||
theme=self.hostname_theme,
|
theme=self.hostname_theme,
|
||||||
rand=[choice("abcdefghijhk") for _ in xrange(4)])
|
rand=[choice("abcdefghijhk") for _ in range(4)])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def arn(self):
|
def arn(self):
|
||||||
@ -432,14 +440,16 @@ class OpsWorksBackend(BaseBackend):
|
|||||||
if unknown_layers:
|
if unknown_layers:
|
||||||
raise ResourceNotFoundException(", ".join(unknown_layers))
|
raise ResourceNotFoundException(", ".join(unknown_layers))
|
||||||
|
|
||||||
if any([layer.stack_id != stack_id for layer in self.layers.values()]):
|
layers = [self.layers[id] for id in layer_ids]
|
||||||
|
if len(set([layer.stack_id for layer in layers])) != 1 or \
|
||||||
|
any([layer.stack_id != stack_id for layer in layers]):
|
||||||
raise ValidationException(
|
raise ValidationException(
|
||||||
"Please only provide layer IDs from the same stack")
|
"Please only provide layer IDs from the same stack")
|
||||||
|
|
||||||
stack = self.stacks[stack_id]
|
stack = self.stacks[stack_id]
|
||||||
# pick the first to set default instance_profile_arn and
|
# pick the first to set default instance_profile_arn and
|
||||||
# security_group_ids on the instance.
|
# security_group_ids on the instance.
|
||||||
layer = self.layers[layer_ids[0]]
|
layer = layers[0]
|
||||||
|
|
||||||
kwargs.setdefault("hostname", stack.generate_hostname())
|
kwargs.setdefault("hostname", stack.generate_hostname())
|
||||||
kwargs.setdefault("ssh_keyname", stack.default_ssh_keyname)
|
kwargs.setdefault("ssh_keyname", stack.default_ssh_keyname)
|
||||||
@ -482,6 +492,37 @@ class OpsWorksBackend(BaseBackend):
|
|||||||
raise ResourceNotFoundException(", ".join(unknown_layers))
|
raise ResourceNotFoundException(", ".join(unknown_layers))
|
||||||
return [self.layers[id].to_dict() for id in layer_ids]
|
return [self.layers[id].to_dict() for id in layer_ids]
|
||||||
|
|
||||||
|
def describe_instances(self, instance_ids, layer_id, stack_id):
|
||||||
|
if len(list(filter(None, (instance_ids, layer_id, stack_id)))) != 1:
|
||||||
|
raise ValidationException("Please provide either one or more "
|
||||||
|
"instance IDs or one stack ID or one "
|
||||||
|
"layer ID")
|
||||||
|
if instance_ids:
|
||||||
|
unknown_instances = set(instance_ids) - set(self.instances.keys())
|
||||||
|
if unknown_instances:
|
||||||
|
raise ResourceNotFoundException(", ".join(unknown_instances))
|
||||||
|
return [self.instances[id].to_dict() for id in instance_ids]
|
||||||
|
|
||||||
|
if layer_id:
|
||||||
|
if layer_id not in self.layers:
|
||||||
|
raise ResourceNotFoundException(
|
||||||
|
"Unable to find layer with ID {}".format(layer_id))
|
||||||
|
instances = [i.to_dict() for i in self.instances.values() if layer_id in i.layer_ids]
|
||||||
|
return instances
|
||||||
|
|
||||||
|
if stack_id:
|
||||||
|
if stack_id not in self.stacks:
|
||||||
|
raise ResourceNotFoundException(
|
||||||
|
"Unable to find stack with ID {}".format(stack_id))
|
||||||
|
instances = [i.to_dict() for i in self.instances.values() if stack_id==i.stack_id]
|
||||||
|
return instances
|
||||||
|
|
||||||
|
def start_instance(self, instance_id):
|
||||||
|
if instance_id not in self.instances:
|
||||||
|
raise ResourceNotFoundException(
|
||||||
|
"Unable to find instance with ID {}".format(instance_id))
|
||||||
|
self.instances[instance_id].start()
|
||||||
|
|
||||||
|
|
||||||
opsworks_backends = {}
|
opsworks_backends = {}
|
||||||
for region, ec2_backend in ec2_backends.items():
|
for region, ec2_backend in ec2_backends.items():
|
||||||
|
@ -96,5 +96,17 @@ class OpsWorksResponse(BaseResponse):
|
|||||||
stack_id = self.parameters.get("StackId")
|
stack_id = self.parameters.get("StackId")
|
||||||
layer_ids = self.parameters.get("LayerIds")
|
layer_ids = self.parameters.get("LayerIds")
|
||||||
layers = self.opsworks_backend.describe_layers(stack_id, layer_ids)
|
layers = self.opsworks_backend.describe_layers(stack_id, layer_ids)
|
||||||
return json.dumps({"Layers": layers})
|
return json.dumps({"Layers": layers}, indent=1)
|
||||||
|
|
||||||
|
def describe_instances(self):
|
||||||
|
instance_ids = self.parameters.get("InstanceIds")
|
||||||
|
layer_id = self.parameters.get("LayerId")
|
||||||
|
stack_id = self.parameters.get("StackId")
|
||||||
|
instances = self.opsworks_backend.describe_instances(
|
||||||
|
instance_ids, layer_id, stack_id)
|
||||||
|
return json.dumps({"Instances": instances}, indent=1)
|
||||||
|
|
||||||
|
def start_instance(self):
|
||||||
|
instance_id = self.parameters.get("InstanceId")
|
||||||
|
self.opsworks_backend.start_instance(instance_id)
|
||||||
|
return ""
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import boto3
|
import boto3
|
||||||
import sure # noqa
|
import sure # noqa
|
||||||
import re
|
|
||||||
|
|
||||||
from moto import mock_opsworks
|
from moto import mock_opsworks
|
||||||
|
from moto import mock_ec2
|
||||||
|
|
||||||
|
|
||||||
@mock_opsworks
|
@mock_opsworks
|
||||||
def test_create_instance_response():
|
def test_create_instance():
|
||||||
client = boto3.client('opsworks')
|
client = boto3.client('opsworks')
|
||||||
stack_id = client.create_stack(
|
stack_id = client.create_stack(
|
||||||
Name="test_stack_1",
|
Name="test_stack_1",
|
||||||
@ -28,3 +29,148 @@ def test_create_instance_response():
|
|||||||
|
|
||||||
response.should.contain("InstanceId")
|
response.should.contain("InstanceId")
|
||||||
|
|
||||||
|
client.create_instance.when.called_with(
|
||||||
|
StackId="nothere", LayerIds=[layer_id], InstanceType="t2.micro"
|
||||||
|
).should.throw(Exception, "Unable to find stack with ID nothere")
|
||||||
|
|
||||||
|
client.create_instance.when.called_with(
|
||||||
|
StackId=stack_id, LayerIds=["nothere"], InstanceType="t2.micro"
|
||||||
|
).should.throw(Exception, "nothere")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_opsworks
|
||||||
|
def test_describe_instances():
|
||||||
|
"""
|
||||||
|
create two stacks, with 1 layer and 2 layers (S1L1, S2L1, S2L2)
|
||||||
|
|
||||||
|
populate S1L1 with 2 instances (S1L1_i1, S1L1_i2)
|
||||||
|
populate S2L1 with 1 instance (S2L1_i1)
|
||||||
|
populate S2L2 with 3 instances (S2L2_i1..2)
|
||||||
|
"""
|
||||||
|
|
||||||
|
client = boto3.client('opsworks')
|
||||||
|
S1 = client.create_stack(
|
||||||
|
Name="S1",
|
||||||
|
Region="us-east-1",
|
||||||
|
ServiceRoleArn="service_arn",
|
||||||
|
DefaultInstanceProfileArn="profile_arn"
|
||||||
|
)['StackId']
|
||||||
|
S1L1 = client.create_layer(
|
||||||
|
StackId=S1,
|
||||||
|
Type="custom",
|
||||||
|
Name="S1L1",
|
||||||
|
Shortname="S1L1"
|
||||||
|
)['LayerId']
|
||||||
|
S2 = client.create_stack(
|
||||||
|
Name="S2",
|
||||||
|
Region="us-east-1",
|
||||||
|
ServiceRoleArn="service_arn",
|
||||||
|
DefaultInstanceProfileArn="profile_arn"
|
||||||
|
)['StackId']
|
||||||
|
S2L1 = client.create_layer(
|
||||||
|
StackId=S2,
|
||||||
|
Type="custom",
|
||||||
|
Name="S2L1",
|
||||||
|
Shortname="S2L1"
|
||||||
|
)['LayerId']
|
||||||
|
S2L2 = client.create_layer(
|
||||||
|
StackId=S2,
|
||||||
|
Type="custom",
|
||||||
|
Name="S2L2",
|
||||||
|
Shortname="S2L2"
|
||||||
|
)['LayerId']
|
||||||
|
|
||||||
|
S1L1_i1 = client.create_instance(
|
||||||
|
StackId=S1, LayerIds=[S1L1], InstanceType="t2.micro"
|
||||||
|
)['InstanceId']
|
||||||
|
S1L1_i2 = client.create_instance(
|
||||||
|
StackId=S1, LayerIds=[S1L1], InstanceType="t2.micro"
|
||||||
|
)['InstanceId']
|
||||||
|
S2L1_i1 = client.create_instance(
|
||||||
|
StackId=S2, LayerIds=[S2L1], InstanceType="t2.micro"
|
||||||
|
)['InstanceId']
|
||||||
|
S2L2_i1 = client.create_instance(
|
||||||
|
StackId=S2, LayerIds=[S2L2], InstanceType="t2.micro"
|
||||||
|
)['InstanceId']
|
||||||
|
S2L2_i2 = client.create_instance(
|
||||||
|
StackId=S2, LayerIds=[S2L2], InstanceType="t2.micro"
|
||||||
|
)['InstanceId']
|
||||||
|
|
||||||
|
# instances in Stack 1
|
||||||
|
response = client.describe_instances(StackId=S1)['Instances']
|
||||||
|
response.should.have.length_of(2)
|
||||||
|
S1L1_i1.should.be.within([i["InstanceId"] for i in response])
|
||||||
|
S1L1_i2.should.be.within([i["InstanceId"] for i in response])
|
||||||
|
|
||||||
|
response2 = client.describe_instances(InstanceIds=[S1L1_i1, S1L1_i2])['Instances']
|
||||||
|
sorted(response2, key=lambda d: d['InstanceId']).should.equal(
|
||||||
|
sorted(response, key=lambda d: d['InstanceId']))
|
||||||
|
|
||||||
|
response3 = client.describe_instances(LayerId=S1L1)['Instances']
|
||||||
|
sorted(response3, key=lambda d: d['InstanceId']).should.equal(
|
||||||
|
sorted(response, key=lambda d: d['InstanceId']))
|
||||||
|
|
||||||
|
response = client.describe_instances(StackId=S1)['Instances']
|
||||||
|
response.should.have.length_of(2)
|
||||||
|
S1L1_i1.should.be.within([i["InstanceId"] for i in response])
|
||||||
|
S1L1_i2.should.be.within([i["InstanceId"] for i in response])
|
||||||
|
|
||||||
|
# instances in Stack 2
|
||||||
|
response = client.describe_instances(StackId=S2)['Instances']
|
||||||
|
response.should.have.length_of(3)
|
||||||
|
S2L1_i1.should.be.within([i["InstanceId"] for i in response])
|
||||||
|
S2L2_i1.should.be.within([i["InstanceId"] for i in response])
|
||||||
|
S2L2_i2.should.be.within([i["InstanceId"] for i in response])
|
||||||
|
|
||||||
|
response = client.describe_instances(LayerId=S2L1)['Instances']
|
||||||
|
response.should.have.length_of(1)
|
||||||
|
S2L1_i1.should.be.within([i["InstanceId"] for i in response])
|
||||||
|
|
||||||
|
response = client.describe_instances(LayerId=S2L2)['Instances']
|
||||||
|
response.should.have.length_of(2)
|
||||||
|
S2L1_i1.should_not.be.within([i["InstanceId"] for i in response])
|
||||||
|
|
||||||
|
|
||||||
|
@mock_opsworks
|
||||||
|
@mock_ec2
|
||||||
|
def test_ec2_integration():
|
||||||
|
"""
|
||||||
|
instances created via OpsWorks should be discoverable via ec2
|
||||||
|
"""
|
||||||
|
|
||||||
|
opsworks = boto3.client('opsworks')
|
||||||
|
stack_id = opsworks.create_stack(
|
||||||
|
Name="S1",
|
||||||
|
Region="us-east-1",
|
||||||
|
ServiceRoleArn="service_arn",
|
||||||
|
DefaultInstanceProfileArn="profile_arn"
|
||||||
|
)['StackId']
|
||||||
|
|
||||||
|
layer_id = opsworks.create_layer(
|
||||||
|
StackId=stack_id,
|
||||||
|
Type="custom",
|
||||||
|
Name="S1L1",
|
||||||
|
Shortname="S1L1"
|
||||||
|
)['LayerId']
|
||||||
|
|
||||||
|
instance_id = opsworks.create_instance(
|
||||||
|
StackId=stack_id, LayerIds=[layer_id], InstanceType="t2.micro"
|
||||||
|
)['InstanceId']
|
||||||
|
|
||||||
|
ec2 = boto3.client('ec2')
|
||||||
|
|
||||||
|
# Before starting the instance, it shouldn't be discoverable via ec2
|
||||||
|
reservations = ec2.describe_instances()['Reservations']
|
||||||
|
assert reservations.should.be.empty
|
||||||
|
|
||||||
|
# After starting the instance, it should be discoverable via ec2
|
||||||
|
opsworks.start_instance(InstanceId=instance_id)
|
||||||
|
reservations = ec2.describe_instances()['Reservations']
|
||||||
|
reservations[0]['Instances'].should.have.length_of(1)
|
||||||
|
instance = reservations[0]['Instances'][0]
|
||||||
|
opsworks_instance = opsworks.describe_instances(StackId=stack_id)['Instances'][0]
|
||||||
|
|
||||||
|
instance['InstanceId'].should.equal(opsworks_instance['Ec2InstanceId'])
|
||||||
|
instance['PrivateIpAddress'].should.equal(opsworks_instance['PrivateIp'])
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ def test_create_stack_response():
|
|||||||
@mock_opsworks
|
@mock_opsworks
|
||||||
def test_describe_stacks():
|
def test_describe_stacks():
|
||||||
client = boto3.client('opsworks')
|
client = boto3.client('opsworks')
|
||||||
for i in xrange(1, 4):
|
for i in range(1, 4):
|
||||||
client.create_stack(
|
client.create_stack(
|
||||||
Name="test_stack_{}".format(i),
|
Name="test_stack_{}".format(i),
|
||||||
Region="us-east-1",
|
Region="us-east-1",
|
||||||
|
Loading…
Reference in New Issue
Block a user