def test_component_auto_scaling_group_configurable_properties2(): definition = {"Resources": {}} configuration = { 'Name': 'Foo', 'InstanceType': 't2.micro', 'Image': 'foo', 'SpotPrice': 0.250 } args = MagicMock() args.region = "foo" info = { 'StackName': 'FooStack', 'StackVersion': 'FooVersion' } result = component_auto_scaling_group(definition, configuration, args, info, False, MagicMock()) assert result["Resources"]["FooConfig"]["Properties"]["SpotPrice"] == 0.250 del configuration["SpotPrice"] result = component_auto_scaling_group(definition, configuration, args, info, False, MagicMock()) assert "SpotPrice" not in result["Resources"]["FooConfig"]["Properties"]
def component_taupage_auto_scaling_group(definition, configuration, args, info, force, account_info): # inherit from the normal auto scaling group but discourage user info and replace with a Taupage config if 'Image' not in configuration: configuration['Image'] = 'LatestTaupageImage' definition = component_auto_scaling_group(definition, configuration, args, info, force, account_info) taupage_config = configuration['TaupageConfig'] if 'notify_cfn' not in taupage_config: taupage_config['notify_cfn'] = {'stack': '{}-{}'.format(info["StackName"], info["StackVersion"]), 'resource': configuration['Name']} if 'application_id' not in taupage_config: taupage_config['application_id'] = info['StackName'] if 'application_version' not in taupage_config: taupage_config['application_version'] = info['StackVersion'] check_application_id(taupage_config['application_id']) check_application_version(taupage_config['application_version']) runtime = taupage_config.get('runtime') if runtime != 'Docker': raise click.UsageError('Taupage only supports the "Docker" runtime currently') source = taupage_config.get('source') if not source: raise click.UsageError('The "source" property of TaupageConfig must be specified') docker_image = pierone.api.DockerImage.parse(source) if not force and docker_image.registry: check_docker_image_exists(docker_image) config_name = configuration["Name"] + "Config" ensure_keys(definition, "Resources", config_name, "Properties") properties = definition["Resources"][config_name]["Properties"] mappings = definition.get('Mappings', {}) server_subnets = set(mappings.get('ServerSubnets', {}).get(args.region, {}).get('Subnets', [])) # in dmz or public subnet but without public ip if server_subnets and not properties.get('AssociatePublicIpAddress') and server_subnets ==\ set(mappings.get('LoadBalancerInternalSubnets', {}).get(args.region, {}).get('Subnets', [])): # we need to extend taupage_config with the mapping subnet-id => net ip nat_gateways = {} ec2 = boto3.client('ec2', args.region) for nat_gateway in ec2.describe_nat_gateways()['NatGateways']: if nat_gateway['SubnetId'] in server_subnets: for address in nat_gateway['NatGatewayAddresses']: nat_gateways[nat_gateway['SubnetId']] = address['PrivateIp'] break if nat_gateways: taupage_config['nat_gateways'] = nat_gateways properties["UserData"] = {"Fn::Base64": generate_user_data(taupage_config, args.region)} return definition
def test_component_auto_scaling_group_configurable_properties(): definition = {"Resources": {}} configuration = { 'Name': 'Foo', 'InstanceType': 't2.micro', 'Image': 'foo', 'AutoScaling': { 'Minimum': 2, 'Maximum': 10, 'SuccessRequires': '4 within 30m', 'MetricType': 'CPU', 'Period': 60, 'ScaleUpThreshold': 50, 'ScaleDownThreshold': 20, 'EvaluationPeriods': 1, 'Cooldown': 30, 'Statistic': 'Maximum' } } args = MagicMock() args.region = "foo" info = { 'StackName': 'FooStack', 'StackVersion': 'FooVersion' } result = component_auto_scaling_group(definition, configuration, args, info, False, MagicMock()) assert result["Resources"]["FooScaleUp"] is not None assert result["Resources"]["FooScaleUp"]["Properties"] is not None assert result["Resources"]["FooScaleUp"]["Properties"]["ScalingAdjustment"] == "1" assert result["Resources"]["FooScaleUp"]["Properties"]["Cooldown"] == "30" assert result["Resources"]["FooScaleDown"] is not None assert result["Resources"]["FooScaleDown"]["Properties"] is not None assert result["Resources"]["FooScaleDown"]["Properties"]["Cooldown"] == "30" assert result["Resources"]["FooScaleDown"]["Properties"]["ScalingAdjustment"] == "-1" assert result["Resources"]["Foo"] is not None assert result["Resources"]["Foo"]["CreationPolicy"] is not None assert result["Resources"]["Foo"]["CreationPolicy"]["ResourceSignal"] is not None assert result["Resources"]["Foo"]["CreationPolicy"]["ResourceSignal"]["Timeout"] == "PT30M" assert result["Resources"]["Foo"]["CreationPolicy"]["ResourceSignal"]["Count"] == "4" assert result["Resources"]["Foo"]["Properties"] is not None assert result["Resources"]["Foo"]["Properties"]["HealthCheckType"] == "EC2" assert result["Resources"]["Foo"]["Properties"]["MinSize"] == 2 assert result["Resources"]["Foo"]["Properties"]["DesiredCapacity"] == 2 assert result["Resources"]["Foo"]["Properties"]["MaxSize"] == 10 expected_desc = "Scale-down if CPU < 20% for 1.0 minutes (Maximum)" assert result["Resources"]["FooCPUAlarmHigh"]["Properties"]["Statistic"] == "Maximum" assert result["Resources"]["FooCPUAlarmLow"]["Properties"]["Period"] == "60" assert result["Resources"]["FooCPUAlarmHigh"]["Properties"]["EvaluationPeriods"] == "1" assert result["Resources"]["FooCPUAlarmLow"]["Properties"]["AlarmDescription"] == expected_desc
def test_resource_overrides_autoscaling_policy(): definition = { "Resources": { "FooScaleUp": { "Properties": { "AdjustmentType": "ChangeInCapacity", "AutoScalingGroupName": { "Ref": "Foo" }, "Cooldown": "180", "ScalingAdjustment": "2" }, "Type": "AWS::AutoScaling::ScalingPolicy" }, "FooScaleDown": { "Properties": { "AdjustmentType": "ChangeInCapacity", "AutoScalingGroupName": { "Ref": "Foo" }, "Cooldown": "90", "ScalingAdjustment": "-3" }, "Type": "AWS::AutoScaling::ScalingPolicy" }, } } expected = copy.copy(definition["Resources"]) configuration = { 'Name': 'Foo', 'InstanceType': 't2.micro', 'Image': 'foo', 'AutoScaling': { 'Minimum': 2, 'Maximum': 10, } } args = MagicMock() args.region = "foo" info = { 'StackName': 'FooStack', 'StackVersion': 'FooVersion' } result = component_auto_scaling_group( definition, configuration, args, info, False, MagicMock()) assert result["Resources"]["FooScaleUp"] == expected["FooScaleUp"] assert result["Resources"]["FooScaleDown"] == expected["FooScaleDown"]
def test_resource_overrides_autoscaling_policy(): definition = { "Resources": { "FooScaleUp": { "Properties": { "AdjustmentType": "ChangeInCapacity", "AutoScalingGroupName": { "Ref": "Foo" }, "Cooldown": "180", "ScalingAdjustment": "2" }, "Type": "AWS::AutoScaling::ScalingPolicy" }, "FooScaleDown": { "Properties": { "AdjustmentType": "ChangeInCapacity", "AutoScalingGroupName": { "Ref": "Foo" }, "Cooldown": "90", "ScalingAdjustment": "-3" }, "Type": "AWS::AutoScaling::ScalingPolicy" }, } } expected = copy.copy(definition["Resources"]) configuration = { 'Name': 'Foo', 'InstanceType': 't2.micro', 'Image': 'foo', 'AutoScaling': { 'Minimum': 2, 'Maximum': 10, } } args = MagicMock() args.region = "foo" info = {'StackName': 'FooStack', 'StackVersion': 'FooVersion'} result = component_auto_scaling_group(definition, configuration, args, info, False, MagicMock()) assert result["Resources"]["FooScaleUp"] == expected["FooScaleUp"] assert result["Resources"]["FooScaleDown"] == expected["FooScaleDown"]
def test_component_auto_scaling_group_metric_type(): definition = {"Resources": {}} configuration = { 'Name': 'Foo', 'InstanceType': 't2.micro', 'Image': 'foo', 'AutoScaling': { 'Minimum': 2, 'Maximum': 10, 'MetricType': 'NetworkIn', 'Period': 60, 'EvaluationPeriods': 10, 'ScaleUpThreshold': '50 TB', 'ScaleDownThreshold': '10', 'Statistic': 'Maximum' } } args = MagicMock() args.region = "foo" info = { 'StackName': 'FooStack', 'StackVersion': 'FooVersion' } result = component_auto_scaling_group(definition, configuration, args, info, False, MagicMock()) expected_high_desc = "Scale-up if NetworkIn > 50 Terabytes for 10.0 minutes (Maximum)" assert result["Resources"]["FooNetworkAlarmHigh"] is not None assert result["Resources"]["FooNetworkAlarmHigh"]["Properties"] is not None assert result["Resources"]["FooNetworkAlarmHigh"]["Properties"]["MetricName"] == "NetworkIn" assert result["Resources"]["FooNetworkAlarmHigh"]["Properties"]["Unit"] == "Terabytes" assert result["Resources"]["FooNetworkAlarmHigh"]["Properties"]["Threshold"] == "50" assert result["Resources"]["FooNetworkAlarmHigh"]["Properties"]["Statistic"] == "Maximum" assert result["Resources"]["FooNetworkAlarmHigh"]["Properties"]["Period"] == "60" assert result["Resources"]["FooNetworkAlarmHigh"]["Properties"]["EvaluationPeriods"] == "10" assert result["Resources"]["FooNetworkAlarmHigh"]["Properties"]["AlarmDescription"] == expected_high_desc expected_low_desc = "Scale-down if NetworkIn < 10 Bytes for 10.0 minutes (Maximum)" assert result["Resources"]["FooNetworkAlarmLow"]is not None assert result["Resources"]["FooNetworkAlarmLow"]["Properties"] is not None assert result["Resources"]["FooNetworkAlarmLow"]["Properties"]["MetricName"] == "NetworkIn" assert result["Resources"]["FooNetworkAlarmLow"]["Properties"]["Unit"] == "Bytes" assert result["Resources"]["FooNetworkAlarmLow"]["Properties"]["Threshold"] == "10" assert result["Resources"]["FooNetworkAlarmLow"]["Properties"]["Statistic"] == "Maximum" assert result["Resources"]["FooNetworkAlarmLow"]["Properties"]["Period"] == "60" assert result["Resources"]["FooNetworkAlarmLow"]["Properties"]["EvaluationPeriods"] == "10" assert result["Resources"]["FooNetworkAlarmLow"]["Properties"]["AlarmDescription"] == expected_low_desc
def test_component_autoscaling_group_multiple_load_balancer_v2(monkeypatch): configuration = { 'Name': 'Test', 'InstanceType': 't2.micro', 'Image': 'foo', 'ElasticLoadBalancerV2': [ 'LB1', 'LB2'] } definition = {"Resources": {}} info = {'StackName': 'foobar', 'StackVersion': '0.1'} args = MagicMock() args.region = "foo" result = component_auto_scaling_group(definition, configuration, args, info, False, MagicMock()) assert [{'Ref': 'LB1TargetGroup'},{'Ref': 'LB2TargetGroup'}] == result['Resources']['Test']['Properties']['TargetGroupARNs']
def component_taupage_auto_scaling_group(definition, configuration, args, info, force, account_info): # inherit from the normal auto scaling group but discourage user info and replace with a Taupage config if 'Image' not in configuration: configuration['Image'] = 'LatestTaupageImage' definition = component_auto_scaling_group(definition, configuration, args, info, force, account_info) taupage_config = configuration['TaupageConfig'] if 'notify_cfn' not in taupage_config: taupage_config['notify_cfn'] = { 'stack': '{}-{}'.format(info["StackName"], info["StackVersion"]), 'resource': configuration['Name'] } if 'application_id' not in taupage_config: taupage_config['application_id'] = info['StackName'] if 'application_version' not in taupage_config: taupage_config['application_version'] = info['StackVersion'] check_application_id(taupage_config['application_id']) check_application_version(taupage_config['application_version']) runtime = taupage_config.get('runtime') if runtime != 'Docker': raise click.UsageError( 'Taupage only supports the "Docker" runtime currently') source = taupage_config.get('source') if not source: raise click.UsageError( 'The "source" property of TaupageConfig must be specified') docker_image = pierone.api.DockerImage.parse(source) if not force and docker_image.registry: check_docker_image_exists(docker_image) userdata = generate_user_data(taupage_config, args.region) config_name = configuration["Name"] + "Config" ensure_keys(definition, "Resources", config_name, "Properties", "UserData") definition["Resources"][config_name]["Properties"]["UserData"][ "Fn::Base64"] = userdata return definition
def test_component_auto_scaling_group_custom_tags(): definition = {"Resources": {}} configuration = { 'Name': 'Foo', 'InstanceType': 't2.micro', 'Image': 'foo', 'Tags': [{ 'Key': 'Tag1', 'Value': 'alpha' }, { 'Key': 'Tag2', 'Value': 'beta' }] } args = MagicMock() args.region = "foo" info = {'StackName': 'FooStack', 'StackVersion': 'FooVersion'} result = component_auto_scaling_group(definition, configuration, args, info, False, MagicMock()) assert result["Resources"]["Foo"] is not None assert result["Resources"]["Foo"]["Properties"] is not None assert result["Resources"]["Foo"]["Properties"]["Tags"] is not None # verify custom tags: t1 = next(t for t in result["Resources"]["Foo"]["Properties"]["Tags"] if t["Key"] == 'Tag1') assert t1 is not None assert t1["Value"] == 'alpha' t2 = next(t for t in result["Resources"]["Foo"]["Properties"]["Tags"] if t["Key"] == 'Tag2') assert t2 is not None assert t2["Value"] == 'beta' # verify default tags are in place: ts = next(t for t in result["Resources"]["Foo"]["Properties"]["Tags"] if t["Key"] == 'Name') assert ts is not None assert ts["Value"] == 'FooStack-FooVersion'
def component_taupage_auto_scaling_group(definition, configuration, args, info, force, account_info): # inherit from the normal auto scaling group but discourage user info and replace with a Taupage config if 'Image' not in configuration: configuration['Image'] = 'LatestTaupageImage' definition = component_auto_scaling_group(definition, configuration, args, info, force, account_info) taupage_config = configuration['TaupageConfig'] if 'notify_cfn' not in taupage_config: taupage_config['notify_cfn'] = {'stack': '{}-{}'.format(info["StackName"], info["StackVersion"]), 'resource': configuration['Name']} if 'application_id' not in taupage_config: taupage_config['application_id'] = info['StackName'] if 'application_version' not in taupage_config: taupage_config['application_version'] = info['StackVersion'] check_application_id(taupage_config['application_id']) check_application_version(taupage_config['application_version']) runtime = taupage_config.get('runtime') if runtime != 'Docker': raise click.UsageError('Taupage only supports the "Docker" runtime currently') source = taupage_config.get('source') if not source: raise click.UsageError('The "source" property of TaupageConfig must be specified') docker_image = pierone.api.DockerImage.parse(source) if not force and docker_image.registry: check_docker_image_exists(docker_image) userdata = generate_user_data(taupage_config, args.region) config_name = configuration["Name"] + "Config" ensure_keys(definition, "Resources", config_name, "Properties", "UserData") definition["Resources"][config_name]["Properties"]["UserData"]["Fn::Base64"] = userdata return definition
def component_taupage_auto_scaling_group(definition, configuration, args, info, force, account_info): # inherit from the normal auto scaling group but discourage user info and replace with a Taupage config if "Image" not in configuration: configuration["Image"] = "LatestTaupageImage" definition = component_auto_scaling_group(definition, configuration, args, info, force, account_info) taupage_config = configuration["TaupageConfig"] if "notify_cfn" not in taupage_config: taupage_config["notify_cfn"] = { "stack": "{}-{}".format(info["StackName"], info["StackVersion"]), "resource": configuration["Name"], } if "application_id" not in taupage_config: taupage_config["application_id"] = info["StackName"] if "application_version" not in taupage_config: taupage_config["application_version"] = info["StackVersion"] runtime = taupage_config.get("runtime") if runtime != "Docker": raise click.UsageError('Taupage only supports the "Docker" runtime currently') source = taupage_config.get("source") if not source: raise click.UsageError('The "source" property of TaupageConfig must be specified') docker_image = pierone.api.DockerImage.parse(source) if not force and docker_image.registry: check_docker_image_exists(docker_image) userdata = generate_user_data(taupage_config) config_name = configuration["Name"] + "Config" ensure_keys(definition, "Resources", config_name, "Properties", "UserData") definition["Resources"][config_name]["Properties"]["UserData"]["Fn::Base64"] = userdata return definition
def test_resource_overrides_autoscaling_policy_with_incorrect_ref(): definition = { "Resources": { "FooScaleUp": { "Properties": { "AdjustmentType": "ChangeInCapacity", "AutoScalingGroupName": { "Ref": "NotFoo" }, "Cooldown": "180", "ScalingAdjustment": "2" }, "Type": "AWS::AutoScaling::ScalingPolicy" }, } } expected = copy.copy(definition["Resources"]["FooScaleUp"]) configuration = { 'Name': 'Foo', 'InstanceType': 't2.micro', 'Image': 'foo', 'AutoScaling': { 'Minimum': 2, 'Maximum': 10, } } args = MagicMock() args.region = "foo" info = { 'StackName': 'FooStack', 'StackVersion': 'FooVersion' } with pytest.raises(click.exceptions.UsageError): result = component_auto_scaling_group( definition, configuration, args, info, False, MagicMock())
def test_component_auto_scaling_group_optional_metric_type(): definition = {"Resources": {}} configuration = { 'Name': 'Foo', 'InstanceType': 't2.micro', 'Image': 'foo', 'AutoScaling': { 'Minimum': 2, 'Maximum': 10, } } args = MagicMock() args.region = "foo" info = {'StackName': 'FooStack', 'StackVersion': 'FooVersion'} result = component_auto_scaling_group(definition, configuration, args, info, False, MagicMock()) assert "FooCPUAlarmHigh" not in result["Resources"] assert "FooNetworkAlarmHigh" not in result["Resources"]
def test_resource_overrides_autoscaling_policy_with_incorrect_ref(): definition = { "Resources": { "FooScaleUp": { "Properties": { "AdjustmentType": "ChangeInCapacity", "AutoScalingGroupName": { "Ref": "NotFoo" }, "Cooldown": "180", "ScalingAdjustment": "2" }, "Type": "AWS::AutoScaling::ScalingPolicy" }, } } expected = copy.copy(definition["Resources"]["FooScaleUp"]) configuration = { 'Name': 'Foo', 'InstanceType': 't2.micro', 'Image': 'foo', 'AutoScaling': { 'Minimum': 2, 'Maximum': 10, } } args = MagicMock() args.region = "foo" info = {'StackName': 'FooStack', 'StackVersion': 'FooVersion'} with pytest.raises(click.exceptions.UsageError): result = component_auto_scaling_group(definition, configuration, args, info, False, MagicMock())
def test_component_auto_scaling_group_custom_tags(): definition = {"Resources": {}} configuration = { 'Name': 'Foo', 'InstanceType': 't2.micro', 'Image': 'foo', 'Tags': [ {'Key': 'Tag1', 'Value': 'alpha'}, {'Key': 'Tag2', 'Value': 'beta'} ] } args = MagicMock() args.region = "foo" info = { 'StackName': 'FooStack', 'StackVersion': 'FooVersion' } result = component_auto_scaling_group(definition, configuration, args, info, False, MagicMock()) assert result["Resources"]["Foo"] is not None assert result["Resources"]["Foo"]["Properties"] is not None assert result["Resources"]["Foo"]["Properties"]["Tags"] is not None # verify custom tags: t1 = next(t for t in result["Resources"]["Foo"]["Properties"]["Tags"] if t["Key"] == 'Tag1') assert t1 is not None assert t1["Value"] == 'alpha' t2 = next(t for t in result["Resources"]["Foo"]["Properties"]["Tags"] if t["Key"] == 'Tag2') assert t2 is not None assert t2["Value"] == 'beta' # verify default tags are in place: ts = next(t for t in result["Resources"]["Foo"]["Properties"]["Tags"] if t["Key"] == 'Name') assert ts is not None assert ts["Value"] == 'FooStack-FooVersion'
def test_component_auto_scaling_group_optional_metric_type(): definition = {"Resources": {}} configuration = { 'Name': 'Foo', 'InstanceType': 't2.micro', 'Image': 'foo', 'AutoScaling': { 'Minimum': 2, 'Maximum': 10, } } args = MagicMock() args.region = "foo" info = { 'StackName': 'FooStack', 'StackVersion': 'FooVersion' } result = component_auto_scaling_group(definition, configuration, args, info, False, MagicMock()) assert "FooCPUAlarmHigh" not in result["Resources"] assert "FooNetworkAlarmHigh" not in result["Resources"]
def test_resource_overrides_cpu_alarm(): definition = { "Resources": { "FooCPUAlarmLow": { "Properties": { "AlarmActions": [{ "Ref": "FooScaleDown" }], "AlarmDescription": "Scale-down if CPU < 30% for 10.0 minutes (Average)", "ComparisonOperator": "LessThanThreshold", "Dimensions": [{ "Name": "AutoScalingGroupName", "Value": { "Ref": "Foo" } }], "EvaluationPeriods": 10, "MetricName": "CPUUtilization", "Namespace": "AWS/EC2", "Period": 60, "Statistic": "Average", "Threshold": 30 }, "Type": "AWS::CloudWatch::Alarm" }, } } expected = copy.copy(definition["Resources"]["FooCPUAlarmLow"]) configuration = { 'Name': 'Foo', 'InstanceType': 't2.micro', 'Image': 'foo', 'AutoScaling': { 'Minimum': 2, 'Maximum': 10, 'MetricType': 'NetworkIn', 'Period': 60, 'EvaluationPeriods': 10, 'ScaleUpThreshold': '50 TB', 'ScaleDownThreshold': '10', 'Statistic': 'Maximum' } } args = MagicMock() args.region = "foo" info = {'StackName': 'FooStack', 'StackVersion': 'FooVersion'} result = component_auto_scaling_group(definition, configuration, args, info, False, MagicMock()) assert "FooCPUAlarmLow" in result["Resources"] assert result["Resources"]["FooCPUAlarmLow"] == expected
def test_resource_overrides_cpu_alarm(): definition = { "Resources": { "FooCPUAlarmLow": { "Properties": { "AlarmActions": [ { "Ref": "FooScaleDown" } ], "AlarmDescription": "Scale-down if CPU < 30% for 10.0 minutes (Average)", "ComparisonOperator": "LessThanThreshold", "Dimensions": [ { "Name": "AutoScalingGroupName", "Value": { "Ref": "Foo" } } ], "EvaluationPeriods": 10, "MetricName": "CPUUtilization", "Namespace": "AWS/EC2", "Period": 60, "Statistic": "Average", "Threshold": 30 }, "Type": "AWS::CloudWatch::Alarm" }, } } expected = copy.copy(definition["Resources"]["FooCPUAlarmLow"]) configuration = { 'Name': 'Foo', 'InstanceType': 't2.micro', 'Image': 'foo', 'AutoScaling': { 'Minimum': 2, 'Maximum': 10, 'MetricType': 'NetworkIn', 'Period': 60, 'EvaluationPeriods': 10, 'ScaleUpThreshold': '50 TB', 'ScaleDownThreshold': '10', 'Statistic': 'Maximum' } } args = MagicMock() args.region = "foo" info = { 'StackName': 'FooStack', 'StackVersion': 'FooVersion' } result = component_auto_scaling_group( definition, configuration, args, info, False, MagicMock()) assert "FooCPUAlarmLow" in result["Resources"] assert result["Resources"]["FooCPUAlarmLow"] == expected