Merge branch 'master' into lotsa_stuff
This commit is contained in:
commit
c4273c9da1
@ -298,7 +298,12 @@ class LambdaFunction(BaseModel):
|
||||
volumes=["{}:/var/task".format(data_vol.name)], environment=env_vars, detach=True, **run_kwargs)
|
||||
finally:
|
||||
if container:
|
||||
exit_code = container.wait()
|
||||
try:
|
||||
exit_code = container.wait(timeout=300)
|
||||
except requests.exceptions.ReadTimeout:
|
||||
exit_code = -1
|
||||
container.stop()
|
||||
container.kill()
|
||||
output = container.logs(stdout=False, stderr=True)
|
||||
output += container.logs(stdout=True, stderr=False)
|
||||
container.remove()
|
||||
|
@ -15,6 +15,7 @@ from moto.dynamodb import models as dynamodb_models
|
||||
from moto.ec2 import models as ec2_models
|
||||
from moto.ecs import models as ecs_models
|
||||
from moto.elb import models as elb_models
|
||||
from moto.elbv2 import models as elbv2_models
|
||||
from moto.iam import models as iam_models
|
||||
from moto.kinesis import models as kinesis_models
|
||||
from moto.kms import models as kms_models
|
||||
@ -61,6 +62,9 @@ MODEL_MAP = {
|
||||
"AWS::ECS::TaskDefinition": ecs_models.TaskDefinition,
|
||||
"AWS::ECS::Service": ecs_models.Service,
|
||||
"AWS::ElasticLoadBalancing::LoadBalancer": elb_models.FakeLoadBalancer,
|
||||
"AWS::ElasticLoadBalancingV2::LoadBalancer": elbv2_models.FakeLoadBalancer,
|
||||
"AWS::ElasticLoadBalancingV2::TargetGroup": elbv2_models.FakeTargetGroup,
|
||||
"AWS::ElasticLoadBalancingV2::Listener": elbv2_models.FakeListener,
|
||||
"AWS::DataPipeline::Pipeline": datapipeline_models.Pipeline,
|
||||
"AWS::IAM::InstanceProfile": iam_models.InstanceProfile,
|
||||
"AWS::IAM::Role": iam_models.Role,
|
||||
@ -326,7 +330,7 @@ def parse_output(output_logical_id, output_json, resources_map):
|
||||
output_json = clean_json(output_json, resources_map)
|
||||
output = Output()
|
||||
output.key = output_logical_id
|
||||
output.value = output_json['Value']
|
||||
output.value = clean_json(output_json['Value'], resources_map)
|
||||
output.description = output_json.get('Description')
|
||||
return output
|
||||
|
||||
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
||||
|
||||
import copy
|
||||
import itertools
|
||||
import ipaddress
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
@ -402,6 +403,10 @@ class Instance(TaggedEC2Resource, BotoInstance):
|
||||
subnet = ec2_backend.get_subnet(self.subnet_id)
|
||||
self.vpc_id = subnet.vpc_id
|
||||
self._placement.zone = subnet.availability_zone
|
||||
|
||||
if associate_public_ip is None:
|
||||
# Mapping public ip hasnt been explicitly enabled or disabled
|
||||
associate_public_ip = subnet.map_public_ip_on_launch == 'true'
|
||||
elif placement:
|
||||
self._placement.zone = placement
|
||||
else:
|
||||
@ -409,10 +414,22 @@ class Instance(TaggedEC2Resource, BotoInstance):
|
||||
|
||||
self.block_device_mapping = BlockDeviceMapping()
|
||||
|
||||
self.prep_nics(kwargs.get("nics", {}),
|
||||
subnet_id=self.subnet_id,
|
||||
private_ip=kwargs.get("private_ip"),
|
||||
associate_public_ip=associate_public_ip)
|
||||
self._private_ips = set()
|
||||
self.prep_nics(
|
||||
kwargs.get("nics", {}),
|
||||
private_ip=kwargs.get("private_ip"),
|
||||
associate_public_ip=associate_public_ip
|
||||
)
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
subnet = self.ec2_backend.get_subnet(self.subnet_id)
|
||||
for ip in self._private_ips:
|
||||
subnet.del_subnet_ip(ip)
|
||||
except Exception:
|
||||
# Its not "super" critical we clean this up, as reset will do this
|
||||
# worst case we'll get IP address exaustion... rarely
|
||||
pass
|
||||
|
||||
def setup_defaults(self):
|
||||
# Default have an instance with root volume should you not wish to
|
||||
@ -547,14 +564,23 @@ class Instance(TaggedEC2Resource, BotoInstance):
|
||||
else:
|
||||
return self.security_groups
|
||||
|
||||
def prep_nics(self, nic_spec, subnet_id=None, private_ip=None, associate_public_ip=None):
|
||||
def prep_nics(self, nic_spec, private_ip=None, associate_public_ip=None):
|
||||
self.nics = {}
|
||||
|
||||
if not private_ip:
|
||||
if self.subnet_id:
|
||||
subnet = self.ec2_backend.get_subnet(self.subnet_id)
|
||||
if not private_ip:
|
||||
private_ip = subnet.get_available_subnet_ip(instance=self)
|
||||
else:
|
||||
subnet.request_ip(private_ip, instance=self)
|
||||
|
||||
self._private_ips.add(private_ip)
|
||||
elif private_ip is None:
|
||||
# Preserve old behaviour if in EC2-Classic mode
|
||||
private_ip = random_private_ip()
|
||||
|
||||
# Primary NIC defaults
|
||||
primary_nic = {'SubnetId': subnet_id,
|
||||
primary_nic = {'SubnetId': self.subnet_id,
|
||||
'PrivateIpAddress': private_ip,
|
||||
'AssociatePublicIpAddress': associate_public_ip}
|
||||
primary_nic = dict((k, v) for k, v in primary_nic.items() if v)
|
||||
@ -2114,10 +2140,17 @@ class Subnet(TaggedEC2Resource):
|
||||
self.id = subnet_id
|
||||
self.vpc_id = vpc_id
|
||||
self.cidr_block = cidr_block
|
||||
self.cidr = ipaddress.ip_network(six.text_type(self.cidr_block))
|
||||
self._availability_zone = availability_zone
|
||||
self.default_for_az = default_for_az
|
||||
self.map_public_ip_on_launch = map_public_ip_on_launch
|
||||
|
||||
# Theory is we assign ip's as we go (as 16,777,214 usable IPs in a /8)
|
||||
self._subnet_ip_generator = self.cidr.hosts()
|
||||
self.reserved_ips = [six.next(self._subnet_ip_generator) for _ in range(0, 3)] # Reserved by AWS
|
||||
self._unused_ips = set() # if instance is destroyed hold IP here for reuse
|
||||
self._subnet_ips = {} # has IP: instance
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
|
||||
properties = cloudformation_json['Properties']
|
||||
@ -2184,6 +2217,46 @@ class Subnet(TaggedEC2Resource):
|
||||
'"Fn::GetAtt" : [ "{0}" , "AvailabilityZone" ]"')
|
||||
raise UnformattedGetAttTemplateException()
|
||||
|
||||
def get_available_subnet_ip(self, instance):
|
||||
try:
|
||||
new_ip = self._unused_ips.pop()
|
||||
except KeyError:
|
||||
new_ip = six.next(self._subnet_ip_generator)
|
||||
|
||||
# Skips any IP's if they've been manually specified
|
||||
while str(new_ip) in self._subnet_ips:
|
||||
new_ip = six.next(self._subnet_ip_generator)
|
||||
|
||||
if new_ip == self.cidr.broadcast_address:
|
||||
raise StopIteration() # Broadcast address cant be used obviously
|
||||
# TODO StopIteration will be raised if no ip's available, not sure how aws handles this.
|
||||
|
||||
new_ip = str(new_ip)
|
||||
self._subnet_ips[new_ip] = instance
|
||||
|
||||
return new_ip
|
||||
|
||||
def request_ip(self, ip, instance):
|
||||
if ipaddress.ip_address(ip) not in self.cidr:
|
||||
raise Exception('IP does not fall in the subnet CIDR of {0}'.format(self.cidr))
|
||||
|
||||
if ip in self._subnet_ips:
|
||||
raise Exception('IP already in use')
|
||||
try:
|
||||
self._unused_ips.remove(ip)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
self._subnet_ips[ip] = instance
|
||||
return ip
|
||||
|
||||
def del_subnet_ip(self, ip):
|
||||
try:
|
||||
del self._subnet_ips[ip]
|
||||
self._unused_ips.add(ip)
|
||||
except KeyError:
|
||||
pass # Unknown IP
|
||||
|
||||
|
||||
class SubnetBackend(object):
|
||||
def __init__(self):
|
||||
|
@ -57,7 +57,8 @@ class FakeTargetGroup(BaseModel):
|
||||
healthcheck_timeout_seconds,
|
||||
healthy_threshold_count,
|
||||
unhealthy_threshold_count,
|
||||
http_codes):
|
||||
matcher=None,
|
||||
target_type=None):
|
||||
self.name = name
|
||||
self.arn = arn
|
||||
self.vpc_id = vpc_id
|
||||
@ -72,7 +73,8 @@ class FakeTargetGroup(BaseModel):
|
||||
self.unhealthy_threshold_count = unhealthy_threshold_count
|
||||
self.load_balancer_arns = []
|
||||
self.tags = {}
|
||||
self.http_status_codes = http_codes
|
||||
self.matcher = matcher
|
||||
self.target_type = target_type
|
||||
|
||||
self.attributes = {
|
||||
'deregistration_delay.timeout_seconds': 300,
|
||||
@ -81,6 +83,10 @@ class FakeTargetGroup(BaseModel):
|
||||
|
||||
self.targets = OrderedDict()
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.arn
|
||||
|
||||
def register(self, targets):
|
||||
for target in targets:
|
||||
self.targets[target['id']] = {
|
||||
@ -105,6 +111,46 @@ class FakeTargetGroup(BaseModel):
|
||||
raise InvalidTargetError()
|
||||
return FakeHealthStatus(t['id'], t['port'], self.healthcheck_port, 'healthy')
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
elbv2_backend = elbv2_backends[region_name]
|
||||
|
||||
# per cloudformation docs:
|
||||
# The target group name should be shorter than 22 characters because
|
||||
# AWS CloudFormation uses the target group name to create the name of the load balancer.
|
||||
name = properties.get('Name', resource_name[:22])
|
||||
vpc_id = properties.get("VpcId")
|
||||
protocol = properties.get('Protocol')
|
||||
port = properties.get("Port")
|
||||
healthcheck_protocol = properties.get("HealthCheckProtocol")
|
||||
healthcheck_port = properties.get("HealthCheckPort")
|
||||
healthcheck_path = properties.get("HealthCheckPath")
|
||||
healthcheck_interval_seconds = properties.get("HealthCheckIntervalSeconds")
|
||||
healthcheck_timeout_seconds = properties.get("HealthCheckTimeoutSeconds")
|
||||
healthy_threshold_count = properties.get("HealthyThresholdCount")
|
||||
unhealthy_threshold_count = properties.get("UnhealthyThresholdCount")
|
||||
matcher = properties.get("Matcher")
|
||||
target_type = properties.get("TargetType")
|
||||
|
||||
target_group = elbv2_backend.create_target_group(
|
||||
name=name,
|
||||
vpc_id=vpc_id,
|
||||
protocol=protocol,
|
||||
port=port,
|
||||
healthcheck_protocol=healthcheck_protocol,
|
||||
healthcheck_port=healthcheck_port,
|
||||
healthcheck_path=healthcheck_path,
|
||||
healthcheck_interval_seconds=healthcheck_interval_seconds,
|
||||
healthcheck_timeout_seconds=healthcheck_timeout_seconds,
|
||||
healthy_threshold_count=healthy_threshold_count,
|
||||
unhealthy_threshold_count=unhealthy_threshold_count,
|
||||
matcher=matcher,
|
||||
target_type=target_type,
|
||||
)
|
||||
return target_group
|
||||
|
||||
|
||||
class FakeListener(BaseModel):
|
||||
|
||||
@ -126,6 +172,10 @@ class FakeListener(BaseModel):
|
||||
is_default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.arn
|
||||
|
||||
@property
|
||||
def rules(self):
|
||||
return self._non_default_rules + [self._default_rule]
|
||||
@ -137,6 +187,28 @@ class FakeListener(BaseModel):
|
||||
self._non_default_rules.append(rule)
|
||||
self._non_default_rules = sorted(self._non_default_rules, key=lambda x: x.priority)
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
elbv2_backend = elbv2_backends[region_name]
|
||||
load_balancer_arn = properties.get("LoadBalancerArn")
|
||||
protocol = properties.get("Protocol")
|
||||
port = properties.get("Port")
|
||||
ssl_policy = properties.get("SslPolicy")
|
||||
certificates = properties.get("Certificates")
|
||||
# transform default actions to confirm with the rest of the code and XML templates
|
||||
if "DefaultActions" in properties:
|
||||
default_actions = []
|
||||
for action in properties['DefaultActions']:
|
||||
default_actions.append({'type': action['Type'], 'target_group_arn': action['TargetGroupArn']})
|
||||
else:
|
||||
default_actions = None
|
||||
|
||||
listener = elbv2_backend.create_listener(
|
||||
load_balancer_arn, protocol, port, ssl_policy, certificates, default_actions)
|
||||
return listener
|
||||
|
||||
|
||||
class FakeRule(BaseModel):
|
||||
|
||||
@ -186,7 +258,7 @@ class FakeLoadBalancer(BaseModel):
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.name
|
||||
return self.arn
|
||||
|
||||
def add_tag(self, key, value):
|
||||
if len(self.tags) >= 10 and key not in self.tags:
|
||||
@ -204,6 +276,27 @@ class FakeLoadBalancer(BaseModel):
|
||||
''' Not exposed as part of the ELB API - used for CloudFormation. '''
|
||||
elbv2_backends[region].delete_load_balancer(self.arn)
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
elbv2_backend = elbv2_backends[region_name]
|
||||
|
||||
name = properties.get('Name', resource_name)
|
||||
security_groups = properties.get("SecurityGroups")
|
||||
subnet_ids = properties.get('Subnets')
|
||||
scheme = properties.get('Scheme', 'internet-facing')
|
||||
|
||||
load_balancer = elbv2_backend.create_load_balancer(name, security_groups, subnet_ids, scheme=scheme)
|
||||
return load_balancer
|
||||
|
||||
def get_cfn_attribute(self, attribute_name):
|
||||
attributes = {
|
||||
'DNSName': self.dns_name,
|
||||
'LoadBalancerName': self.name,
|
||||
}
|
||||
return attributes[attribute_name]
|
||||
|
||||
|
||||
class ELBv2Backend(BaseBackend):
|
||||
|
||||
@ -316,7 +409,7 @@ class ELBv2Backend(BaseBackend):
|
||||
def create_target_group(self, name, **kwargs):
|
||||
if len(name) > 32:
|
||||
raise InvalidTargetGroupNameError(
|
||||
"Target group name '%s' cannot be longer than '32' characters" % name
|
||||
"Target group name '%s' cannot be longer than '22' characters" % name
|
||||
)
|
||||
if not re.match('^[a-zA-Z0-9\-]+$', name):
|
||||
raise InvalidTargetGroupNameError(
|
||||
|
@ -734,9 +734,14 @@ CREATE_TARGET_GROUP_TEMPLATE = """<CreateTargetGroupResponse xmlns="http://elast
|
||||
<HealthCheckTimeoutSeconds>{{ target_group.healthcheck_timeout_seconds }}</HealthCheckTimeoutSeconds>
|
||||
<HealthyThresholdCount>{{ target_group.healthy_threshold_count }}</HealthyThresholdCount>
|
||||
<UnhealthyThresholdCount>{{ target_group.unhealthy_threshold_count }}</UnhealthyThresholdCount>
|
||||
{% if target_group.matcher %}
|
||||
<Matcher>
|
||||
<HttpCode>200</HttpCode>
|
||||
<HttpCode>{{ target_group.matcher['HttpCode'] }}</HttpCode>
|
||||
</Matcher>
|
||||
{% endif %}
|
||||
{% if target_group.target_type %}
|
||||
<TargetType>{{ target_group.target_type }}</TargetType>
|
||||
{% endif %}
|
||||
</member>
|
||||
</TargetGroups>
|
||||
</CreateTargetGroupResult>
|
||||
@ -836,6 +841,7 @@ DESCRIBE_LOAD_BALANCERS_TEMPLATE = """<DescribeLoadBalancersResponse xmlns="http
|
||||
<Code>provisioning</Code>
|
||||
</State>
|
||||
<Type>application</Type>
|
||||
<IpAddressType>ipv4</IpAddressType>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</LoadBalancers>
|
||||
@ -905,9 +911,14 @@ DESCRIBE_TARGET_GROUPS_TEMPLATE = """<DescribeTargetGroupsResponse xmlns="http:/
|
||||
<HealthCheckTimeoutSeconds>{{ target_group.healthcheck_timeout_seconds }}</HealthCheckTimeoutSeconds>
|
||||
<HealthyThresholdCount>{{ target_group.healthy_threshold_count }}</HealthyThresholdCount>
|
||||
<UnhealthyThresholdCount>{{ target_group.unhealthy_threshold_count }}</UnhealthyThresholdCount>
|
||||
{% if target_group.matcher %}
|
||||
<Matcher>
|
||||
<HttpCode>{{ target_group.http_status_codes }}</HttpCode>
|
||||
<HttpCode>{{ target_group.matcher['HttpCode'] }}</HttpCode>
|
||||
</Matcher>
|
||||
{% endif %}
|
||||
{% if target_group.target_type %}
|
||||
<TargetType>{{ target_group.target_type }}</TargetType>
|
||||
{% endif %}
|
||||
<LoadBalancerArns>
|
||||
{% for load_balancer_arn in target_group.load_balancer_arns %}
|
||||
<member>{{ load_balancer_arn }}</member>
|
||||
@ -1351,7 +1362,7 @@ MODIFY_TARGET_GROUP_TEMPLATE = """<ModifyTargetGroupResponse xmlns="http://elast
|
||||
<HealthyThresholdCount>{{ target_group.healthy_threshold_count }}</HealthyThresholdCount>
|
||||
<UnhealthyThresholdCount>{{ target_group.unhealthy_threshold_count }}</UnhealthyThresholdCount>
|
||||
<Matcher>
|
||||
<HttpCode>{{ target_group.http_status_codes }}</HttpCode>
|
||||
<HttpCode>{{ target_group.matcher['HttpCode'] }}</HttpCode>
|
||||
</Matcher>
|
||||
<LoadBalancerArns>
|
||||
{% for load_balancer_arn in target_group.load_balancer_arns %}
|
||||
|
@ -704,7 +704,8 @@ class RDS2Backend(BaseBackend):
|
||||
if self.arn_regex.match(source_database_id):
|
||||
db_kwargs['region'] = self.region
|
||||
|
||||
replica = copy.deepcopy(primary)
|
||||
# Shouldn't really copy here as the instance is duplicated. RDS replicas have different instances.
|
||||
replica = copy.copy(primary)
|
||||
replica.update(db_kwargs)
|
||||
replica.set_as_replica()
|
||||
self.databases[database_id] = replica
|
||||
|
@ -488,6 +488,7 @@ def lambda_handler(event, context):
|
||||
assert 'FunctionError' in result
|
||||
assert result['FunctionError'] == 'Handled'
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@mock_s3
|
||||
def test_tags():
|
||||
@ -554,6 +555,7 @@ def test_tags():
|
||||
TagKeys=['spam']
|
||||
)['ResponseMetadata']['HTTPStatusCode'].should.equal(204)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_tags_not_found():
|
||||
"""
|
||||
@ -574,6 +576,7 @@ def test_tags_not_found():
|
||||
TagKeys=['spam']
|
||||
).should.throw(botocore.client.ClientError)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_invoke_async_function():
|
||||
conn = boto3.client('lambda', 'us-west-2')
|
||||
@ -581,10 +584,8 @@ def test_invoke_async_function():
|
||||
FunctionName='testFunction',
|
||||
Runtime='python2.7',
|
||||
Role='test-iam-role',
|
||||
Handler='lambda_function.handler',
|
||||
Code={
|
||||
'ZipFile': get_test_zip_file1(),
|
||||
},
|
||||
Handler='lambda_function.lambda_handler',
|
||||
Code={'ZipFile': get_test_zip_file1()},
|
||||
Description='test lambda function',
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
@ -593,11 +594,12 @@ def test_invoke_async_function():
|
||||
|
||||
success_result = conn.invoke_async(
|
||||
FunctionName='testFunction',
|
||||
InvokeArgs=json.dumps({ 'test': 'event' })
|
||||
InvokeArgs=json.dumps({'test': 'event'})
|
||||
)
|
||||
|
||||
success_result['Status'].should.equal(202)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@freeze_time('2015-01-01 00:00:00')
|
||||
def test_get_function_created_with_zipfile():
|
||||
@ -646,6 +648,7 @@ def test_get_function_created_with_zipfile():
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def add_function_permission():
|
||||
conn = boto3.client('lambda', 'us-west-2')
|
||||
|
@ -38,7 +38,7 @@ from moto import (
|
||||
mock_sns_deprecated,
|
||||
mock_sqs,
|
||||
mock_sqs_deprecated,
|
||||
)
|
||||
mock_elbv2)
|
||||
|
||||
from .fixtures import (
|
||||
ec2_classic_eip,
|
||||
@ -2111,3 +2111,156 @@ def test_stack_spot_fleet():
|
||||
launch_spec['SubnetId'].should.equal(subnet_id)
|
||||
launch_spec['SpotPrice'].should.equal("0.13")
|
||||
launch_spec['WeightedCapacity'].should.equal(2.0)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
@mock_elbv2
|
||||
@mock_cloudformation
|
||||
def test_stack_elbv2_resources_integration():
|
||||
alb_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Outputs": {
|
||||
"albdns": {
|
||||
"Description": "Load balanacer DNS",
|
||||
"Value": {"Fn::GetAtt": ["alb", "DNSName"]},
|
||||
},
|
||||
"albname": {
|
||||
"Description": "Load balancer name",
|
||||
"Value": {"Fn::GetAtt": ["alb", "LoadBalancerName"]},
|
||||
},
|
||||
},
|
||||
"Resources": {
|
||||
"alb": {
|
||||
"Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
|
||||
"Properties": {
|
||||
"Name": "myelbv2",
|
||||
"Scheme": "internet-facing",
|
||||
"Subnets": [{
|
||||
"Ref": "mysubnet",
|
||||
}],
|
||||
"SecurityGroups": [{
|
||||
"Ref": "mysg",
|
||||
}],
|
||||
"Type": "application",
|
||||
"IpAddressType": "ipv4",
|
||||
}
|
||||
},
|
||||
"mytargetgroup": {
|
||||
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
|
||||
"Properties": {
|
||||
"HealthCheckIntervalSeconds": 30,
|
||||
"HealthCheckPath": "/status",
|
||||
"HealthCheckPort": 80,
|
||||
"HealthCheckProtocol": "HTTP",
|
||||
"HealthCheckTimeoutSeconds": 5,
|
||||
"HealthyThresholdCount": 30,
|
||||
"UnhealthyThresholdCount": 5,
|
||||
"Matcher": {
|
||||
"HttpCode": "200,201"
|
||||
},
|
||||
"Name": "mytargetgroup",
|
||||
"Port": 80,
|
||||
"Protocol": "HTTP",
|
||||
"TargetType": "instance",
|
||||
"Targets": [{
|
||||
"Id": {
|
||||
"Ref": "ec2instance",
|
||||
"Port": 80,
|
||||
},
|
||||
}],
|
||||
"VpcId": {
|
||||
"Ref": "myvpc",
|
||||
}
|
||||
}
|
||||
},
|
||||
"listener": {
|
||||
"Type": "AWS::ElasticLoadBalancingV2::Listener",
|
||||
"Properties": {
|
||||
"DefaultActions": [{
|
||||
"Type": "forward",
|
||||
"TargetGroupArn": {"Ref": "mytargetgroup"}
|
||||
}],
|
||||
"LoadBalancerArn": {"Ref": "alb"},
|
||||
"Port": "80",
|
||||
"Protocol": "HTTP"
|
||||
}
|
||||
},
|
||||
"myvpc": {
|
||||
"Type": "AWS::EC2::VPC",
|
||||
"Properties": {
|
||||
"CidrBlock": "10.0.0.0/16",
|
||||
}
|
||||
},
|
||||
"mysubnet": {
|
||||
"Type": "AWS::EC2::Subnet",
|
||||
"Properties": {
|
||||
"CidrBlock": "10.0.0.0/27",
|
||||
"VpcId": {"Ref": "myvpc"},
|
||||
}
|
||||
},
|
||||
"mysg": {
|
||||
"Type": "AWS::EC2::SecurityGroup",
|
||||
"Properties": {
|
||||
"GroupName": "mysg",
|
||||
"GroupDescription": "test security group",
|
||||
"VpcId": {"Ref": "myvpc"}
|
||||
}
|
||||
},
|
||||
"ec2instance": {
|
||||
"Type": "AWS::EC2::Instance",
|
||||
"Properties": {
|
||||
"ImageId": "ami-1234abcd",
|
||||
"UserData": "some user data",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
alb_template_json = json.dumps(alb_template)
|
||||
|
||||
cfn_conn = boto3.client("cloudformation", "us-west-1")
|
||||
cfn_conn.create_stack(
|
||||
StackName="elb_stack",
|
||||
TemplateBody=alb_template_json,
|
||||
)
|
||||
|
||||
elbv2_conn = boto3.client("elbv2", "us-west-1")
|
||||
|
||||
load_balancers = elbv2_conn.describe_load_balancers()['LoadBalancers']
|
||||
len(load_balancers).should.equal(1)
|
||||
load_balancers[0]['LoadBalancerName'].should.equal('myelbv2')
|
||||
load_balancers[0]['Scheme'].should.equal('internet-facing')
|
||||
load_balancers[0]['Type'].should.equal('application')
|
||||
load_balancers[0]['IpAddressType'].should.equal('ipv4')
|
||||
|
||||
target_groups = elbv2_conn.describe_target_groups()['TargetGroups']
|
||||
len(target_groups).should.equal(1)
|
||||
target_groups[0]['HealthCheckIntervalSeconds'].should.equal(30)
|
||||
target_groups[0]['HealthCheckPath'].should.equal('/status')
|
||||
target_groups[0]['HealthCheckPort'].should.equal('80')
|
||||
target_groups[0]['HealthCheckProtocol'].should.equal('HTTP')
|
||||
target_groups[0]['HealthCheckTimeoutSeconds'].should.equal(5)
|
||||
target_groups[0]['HealthyThresholdCount'].should.equal(30)
|
||||
target_groups[0]['UnhealthyThresholdCount'].should.equal(5)
|
||||
target_groups[0]['Matcher'].should.equal({'HttpCode': '200,201'})
|
||||
target_groups[0]['TargetGroupName'].should.equal('mytargetgroup')
|
||||
target_groups[0]['Port'].should.equal(80)
|
||||
target_groups[0]['Protocol'].should.equal('HTTP')
|
||||
target_groups[0]['TargetType'].should.equal('instance')
|
||||
|
||||
listeners = elbv2_conn.describe_listeners(LoadBalancerArn=load_balancers[0]['LoadBalancerArn'])['Listeners']
|
||||
len(listeners).should.equal(1)
|
||||
listeners[0]['LoadBalancerArn'].should.equal(load_balancers[0]['LoadBalancerArn'])
|
||||
listeners[0]['Port'].should.equal(80)
|
||||
listeners[0]['Protocol'].should.equal('HTTP')
|
||||
listeners[0]['DefaultActions'].should.equal([{
|
||||
"Type": "forward",
|
||||
"TargetGroupArn": target_groups[0]['TargetGroupArn']
|
||||
}])
|
||||
|
||||
# test outputs
|
||||
stacks = cfn_conn.describe_stacks(StackName='elb_stack')['Stacks']
|
||||
len(stacks).should.equal(1)
|
||||
stacks[0]['Outputs'].should.equal([
|
||||
{'OutputKey': 'albdns', 'OutputValue': load_balancers[0]['DNSName']},
|
||||
{'OutputKey': 'albname', 'OutputValue': load_balancers[0]['LoadBalancerName']},
|
||||
])
|
||||
|
@ -5,7 +5,9 @@ from nose.tools import assert_raises
|
||||
|
||||
import base64
|
||||
import datetime
|
||||
import ipaddress
|
||||
|
||||
import six
|
||||
import boto
|
||||
import boto3
|
||||
from boto.ec2.instance import Reservation, InstanceAttribute
|
||||
@ -413,6 +415,7 @@ def test_get_instances_filtering_by_image_id():
|
||||
'Values': [image_id]}])['Reservations']
|
||||
reservations[0]['Instances'].should.have.length_of(1)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_get_instances_filtering_by_private_dns():
|
||||
image_id = 'ami-1234abcd'
|
||||
@ -427,6 +430,7 @@ def test_get_instances_filtering_by_private_dns():
|
||||
])['Reservations']
|
||||
reservations[0]['Instances'].should.have.length_of(1)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_get_instances_filtering_by_ni_private_dns():
|
||||
image_id = 'ami-1234abcd'
|
||||
@ -441,6 +445,7 @@ def test_get_instances_filtering_by_ni_private_dns():
|
||||
])['Reservations']
|
||||
reservations[0]['Instances'].should.have.length_of(1)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_get_instances_filtering_by_instance_group_name():
|
||||
image_id = 'ami-1234abcd'
|
||||
@ -458,6 +463,7 @@ def test_get_instances_filtering_by_instance_group_name():
|
||||
])['Reservations']
|
||||
reservations[0]['Instances'].should.have.length_of(1)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_get_instances_filtering_by_instance_group_id():
|
||||
image_id = 'ami-1234abcd'
|
||||
@ -476,6 +482,7 @@ def test_get_instances_filtering_by_instance_group_id():
|
||||
])['Reservations']
|
||||
reservations[0]['Instances'].should.have.length_of(1)
|
||||
|
||||
|
||||
@mock_ec2_deprecated
|
||||
def test_get_instances_filtering_by_tag():
|
||||
conn = boto.connect_ec2()
|
||||
@ -830,18 +837,113 @@ def test_run_instance_with_placement():
|
||||
instance.placement.should.equal("us-east-1b")
|
||||
|
||||
|
||||
@mock_ec2_deprecated
|
||||
def test_run_instance_with_subnet():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc = conn.create_vpc("10.0.0.0/16")
|
||||
subnet = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||
reservation = conn.run_instances('ami-1234abcd', subnet_id=subnet.id)
|
||||
instance = reservation.instances[0]
|
||||
@mock_ec2
|
||||
def test_run_instance_with_subnet_boto3():
|
||||
client = boto3.client('ec2', region_name='eu-central-1')
|
||||
|
||||
instance.subnet_id.should.equal(subnet.id)
|
||||
ip_networks = [
|
||||
(ipaddress.ip_network('10.0.0.0/16'), ipaddress.ip_network('10.0.99.0/24')),
|
||||
(ipaddress.ip_network('192.168.42.0/24'), ipaddress.ip_network('192.168.42.0/25'))
|
||||
]
|
||||
|
||||
all_enis = conn.get_all_network_interfaces()
|
||||
all_enis.should.have.length_of(1)
|
||||
# Tests instances are created with the correct IPs
|
||||
for vpc_cidr, subnet_cidr in ip_networks:
|
||||
resp = client.create_vpc(
|
||||
CidrBlock=str(vpc_cidr),
|
||||
AmazonProvidedIpv6CidrBlock=False,
|
||||
DryRun=False,
|
||||
InstanceTenancy='default'
|
||||
)
|
||||
vpc_id = resp['Vpc']['VpcId']
|
||||
|
||||
resp = client.create_subnet(
|
||||
CidrBlock=str(subnet_cidr),
|
||||
VpcId=vpc_id
|
||||
)
|
||||
subnet_id = resp['Subnet']['SubnetId']
|
||||
|
||||
resp = client.run_instances(
|
||||
ImageId='ami-1234abcd',
|
||||
MaxCount=1,
|
||||
MinCount=1,
|
||||
SubnetId=subnet_id
|
||||
)
|
||||
instance = resp['Instances'][0]
|
||||
instance['SubnetId'].should.equal(subnet_id)
|
||||
|
||||
priv_ipv4 = ipaddress.ip_address(six.text_type(instance['PrivateIpAddress']))
|
||||
subnet_cidr.should.contain(priv_ipv4)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_run_instance_with_specified_private_ipv4():
|
||||
client = boto3.client('ec2', region_name='eu-central-1')
|
||||
|
||||
vpc_cidr = ipaddress.ip_network('192.168.42.0/24')
|
||||
subnet_cidr = ipaddress.ip_network('192.168.42.0/25')
|
||||
|
||||
resp = client.create_vpc(
|
||||
CidrBlock=str(vpc_cidr),
|
||||
AmazonProvidedIpv6CidrBlock=False,
|
||||
DryRun=False,
|
||||
InstanceTenancy='default'
|
||||
)
|
||||
vpc_id = resp['Vpc']['VpcId']
|
||||
|
||||
resp = client.create_subnet(
|
||||
CidrBlock=str(subnet_cidr),
|
||||
VpcId=vpc_id
|
||||
)
|
||||
subnet_id = resp['Subnet']['SubnetId']
|
||||
|
||||
resp = client.run_instances(
|
||||
ImageId='ami-1234abcd',
|
||||
MaxCount=1,
|
||||
MinCount=1,
|
||||
SubnetId=subnet_id,
|
||||
PrivateIpAddress='192.168.42.5'
|
||||
)
|
||||
instance = resp['Instances'][0]
|
||||
instance['SubnetId'].should.equal(subnet_id)
|
||||
instance['PrivateIpAddress'].should.equal('192.168.42.5')
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_run_instance_mapped_public_ipv4():
|
||||
client = boto3.client('ec2', region_name='eu-central-1')
|
||||
|
||||
vpc_cidr = ipaddress.ip_network('192.168.42.0/24')
|
||||
subnet_cidr = ipaddress.ip_network('192.168.42.0/25')
|
||||
|
||||
resp = client.create_vpc(
|
||||
CidrBlock=str(vpc_cidr),
|
||||
AmazonProvidedIpv6CidrBlock=False,
|
||||
DryRun=False,
|
||||
InstanceTenancy='default'
|
||||
)
|
||||
vpc_id = resp['Vpc']['VpcId']
|
||||
|
||||
resp = client.create_subnet(
|
||||
CidrBlock=str(subnet_cidr),
|
||||
VpcId=vpc_id
|
||||
)
|
||||
subnet_id = resp['Subnet']['SubnetId']
|
||||
client.modify_subnet_attribute(
|
||||
SubnetId=subnet_id,
|
||||
MapPublicIpOnLaunch={'Value': True}
|
||||
)
|
||||
|
||||
resp = client.run_instances(
|
||||
ImageId='ami-1234abcd',
|
||||
MaxCount=1,
|
||||
MinCount=1,
|
||||
SubnetId=subnet_id
|
||||
)
|
||||
instance = resp['Instances'][0]
|
||||
instance.should.contain('PublicDnsName')
|
||||
instance.should.contain('PublicIpAddress')
|
||||
len(instance['PublicDnsName']).should.be.greater_than(0)
|
||||
len(instance['PublicIpAddress']).should.be.greater_than(0)
|
||||
|
||||
|
||||
@mock_ec2_deprecated
|
||||
@ -853,7 +955,7 @@ def test_run_instance_with_nic_autocreated():
|
||||
'test security group #1', 'this is a test security group')
|
||||
security_group2 = conn.create_security_group(
|
||||
'test security group #2', 'this is a test security group')
|
||||
private_ip = "54.0.0.1"
|
||||
private_ip = "10.0.0.1"
|
||||
|
||||
reservation = conn.run_instances('ami-1234abcd', subnet_id=subnet.id,
|
||||
security_groups=[security_group1.name],
|
||||
@ -880,6 +982,7 @@ def test_run_instance_with_nic_autocreated():
|
||||
eni.private_ip_addresses.should.have.length_of(1)
|
||||
eni.private_ip_addresses[0].private_ip_address.should.equal(private_ip)
|
||||
|
||||
|
||||
@mock_ec2_deprecated
|
||||
def test_run_instance_with_nic_preexisting():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
@ -1012,6 +1115,7 @@ def test_ec2_classic_has_public_ip_address():
|
||||
instance.private_ip_address.should_not.equal(None)
|
||||
instance.private_dns_name.should.contain(instance.private_ip_address.replace('.', '-'))
|
||||
|
||||
|
||||
@mock_ec2_deprecated
|
||||
def test_run_instance_with_keypair():
|
||||
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||
|
@ -126,9 +126,9 @@ def test_route_tables_filters_associations():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
|
||||
vpc = conn.create_vpc("10.0.0.0/16")
|
||||
subnet1 = conn.create_subnet(vpc.id, "10.0.0.0/18")
|
||||
subnet2 = conn.create_subnet(vpc.id, "10.0.1.0/18")
|
||||
subnet3 = conn.create_subnet(vpc.id, "10.0.2.0/18")
|
||||
subnet1 = conn.create_subnet(vpc.id, "10.0.0.0/24")
|
||||
subnet2 = conn.create_subnet(vpc.id, "10.0.1.0/24")
|
||||
subnet3 = conn.create_subnet(vpc.id, "10.0.2.0/24")
|
||||
route_table1 = conn.create_route_table(vpc.id)
|
||||
route_table2 = conn.create_route_table(vpc.id)
|
||||
|
||||
|
@ -24,7 +24,7 @@ while True:
|
||||
break
|
||||
except EXCEPTIONS:
|
||||
elapsed_s = time.time() - start_ts
|
||||
if elapsed_s > 30:
|
||||
if elapsed_s > 60:
|
||||
raise
|
||||
|
||||
print('.')
|
||||
|
Loading…
Reference in New Issue
Block a user