opsworks: implement create_stack
This commit is contained in:
parent
100ec4e7c8
commit
165bab0f97
@ -18,6 +18,7 @@ from .ecs import mock_ecs # flake8: noqa
|
|||||||
from .elb import mock_elb # flake8: noqa
|
from .elb import mock_elb # flake8: noqa
|
||||||
from .emr import mock_emr # flake8: noqa
|
from .emr import mock_emr # flake8: noqa
|
||||||
from .glacier import mock_glacier # flake8: noqa
|
from .glacier import mock_glacier # flake8: noqa
|
||||||
|
from .opsworks import mock_opsworks # flake8: noqa
|
||||||
from .iam import mock_iam # flake8: noqa
|
from .iam import mock_iam # flake8: noqa
|
||||||
from .kinesis import mock_kinesis # flake8: noqa
|
from .kinesis import mock_kinesis # flake8: noqa
|
||||||
from .kms import mock_kms # flake8: noqa
|
from .kms import mock_kms # flake8: noqa
|
||||||
|
@ -12,6 +12,7 @@ from moto.elb import elb_backend
|
|||||||
from moto.emr import emr_backend
|
from moto.emr import emr_backend
|
||||||
from moto.glacier import glacier_backend
|
from moto.glacier import glacier_backend
|
||||||
from moto.iam import iam_backend
|
from moto.iam import iam_backend
|
||||||
|
from moto.opsworks import opsworks_backend
|
||||||
from moto.kinesis import kinesis_backend
|
from moto.kinesis import kinesis_backend
|
||||||
from moto.kms import kms_backend
|
from moto.kms import kms_backend
|
||||||
from moto.rds import rds_backend
|
from moto.rds import rds_backend
|
||||||
@ -36,6 +37,7 @@ BACKENDS = {
|
|||||||
'emr': emr_backend,
|
'emr': emr_backend,
|
||||||
'glacier': glacier_backend,
|
'glacier': glacier_backend,
|
||||||
'iam': iam_backend,
|
'iam': iam_backend,
|
||||||
|
'opsworks': opsworks_backend,
|
||||||
'kinesis': kinesis_backend,
|
'kinesis': kinesis_backend,
|
||||||
'kms': kms_backend,
|
'kms': kms_backend,
|
||||||
'redshift': redshift_backend,
|
'redshift': redshift_backend,
|
||||||
|
@ -105,6 +105,7 @@ 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 = {}
|
||||||
|
13
moto/opsworks/__init__.py
Normal file
13
moto/opsworks/__init__.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from .models import opsworks_backends
|
||||||
|
from ..core.models import MockAWS
|
||||||
|
|
||||||
|
opsworks_backend = opsworks_backends['us-east-1']
|
||||||
|
|
||||||
|
|
||||||
|
def mock_opsworks(func=None):
|
||||||
|
if func:
|
||||||
|
return MockAWS(opsworks_backends)(func)
|
||||||
|
else:
|
||||||
|
return MockAWS(opsworks_backends)
|
||||||
|
|
14
moto/opsworks/exceptions.py
Normal file
14
moto/opsworks/exceptions.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import json
|
||||||
|
from werkzeug.exceptions import BadRequest
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceNotFoundException(BadRequest):
|
||||||
|
def __init__(self, message):
|
||||||
|
super(ResourceNotFoundError, self).__init__()
|
||||||
|
self.description = json.dumps({
|
||||||
|
"message": message,
|
||||||
|
'__type': 'ResourceNotFoundException',
|
||||||
|
})
|
||||||
|
|
168
moto/opsworks/models.py
Normal file
168
moto/opsworks/models.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
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 .exceptions import ResourceNotFoundException
|
||||||
|
|
||||||
|
|
||||||
|
class Layer(object):
|
||||||
|
def __init__(self, stack_id, type, name, shortname, attributes,
|
||||||
|
custom_instance_profile_arn, custom_json,
|
||||||
|
custom_security_group_ids, packages, volume_configurations,
|
||||||
|
enable_autohealing, auto_assign_elastic_ips,
|
||||||
|
auto_assign_public_ips, custom_recipes, install_updates_on_boot,
|
||||||
|
use_ebs_optimized_instances, lifecycle_event_configuration):
|
||||||
|
self.stack_id = stack_id
|
||||||
|
self.type = type
|
||||||
|
self.name = name
|
||||||
|
self.shortname = shortname
|
||||||
|
self.attributes = attributes
|
||||||
|
self.custom_instance_profile_arn = custom_instance_profile_arn
|
||||||
|
self.custom_json = custom_json
|
||||||
|
self.custom_security_group_ids = custom_security_group_ids
|
||||||
|
self.packages = packages
|
||||||
|
self.volume_configurations = volume_configurations
|
||||||
|
self.enable_autohealing = enable_autohealing
|
||||||
|
self.auto_assign_elastic_ips = auto_assign_elastic_ips
|
||||||
|
self.auto_assign_public_ips = auto_assign_public_ips
|
||||||
|
self.custom_recipes = custom_recipes
|
||||||
|
self.install_updates_on_boot = install_updates_on_boot
|
||||||
|
self.use_ebs_optimized_instances = use_ebs_optimized_instances
|
||||||
|
self.lifecycle_event_configuration = lifecycle_event_configuration
|
||||||
|
self.instances = []
|
||||||
|
|
||||||
|
|
||||||
|
class Stack(object):
|
||||||
|
def __init__(self, name, region, service_role_arn, default_instance_profile_arn,
|
||||||
|
vpcid='vpc-1f99bf7c',
|
||||||
|
attributes=None,
|
||||||
|
default_os='Ubuntu 12.04 LTS',
|
||||||
|
hostname_theme='Layer_Dependent',
|
||||||
|
default_availability_zone='us-east-1a',
|
||||||
|
default_subnet_id='subnet-73981004',
|
||||||
|
custom_json=None,
|
||||||
|
configuration_manager=None,
|
||||||
|
chef_configuration=None,
|
||||||
|
use_custom_cookbooks=False,
|
||||||
|
use_opsworks_security_groups=True,
|
||||||
|
custom_cookbooks_source=None,
|
||||||
|
default_ssh_keyname=None,
|
||||||
|
default_root_device_type='instance-store',
|
||||||
|
agent_version='LATEST'):
|
||||||
|
|
||||||
|
self.name = name
|
||||||
|
self.region = region
|
||||||
|
self.service_role_arn = service_role_arn
|
||||||
|
self.default_instance_profile_arn = default_instance_profile_arn
|
||||||
|
|
||||||
|
self.vpcid = vpcid
|
||||||
|
self.attributes = attributes
|
||||||
|
if attributes is None:
|
||||||
|
self.attributes = {'Color': None}
|
||||||
|
|
||||||
|
self.configuration_manager = configuration_manager
|
||||||
|
if configuration_manager is None:
|
||||||
|
self.configuration_manager = {'Name': 'Chef', 'Version': '11.4'}
|
||||||
|
|
||||||
|
self.chef_configuration = chef_configuration
|
||||||
|
if chef_configuration is None:
|
||||||
|
self.chef_configuration = {}
|
||||||
|
|
||||||
|
self.custom_cookbooks_source = custom_cookbooks_source
|
||||||
|
if custom_cookbooks_source is None:
|
||||||
|
self.custom_cookbooks_source = {}
|
||||||
|
|
||||||
|
self.custom_json = custom_json
|
||||||
|
self.default_ssh_keyname = default_ssh_keyname
|
||||||
|
self.default_os = default_os
|
||||||
|
self.hostname_theme = hostname_theme
|
||||||
|
self.default_availability_zone = default_availability_zone
|
||||||
|
self.default_subnet_id = default_subnet_id
|
||||||
|
self.use_custom_cookbooks = use_custom_cookbooks
|
||||||
|
self.use_opsworks_security_groups = use_opsworks_security_groups
|
||||||
|
self.default_root_device_type = default_root_device_type
|
||||||
|
self.agent_version = agent_version
|
||||||
|
|
||||||
|
self.id = "{}".format(uuid.uuid4())
|
||||||
|
self.layers = []
|
||||||
|
self.apps = []
|
||||||
|
self.account_number = "123456789012"
|
||||||
|
self.created_at = datetime.datetime.utcnow()
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.id == other.id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def arn(self):
|
||||||
|
return "arn:aws:opsworks:{region}:{account_number}:stack/{id}".format(
|
||||||
|
region=self.region,
|
||||||
|
account_number=self.account_number,
|
||||||
|
id=self.id
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
response = {
|
||||||
|
"AgentVersion": self.agent_version,
|
||||||
|
"Arn": self.arn,
|
||||||
|
"Attributes": self.attributes,
|
||||||
|
"ChefConfiguration": self.chef_configuration,
|
||||||
|
"ConfigurationManager": self.configuration_manager,
|
||||||
|
"CreatedAt": self.created_at.isoformat(),
|
||||||
|
"CustomCookbooksSource": self.custom_cookbooks_source,
|
||||||
|
"DefaultAvailabilityZone": self.default_availability_zone,
|
||||||
|
"DefaultInstanceProfileArn": self.default_instance_profile_arn,
|
||||||
|
"DefaultOs": self.default_os,
|
||||||
|
"DefaultRootDeviceType": self.default_root_device_type,
|
||||||
|
"DefaultSshKeyName": self.default_ssh_keyname,
|
||||||
|
"DefaultSubnetId": self.default_subnet_id,
|
||||||
|
"HostnameTheme": self.hostname_theme,
|
||||||
|
"Name": self.name,
|
||||||
|
"Region": self.region,
|
||||||
|
"ServiceRoleArn": self.service_role_arn,
|
||||||
|
"StackId": self.id,
|
||||||
|
"UseCustomCookbooks": self.use_custom_cookbooks,
|
||||||
|
"UseOpsworksSecurityGroups": self.use_opsworks_security_groups,
|
||||||
|
"VpcId": self.vpcid
|
||||||
|
}
|
||||||
|
if self.custom_json is not None:
|
||||||
|
response.update({"CustomJson": self.custom_json})
|
||||||
|
if self.default_ssh_keyname is not None:
|
||||||
|
response.update({"DefaultSshKeyName": self.default_ssh_keyname})
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class OpsWorksBackend(BaseBackend):
|
||||||
|
def __init__(self, ec2_backend, elb_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)
|
||||||
|
|
||||||
|
def create_stack(self, **kwargs):
|
||||||
|
stack = Stack(**kwargs)
|
||||||
|
self.stacks[stack.id] = stack
|
||||||
|
return stack
|
||||||
|
|
||||||
|
def describe_stacks(self, stack_ids=None):
|
||||||
|
if stack_ids is None:
|
||||||
|
return [stack.to_dict() for stack in self.stacks.values()]
|
||||||
|
|
||||||
|
unknown_stacks = set(stack_ids) - set(self.stacks.keys())
|
||||||
|
if unknown_stacks:
|
||||||
|
raise ResourceNotFoundException(unknown_stacks)
|
||||||
|
return [self.stacks[id].to_dict() for id in stack_ids]
|
||||||
|
|
||||||
|
opsworks_backends = {}
|
||||||
|
for region, ec2_backend in ec2_backends.items():
|
||||||
|
opsworks_backends[region] = OpsWorksBackend(ec2_backend, elb_backends[region])
|
47
moto/opsworks/responses.py
Normal file
47
moto/opsworks/responses.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from moto.core.responses import BaseResponse
|
||||||
|
from .models import opsworks_backends
|
||||||
|
|
||||||
|
|
||||||
|
class OpsWorksResponse(BaseResponse):
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parameters(self):
|
||||||
|
return json.loads(self.body.decode("utf-8"))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def opsworks_backend(self):
|
||||||
|
return opsworks_backends[self.region]
|
||||||
|
|
||||||
|
def create_stack(self):
|
||||||
|
kwargs = dict(
|
||||||
|
name=self.parameters.get("Name"),
|
||||||
|
region=self.parameters.get("Region"),
|
||||||
|
vpcid=self.parameters.get("VpcId"),
|
||||||
|
attributes=self.parameters.get("Attributes"),
|
||||||
|
default_instance_profile_arn=self.parameters.get("DefaultInstanceProfileArn"),
|
||||||
|
default_os=self.parameters.get("DefaultOs"),
|
||||||
|
hostname_theme=self.parameters.get("HostnameTheme"),
|
||||||
|
default_availability_zone=self.parameters.get("DefaultAvailabilityZone"),
|
||||||
|
default_subnet_id=self.parameters.get("DefaultInstanceProfileArn"),
|
||||||
|
custom_json=self.parameters.get("CustomJson"),
|
||||||
|
configuration_manager=self.parameters.get("ConfigurationManager"),
|
||||||
|
chef_configuration=self.parameters.get("ChefConfiguration"),
|
||||||
|
use_custom_cookbooks=self.parameters.get("UseCustomCookbooks"),
|
||||||
|
use_opsworks_security_groups=self.parameters.get("UseOpsworksSecurityGroups"),
|
||||||
|
custom_cookbooks_source=self.parameters.get("CustomCookbooksSource"),
|
||||||
|
default_ssh_keyname=self.parameters.get("DefaultSshKeyName"),
|
||||||
|
default_root_device_type=self.parameters.get("DefaultRootDeviceType"),
|
||||||
|
service_role_arn=self.parameters.get("ServiceRoleArn"),
|
||||||
|
agent_version=self.parameters.get("AgentVersion"),
|
||||||
|
)
|
||||||
|
stack = self.opsworks_backend.create_stack(**kwargs)
|
||||||
|
return json.dumps({"StackId": stack.id}, indent=1)
|
||||||
|
|
||||||
|
def describe_stacks(self):
|
||||||
|
stack_ids = self.parameters.get("StackIds")
|
||||||
|
stacks = self.opsworks_backend.describe_stacks(stack_ids)
|
||||||
|
return json.dumps({"Stacks": stacks}, indent=1)
|
12
moto/opsworks/urls.py
Normal file
12
moto/opsworks/urls.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from .responses import OpsWorksResponse
|
||||||
|
|
||||||
|
# AWS OpsWorks has a single endpoint: opsworks.us-east-1.amazonaws.com
|
||||||
|
# and only supports HTTPS requests.
|
||||||
|
url_bases = [
|
||||||
|
"opsworks.us-east-1.amazonaws.com"
|
||||||
|
]
|
||||||
|
|
||||||
|
url_paths = {
|
||||||
|
'{0}/$': OpsWorksResponse.dispatch,
|
||||||
|
}
|
41
tests/test_opsworks/test_stack.py
Normal file
41
tests/test_opsworks/test_stack.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import boto3
|
||||||
|
import sure # noqa
|
||||||
|
|
||||||
|
from moto import mock_opsworks, mock_ec2, mock_elb
|
||||||
|
|
||||||
|
|
||||||
|
@mock_opsworks
|
||||||
|
def test_create_stack_response():
|
||||||
|
client = boto3.client('opsworks')
|
||||||
|
response = client.create_stack(
|
||||||
|
Name="test_stack_1",
|
||||||
|
Region="us-east-1",
|
||||||
|
ServiceRoleArn="service_arn",
|
||||||
|
DefaultInstanceProfileArn="profile_arn"
|
||||||
|
)
|
||||||
|
response.should.contain("StackId")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_opsworks
|
||||||
|
def test_describe_stacks():
|
||||||
|
client = boto3.client('opsworks')
|
||||||
|
for i in xrange(1, 4):
|
||||||
|
client.create_stack(
|
||||||
|
Name="test_stack_{}".format(i),
|
||||||
|
Region="us-east-1",
|
||||||
|
ServiceRoleArn="service_arn",
|
||||||
|
DefaultInstanceProfileArn="profile_arn"
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.describe_stacks()
|
||||||
|
response['Stacks'].should.have.length_of(3)
|
||||||
|
response['Stacks'][0]['ServiceRoleArn'].should.equal("service_arn")
|
||||||
|
response['Stacks'][0]['DefaultInstanceProfileArn'].should.equal("profile_arn")
|
||||||
|
|
||||||
|
_id = response['Stacks'][0]['StackId']
|
||||||
|
response = client.describe_stacks(StackIds=[_id])
|
||||||
|
response['Stacks'].should.have.length_of(1)
|
||||||
|
response['Stacks'][0]['Arn'].should.contain(_id)
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user