opsworks: impl create_instance
This commit is contained in:
parent
2fe5b77861
commit
09ca1b6e0c
@ -1,13 +1,160 @@
|
||||
from __future__ import unicode_literals
|
||||
from moto.core import BaseBackend
|
||||
from moto.ec2 import ec2_backends
|
||||
from moto.elb import elb_backends
|
||||
import uuid
|
||||
import datetime
|
||||
from random import choice
|
||||
|
||||
from .exceptions import ResourceNotFoundException, ValidationException
|
||||
|
||||
|
||||
class OpsworkInstance(object):
|
||||
"""
|
||||
opsworks maintains its own set of ec2 instance metadata.
|
||||
This metadata exists before any instance reservations are made, and is
|
||||
used to populate a reservation request when "start" is called
|
||||
"""
|
||||
def __init__(self, stack_id, layer_ids, instance_type, ec2_backend,
|
||||
auto_scale_type=None,
|
||||
hostname=None,
|
||||
os=None,
|
||||
ami_id="ami-08111162",
|
||||
ssh_keyname=None,
|
||||
availability_zone=None,
|
||||
virtualization_type="hvm",
|
||||
subnet_id=None,
|
||||
architecture="x86_64",
|
||||
root_device_type="ebs",
|
||||
block_device_mappings=None,
|
||||
install_updates_on_boot=True,
|
||||
ebs_optimized=False,
|
||||
agent_version="INHERIT",
|
||||
instance_profile_arn=None,
|
||||
associate_public_ip=None,
|
||||
security_group_ids=None):
|
||||
|
||||
self.ec2_backend = ec2_backend
|
||||
|
||||
self.instance_profile_arn = instance_profile_arn
|
||||
self.agent_version = agent_version
|
||||
self.ebs_optimized = ebs_optimized
|
||||
self.install_updates_on_boot = install_updates_on_boot
|
||||
self.architecture = architecture
|
||||
self.virtualization_type = virtualization_type
|
||||
self.ami_id = ami_id
|
||||
self.auto_scale_type = auto_scale_type
|
||||
self.instance_type = instance_type
|
||||
self.layer_ids = layer_ids
|
||||
self.stack_id = stack_id
|
||||
|
||||
# may not be totally accurate defaults; instance-type dependent
|
||||
self.root_device_type = root_device_type
|
||||
self.block_device_mappings = block_device_mappings
|
||||
if self.block_device_mappings is None:
|
||||
self.block_device_mappings = [{
|
||||
'DeviceName': 'ROOT_DEVICE',
|
||||
'Ebs': {
|
||||
'VolumeSize': 8,
|
||||
'VolumeType': 'gp2'
|
||||
}
|
||||
}]
|
||||
self.security_group_ids = security_group_ids
|
||||
if self.security_group_ids is None:
|
||||
self.security_group_ids = []
|
||||
|
||||
self.os = os
|
||||
self.hostname = hostname
|
||||
self.ssh_keyname = ssh_keyname
|
||||
self.availability_zone = availability_zone
|
||||
self.subnet_id = subnet_id
|
||||
self.associate_public_ip = associate_public_ip
|
||||
|
||||
self.instance = None
|
||||
self.reported_os = {}
|
||||
self.infrastructure_class = "ec2 (fixed)"
|
||||
self.platform = "linux (fixed)"
|
||||
|
||||
self.id = "{}".format(uuid.uuid4())
|
||||
self.created_at = datetime.datetime.utcnow()
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
create an ec2 reservation if one doesn't already exist and call
|
||||
start_instance. Update instance attributes to the newly created instance
|
||||
attributes
|
||||
"""
|
||||
if self.instances is None:
|
||||
reservation = self.ec2_backend.add_instances(
|
||||
image_id=self.ami_id,
|
||||
count=1,
|
||||
user_data="",
|
||||
security_group_names=[],
|
||||
security_group_ids=self.security_group_ids,
|
||||
instance_type=self.instance_type,
|
||||
key_name=self.ssh_keyname,
|
||||
ebs_optimized=self.ebs_optimized,
|
||||
subnet_id=self.subnet_id,
|
||||
associate_public_ip=self.associate_public_ip,
|
||||
)
|
||||
self.instance = reservation.instances[0]
|
||||
self.reported_os = {
|
||||
'Family': 'rhel (fixed)',
|
||||
'Name': 'amazon (fixed)',
|
||||
'Version': '2016.03 (fixed)'
|
||||
}
|
||||
self.platform = self.instance.platform
|
||||
self.security_group_ids = self.instance.security_groups
|
||||
self.architecture = self.instance.architecture
|
||||
self.virtualization_type = self.instance.virtualization_type
|
||||
self.subnet_id = self.instance.subnet_id
|
||||
|
||||
self.ec2_backend.start_instances([self.instance.id])
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
if self.instance is None:
|
||||
return "stopped"
|
||||
return self.instance._state.name
|
||||
|
||||
def to_dict(self):
|
||||
d = {
|
||||
"AgentVersion": self.agent_version,
|
||||
"Architecture": self.architecture,
|
||||
"AvailabilityZone": self.availability_zone,
|
||||
"BlockDeviceMappings": self.block_device_mappings,
|
||||
"CreatedAt": self.created_at.isoformat(),
|
||||
"EbsOptimized": self.ebs_optimized,
|
||||
"InstanceId": self.id,
|
||||
"Hostname": self.hostname,
|
||||
"InfrastructureClass": self.infrastructure_class,
|
||||
"InstallUpdatesOnBoot": self.install_updates_on_boot,
|
||||
"InstanceType": self.instance_type,
|
||||
"LayerIds": self.layer_ids,
|
||||
"Os": self.os,
|
||||
"Platform": self.platform,
|
||||
"ReportedOs": self.reported_os,
|
||||
"RootDeviceType": self.root_device_type,
|
||||
"SecurityGroupIds": self.security_group_ids,
|
||||
"AmiId": self.ami_id,
|
||||
"Status": self.status,
|
||||
}
|
||||
if self.ssh_keyname is not None:
|
||||
d.update({"SshKeyName": self.ssh_keyname})
|
||||
|
||||
if self.auto_scale_type is not None:
|
||||
d.update({"AutoScaleType": self.auto_scale_type})
|
||||
|
||||
if self.instance is not None:
|
||||
del d['AmiId']
|
||||
d.update({"Ec2InstanceId": self.instance.id})
|
||||
d.update({"ReportedAgentVersion": "2425-20160406102508 (fixed)"})
|
||||
d.update({"RootDeviceVolumeId": "vol-a20e450a (fixed)"})
|
||||
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({"SshHostRsaKeyFingerprint": "3c:bd:37:52:d7:ca:67:e1:6e:4b:ac:31:86:79:f5:6c (fixed)"})
|
||||
return d
|
||||
|
||||
|
||||
class Layer(object):
|
||||
def __init__(self, stack_id, type, name, shortname,
|
||||
attributes=None,
|
||||
@ -94,7 +241,6 @@ class Layer(object):
|
||||
self.install_updates_on_boot = install_updates_on_boot
|
||||
self.use_ebs_optimized_instances = use_ebs_optimized_instances
|
||||
|
||||
self.instances = []
|
||||
self.id = "{}".format(uuid.uuid4())
|
||||
self.created_at = datetime.datetime.utcnow()
|
||||
|
||||
@ -135,7 +281,7 @@ class Layer(object):
|
||||
|
||||
class Stack(object):
|
||||
def __init__(self, name, region, service_role_arn, default_instance_profile_arn,
|
||||
vpcid='vpc-1f99bf7c',
|
||||
vpcid="vpc-1f99bf7a",
|
||||
attributes=None,
|
||||
default_os='Ubuntu 12.04 LTS',
|
||||
hostname_theme='Layer_Dependent',
|
||||
@ -193,6 +339,12 @@ class Stack(object):
|
||||
def __eq__(self, other):
|
||||
return self.id == other.id
|
||||
|
||||
def generate_hostname(self):
|
||||
# this doesn't match amazon's implementation
|
||||
return "{theme}-{rand}-(moto)".format(
|
||||
theme=self.hostname_theme,
|
||||
rand=[choice("abcdefghijhk") for _ in xrange(4)])
|
||||
|
||||
@property
|
||||
def arn(self):
|
||||
return "arn:aws:opsworks:{region}:{account_number}:stack/{id}".format(
|
||||
@ -233,19 +385,16 @@ class Stack(object):
|
||||
|
||||
|
||||
class OpsWorksBackend(BaseBackend):
|
||||
def __init__(self, ec2_backend, elb_backend):
|
||||
def __init__(self, ec2_backend):
|
||||
self.stacks = {}
|
||||
self.layers = {}
|
||||
self.instances = {}
|
||||
self.policies = {}
|
||||
self.ec2_backend = ec2_backend
|
||||
self.elb_backend = elb_backend
|
||||
|
||||
def reset(self):
|
||||
ec2_backend = self.ec2_backend
|
||||
elb_backend = self.elb_backend
|
||||
self.__dict__ = {}
|
||||
self.__init__(ec2_backend, elb_backend)
|
||||
self.__init__(ec2_backend)
|
||||
|
||||
def create_stack(self, **kwargs):
|
||||
stack = Stack(**kwargs)
|
||||
@ -271,6 +420,43 @@ class OpsWorksBackend(BaseBackend):
|
||||
self.stacks[stackid].layers.append(layer)
|
||||
return layer
|
||||
|
||||
def create_instance(self, **kwargs):
|
||||
stack_id = kwargs['stack_id']
|
||||
layer_ids = kwargs['layer_ids']
|
||||
|
||||
if stack_id not in self.stacks:
|
||||
raise ResourceNotFoundException(
|
||||
"Unable to find stack with ID {}".format(stack_id))
|
||||
|
||||
unknown_layers = set(layer_ids) - set(self.layers.keys())
|
||||
if unknown_layers:
|
||||
raise ResourceNotFoundException(", ".join(unknown_layers))
|
||||
|
||||
if any([layer.stack_id != stack_id for layer in self.layers.values()]):
|
||||
raise ValidationException(
|
||||
"Please only provide layer IDs from the same stack")
|
||||
|
||||
stack = self.stacks[stack_id]
|
||||
# pick the first to set default instance_profile_arn and
|
||||
# security_group_ids on the instance.
|
||||
layer = self.layers[layer_ids[0]]
|
||||
|
||||
kwargs.setdefault("hostname", stack.generate_hostname())
|
||||
kwargs.setdefault("ssh_keyname", stack.default_ssh_keyname)
|
||||
kwargs.setdefault("availability_zone", stack.default_availability_zone)
|
||||
kwargs.setdefault("subnet_id", stack.default_subnet_id)
|
||||
kwargs.setdefault("root_device_type", stack.default_root_device_type)
|
||||
if layer.custom_instance_profile_arn:
|
||||
kwargs.setdefault("instance_profile_arn", layer.custom_instance_profile_arn)
|
||||
kwargs.setdefault("instance_profile_arn", stack.default_instance_profile_arn)
|
||||
kwargs.setdefault("security_group_ids", layer.custom_security_group_ids)
|
||||
kwargs.setdefault("associate_public_ip", layer.auto_assign_public_ips)
|
||||
kwargs.setdefault("ebs_optimized", layer.use_ebs_optimized_instances)
|
||||
kwargs.update({"ec2_backend": self.ec2_backend})
|
||||
opsworks_instance = OpsworkInstance(**kwargs)
|
||||
self.instances[opsworks_instance.id] = opsworks_instance
|
||||
return opsworks_instance
|
||||
|
||||
def describe_stacks(self, stack_ids):
|
||||
if stack_ids is None:
|
||||
return [stack.to_dict() for stack in self.stacks.values()]
|
||||
@ -297,7 +483,6 @@ class OpsWorksBackend(BaseBackend):
|
||||
return [self.layers[id].to_dict() for id in layer_ids]
|
||||
|
||||
|
||||
|
||||
opsworks_backends = {}
|
||||
for region, ec2_backend in ec2_backends.items():
|
||||
opsworks_backends[region] = OpsWorksBackend(ec2_backend, elb_backends[region])
|
||||
opsworks_backends[region] = OpsWorksBackend(ec2_backend)
|
||||
|
@ -64,6 +64,29 @@ class OpsWorksResponse(BaseResponse):
|
||||
layer = self.opsworks_backend.create_layer(**kwargs)
|
||||
return json.dumps({"LayerId": layer.id}, indent=1)
|
||||
|
||||
def create_instance(self):
|
||||
kwargs = dict(
|
||||
stack_id=self.parameters.get("StackId"),
|
||||
layer_ids=self.parameters.get("LayerIds"),
|
||||
instance_type=self.parameters.get("InstanceType"),
|
||||
auto_scale_type=self.parameters.get("AutoScalingType"),
|
||||
hostname=self.parameters.get("Hostname"),
|
||||
os=self.parameters.get("Os"),
|
||||
ami_id=self.parameters.get("AmiId"),
|
||||
ssh_keyname=self.parameters.get("SshKeyName"),
|
||||
availability_zone=self.parameters.get("AvailabilityZone"),
|
||||
virtualization_type=self.parameters.get("VirtualizationType"),
|
||||
subnet_id=self.parameters.get("SubnetId"),
|
||||
architecture=self.parameters.get("Architecture"),
|
||||
root_device_type=self.parameters.get("RootDeviceType"),
|
||||
block_device_mappings=self.parameters.get("BlockDeviceMappings"),
|
||||
install_updates_on_boot=self.parameters.get("InstallUpdatesOnBoot"),
|
||||
ebs_optimized=self.parameters.get("EbsOptimized"),
|
||||
agent_version=self.parameters.get("AgentVersion"),
|
||||
)
|
||||
opsworks_instance = self.opsworks_backend.create_instance(**kwargs)
|
||||
return json.dumps({"InstanceId": opsworks_instance.id}, indent=1)
|
||||
|
||||
def describe_stacks(self):
|
||||
stack_ids = self.parameters.get("StackIds")
|
||||
stacks = self.opsworks_backend.describe_stacks(stack_ids)
|
||||
@ -73,4 +96,5 @@ class OpsWorksResponse(BaseResponse):
|
||||
stack_id = self.parameters.get("StackId")
|
||||
layer_ids = self.parameters.get("LayerIds")
|
||||
layers = self.opsworks_backend.describe_layers(stack_id, layer_ids)
|
||||
return json.dumps({"Layers": layers}, indent=1)
|
||||
return json.dumps({"Layers": layers})
|
||||
|
||||
|
30
tests/test_opsworks/test_instances.py
Normal file
30
tests/test_opsworks/test_instances.py
Normal file
@ -0,0 +1,30 @@
|
||||
from __future__ import unicode_literals
|
||||
import boto3
|
||||
import sure # noqa
|
||||
import re
|
||||
|
||||
from moto import mock_opsworks
|
||||
|
||||
@mock_opsworks
|
||||
def test_create_instance_response():
|
||||
client = boto3.client('opsworks')
|
||||
stack_id = client.create_stack(
|
||||
Name="test_stack_1",
|
||||
Region="us-east-1",
|
||||
ServiceRoleArn="service_arn",
|
||||
DefaultInstanceProfileArn="profile_arn"
|
||||
)['StackId']
|
||||
|
||||
layer_id = client.create_layer(
|
||||
StackId=stack_id,
|
||||
Type="custom",
|
||||
Name="TestLayer",
|
||||
Shortname="TestLayerShortName"
|
||||
)['LayerId']
|
||||
|
||||
response = client.create_instance(
|
||||
StackId=stack_id, LayerIds=[layer_id], InstanceType="t2.micro"
|
||||
)
|
||||
|
||||
response.should.contain("InstanceId")
|
||||
|
Loading…
Reference in New Issue
Block a user