diff --git a/grails-app/controllers/com/netflix/asgard/AutoScalingController.groovy b/grails-app/controllers/com/netflix/asgard/AutoScalingController.groovy index 5649ad61..503600bd 100644 --- a/grails-app/controllers/com/netflix/asgard/AutoScalingController.groovy +++ b/grails-app/controllers/com/netflix/asgard/AutoScalingController.groovy @@ -294,7 +294,7 @@ class AutoScalingController { String groupName = Relationships.buildGroupName(params) Subnets subnets = awsEc2Service.getSubnets(userContext) String subnetPurpose = params.subnetPurpose ?: null - String vpcId = subnets.getVpcIdForSubnetPurpose(subnetPurpose) ?: '' + String vpcId = subnets.getVpcIdForSubnetPurpose(subnetPurpose) ?: '' // Auto Scaling Group Integer minSize = (params.min ?: 0) as Integer @@ -336,10 +336,11 @@ class AutoScalingController { String ramdiskId = params.ramdiskId ?: null String iamInstanceProfile = params.iamInstanceProfile ?: configService.defaultIamRole boolean ebsOptimized = params.ebsOptimized?.toBoolean() + boolean associatePublicIpAddress = params.associatePublicIpAddress ?: false LaunchConfiguration launchConfigTemplate = new LaunchConfiguration().withImageId(imageId). withKernelId(kernelId).withInstanceType(instType).withKeyName(keyName).withRamdiskId(ramdiskId). withSecurityGroups(securityGroups).withIamInstanceProfile(iamInstanceProfile). - withEbsOptimized(ebsOptimized) + withEbsOptimized(ebsOptimized).withAssociatePublicIpAddress(associatePublicIpAddress) if (params.pricing == InstancePriceType.SPOT.name()) { launchConfigTemplate.spotPrice = spotInstanceRequestService.recommendSpotPrice(userContext, instType) } diff --git a/grails-app/controllers/com/netflix/asgard/ClusterController.groovy b/grails-app/controllers/com/netflix/asgard/ClusterController.groovy index 03099278..10036167 100644 --- a/grails-app/controllers/com/netflix/asgard/ClusterController.groovy +++ b/grails-app/controllers/com/netflix/asgard/ClusterController.groovy @@ -116,6 +116,12 @@ class ClusterController { withFormat { html { AutoScalingGroupData lastGroup = cluster.last() + LaunchConfiguration lastLaunchConfig = awsAutoScalingService. + getLaunchConfiguration(userContext, lastGroup.launchConfigurationName, From.CACHE) + boolean associatePublicIpAddress = false + if (lastLaunchConfig) { + associatePublicIpAddress = lastLaunchConfig.isAssociatePublicIpAddress() + } String nextGroupName = Relationships.buildNextAutoScalingGroupName(lastGroup.autoScalingGroupName) Boolean okayToCreateGroup = cluster.size() < Relationships.CLUSTER_MAX_GROUPS String recommendedNextStep = cluster.size() >= Relationships.CLUSTER_MAX_GROUPS ? @@ -161,7 +167,9 @@ ${lastGroup.loadBalancerNames}""" loadBalancersGroupedByVpcId: loadBalancers.groupBy { it.VPCId }, selectedLoadBalancers: selectedLoadBalancers, spotUrl: configService.spotUrl, - pricing: params.pricing ?: attributes.pricing + associatePublicIpAddress: lastLaunchConfig.getAssociatePublicIpAddress(), + pricing: params.pricing ?: attributes.pricing, + ]) attributes } @@ -430,6 +438,8 @@ ${loadBalancerNames}""" Group: ${lastGroup.loadBalancerNames}""" boolean ebsOptimized = params.containsKey('ebsOptimized') ? params.ebsOptimized?.toBoolean() : lastLaunchConfig.ebsOptimized + boolean associatePublicIpAddress = params.containsKey('associatePublicIpAddress') ? params.associatePublicIpAddress?.toBoolean() : + lastLaunchConfig.associatePublicIpAddress if (params.noOptionalDefaults != 'true') { securityGroups = securityGroups ?: lastLaunchConfig.securityGroups termPolicies = termPolicies ?: lastGroup.terminationPolicies @@ -440,7 +450,7 @@ Group: ${lastGroup.loadBalancerNames}""" log.debug """ClusterController.createNextGroup for Cluster '${cluster.name}' Load Balancers for next \ Group: ${loadBalancerNames}""" GroupCreateOptions options = new GroupCreateOptions( - common: new CommonPushOptions( + common: new CommonPushOptions( userContext: userContext, checkHealth: checkHealth, afterBootWait: convertToIntOrUseDefault(params.afterBootWait, 30), @@ -450,7 +460,7 @@ Group: ${loadBalancerNames}""" instanceType: instanceType, groupName: nextGroupName, securityGroups: securityGroups, - maxStartupRetries: convertToIntOrUseDefault(params.maxStartupRetries, 5) + maxStartupRetries: convertToIntOrUseDefault(params.maxStartupRetries, 5) ), initialTraffic: initialTraffic, minSize: minSize, @@ -470,7 +480,8 @@ Group: ${loadBalancerNames}""" scheduledActions: newScheduledActions, vpcZoneIdentifier: vpcZoneIdentifier, spotPrice: spotPrice, - ebsOptimized: ebsOptimized + ebsOptimized: ebsOptimized, + associatePublicIpAddress: associatePublicIpAddress ) def operation = pushService.startGroupCreate(options) flash.message = "${operation.task.name} has been started." diff --git a/grails-app/views/launchConfiguration/_launchConfigOptions.gsp b/grails-app/views/launchConfiguration/_launchConfigOptions.gsp index 6a6c8f03..a28f986d 100644 --- a/grails-app/views/launchConfiguration/_launchConfigOptions.gsp +++ b/grails-app/views/launchConfiguration/_launchConfigOptions.gsp @@ -75,6 +75,14 @@ + + + + + + checked /> + + diff --git a/grails-app/views/launchConfiguration/_launchTemplateFields.gsp b/grails-app/views/launchConfiguration/_launchTemplateFields.gsp index 70a10d77..e902fbe3 100644 --- a/grails-app/views/launchConfiguration/_launchTemplateFields.gsp +++ b/grails-app/views/launchConfiguration/_launchTemplateFields.gsp @@ -44,6 +44,10 @@ Instance Type: ${launchTemplate.instanceType} + + VPC Associate Public IP's: + ${launchTemplate.associatePublicIpAddress} + Kernel ID: diff --git a/src/groovy/com/netflix/asgard/model/LaunchConfigurationBeanOptions.groovy b/src/groovy/com/netflix/asgard/model/LaunchConfigurationBeanOptions.groovy index a08111c5..2caede99 100644 --- a/src/groovy/com/netflix/asgard/model/LaunchConfigurationBeanOptions.groovy +++ b/src/groovy/com/netflix/asgard/model/LaunchConfigurationBeanOptions.groovy @@ -68,6 +68,9 @@ import groovy.transform.Canonical /** @see LaunchConfiguration#ebsOptimized */ Boolean ebsOptimized + + /** @see LaunchConfiguration#associatePublicIpAddress */ + Boolean associatePublicIpAddress void setSecurityGroups(Collection securityGroups) { this.securityGroups = copyNonNullToSet(securityGroups) @@ -95,6 +98,7 @@ import groovy.transform.Canonical static LaunchConfigurationBeanOptions from(LaunchConfigurationBeanOptions source) { new LaunchConfigurationBeanOptions( launchConfigurationName: source.launchConfigurationName, + associatePublicIpAddress: source.associatePublicIpAddress, imageId: source.imageId, keyName: source.keyName, securityGroups: copyNonNullToSet(source.securityGroups), @@ -120,6 +124,7 @@ import groovy.transform.Canonical launchConfiguration.with { new LaunchConfigurationBeanOptions( launchConfigurationName: launchConfigurationName, + associatePublicIpAddress: associatePublicIpAddress, imageId: imageId, keyName: keyName, securityGroups: copyNonNullToSet(securityGroups), @@ -151,6 +156,7 @@ import groovy.transform.Canonical } new CreateLaunchConfigurationRequest( launchConfigurationName: launchConfigurationName, + associatePublicIpAddress: associatePublicIpAddress, imageId: imageId, keyName: keyName, securityGroups: copyNonNullToSet(securityGroups), diff --git a/src/groovy/com/netflix/asgard/push/GroupCreateOperation.groovy b/src/groovy/com/netflix/asgard/push/GroupCreateOperation.groovy index 748423ac..39ab77b0 100644 --- a/src/groovy/com/netflix/asgard/push/GroupCreateOperation.groovy +++ b/src/groovy/com/netflix/asgard/push/GroupCreateOperation.groovy @@ -81,7 +81,8 @@ class GroupCreateOperation extends AbstractPushOperation { withKeyName(options.keyName).withRamdiskId(options.ramdiskId). withSecurityGroups(options.common.securityGroups). withIamInstanceProfile(options.iamInstanceProfile). - withSpotPrice(options.spotPrice).withEbsOptimized(options.ebsOptimized) + withSpotPrice(options.spotPrice).withEbsOptimized(options.ebsOptimized). + withAssociatePublicIpAddress(options.associatePublicIpAddress) final Collection suspendedProcesses = Sets.newHashSet() if (options.zoneRebalancingSuspended) { diff --git a/src/groovy/com/netflix/asgard/push/GroupCreateOptions.groovy b/src/groovy/com/netflix/asgard/push/GroupCreateOptions.groovy index 1ba5fa21..7f8b647e 100644 --- a/src/groovy/com/netflix/asgard/push/GroupCreateOptions.groovy +++ b/src/groovy/com/netflix/asgard/push/GroupCreateOptions.groovy @@ -36,6 +36,7 @@ import groovy.transform.Immutable Collection scalingPolicies Collection scheduledActions String spotPrice + boolean associatePublicIpAddress boolean ebsOptimized /** The number of instances to create at a time while inflating the auto scaling group. */ diff --git a/test/unit/com/netflix/asgard/AwsAutoScalingServiceIntegrationSpec.groovy b/test/unit/com/netflix/asgard/AwsAutoScalingServiceIntegrationSpec.groovy index 6e8c52a9..e862fa7e 100644 --- a/test/unit/com/netflix/asgard/AwsAutoScalingServiceIntegrationSpec.groovy +++ b/test/unit/com/netflix/asgard/AwsAutoScalingServiceIntegrationSpec.groovy @@ -142,7 +142,7 @@ class AwsAutoScalingServiceIntegrationSpec extends Specification { withMaxSize(0).withMinSize(0).withDefaultCooldown(0) final LaunchConfiguration launchConfigTemplate = new LaunchConfiguration().withImageId('ami-deadbeef'). withInstanceType('m1.small').withKeyName('keyName').withSecurityGroups([]).withUserData(''). - withEbsOptimized(false) + withEbsOptimized(false).withAssociatePublicIpAddress(true) when: final CreateAutoScalingGroupResult result = awsAutoScalingService.createLaunchConfigAndAutoScalingGroup( @@ -155,7 +155,7 @@ class AwsAutoScalingServiceIntegrationSpec extends Specification { 'helloworld-example' == result.autoScalingGroupName result.launchConfigName =~ /helloworld-example-20[0-9]{12}/ !result.launchConfigDeleted - result.launchConfigCreated + result.launchConfigCreated result.autoScalingGroupCreated result.toString() =~ "Launch Config 'helloworld-example-20[0-9]{12}' has been created. Auto Scaling Group" + " 'helloworld-example' has been created. " diff --git a/test/unit/com/netflix/asgard/model/LaunchConfigurationBeanOptionsSpec.groovy b/test/unit/com/netflix/asgard/model/LaunchConfigurationBeanOptionsSpec.groovy index a5dbedb3..c1d84bc9 100644 --- a/test/unit/com/netflix/asgard/model/LaunchConfigurationBeanOptionsSpec.groovy +++ b/test/unit/com/netflix/asgard/model/LaunchConfigurationBeanOptionsSpec.groovy @@ -37,7 +37,8 @@ class LaunchConfigurationBeanOptionsSpec extends Specification { instanceMonitoring: null, instancePriceType: InstancePriceType.ON_DEMAND, iamInstanceProfile: 'iamInstanceProfile1', - ebsOptimized: false + ebsOptimized: false, + associatePublicIpAddress: true ) LaunchConfiguration awsLaunchConfiguration = new LaunchConfiguration( @@ -52,7 +53,8 @@ class LaunchConfigurationBeanOptionsSpec extends Specification { blockDeviceMappings: [new BlockDeviceMapping(deviceName: 'deviceName1', ebs: new Ebs(volumeSize: 256))], instanceMonitoring: null, iamInstanceProfile: 'iamInstanceProfile1', - ebsOptimized: false + ebsOptimized: false, + associatePublicIpAddress: true ) CreateLaunchConfigurationRequest createLaunchConfigurationRequest = new CreateLaunchConfigurationRequest( launchConfigurationName: 'launchConfigurationName1', @@ -66,7 +68,8 @@ class LaunchConfigurationBeanOptionsSpec extends Specification { blockDeviceMappings: [new BlockDeviceMapping(deviceName: 'deviceName1', ebs: new Ebs(volumeSize: 256))], instanceMonitoring: null, iamInstanceProfile: 'iamInstanceProfile1', - ebsOptimized: false + ebsOptimized: false, + associatePublicIpAddress: true ) def 'should deep copy'() {