Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Auto Scaling Group Tags #380

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import com.amazonaws.services.autoscaling.model.LaunchConfiguration
import com.amazonaws.services.autoscaling.model.ScalingPolicy
import com.amazonaws.services.autoscaling.model.ScheduledUpdateGroupAction
import com.amazonaws.services.autoscaling.model.SuspendedProcess
import com.amazonaws.services.autoscaling.model.Tag
import com.amazonaws.services.autoscaling.model.TagDescription
import com.amazonaws.services.cloudwatch.model.MetricAlarm
import com.amazonaws.services.ec2.model.AvailabilityZone
import com.amazonaws.services.ec2.model.Image
Expand Down Expand Up @@ -169,6 +171,10 @@ class AutoScalingController {
} as Map
String clusterName = Relationships.clusterFromGroupName(name)
boolean isChaosMonkeyActive = cloudReadyService.isChaosMonkeyActive(userContext.region)

//Grab tag data and set for display
List<TagDescription> tags = group.getTags()

def details = [
instanceCount: instanceCount,
showPostponeButton: showPostponeButton,
Expand All @@ -193,7 +199,8 @@ class AutoScalingController {
subnetPurpose: subnetPurpose ?: null,
vpcZoneIdentifier: group.VPCZoneIdentifier,
isChaosMonkeyActive: isChaosMonkeyActive,
chaosMonkeyEditLink: cloudReadyService.constructChaosMonkeyEditLink(userContext.region, appName)
chaosMonkeyEditLink: cloudReadyService.constructChaosMonkeyEditLink(userContext.region, appName),
tags: tags
]
withFormat {
html { return details }
Expand Down Expand Up @@ -254,6 +261,26 @@ class AutoScalingController {
'Chaos Monkey settings directly in Cloudready after ASG creation.'
}
}
// Auto Scaling Group Tags
List<Tag> asgTags = []

if (params.tags) {
Map tags = [:]

// The tags get funky when passed by the save chain
params.entrySet().findAll {
it.key.startsWith('tags.value')
}.each {
tags.put(it.key.tokenize('.')[2], it.value)
}

if (tags.size() > 0){
tags.each { key, value ->
Tag t = new Tag(key:key, value:value, propagateAtLaunch:params['tags.props.' + key] == 'on' ? true:false)
asgTags.add(t)
}
}
}
[
applications: applicationService.getRegisteredApplications(userContext),
group: group,
Expand All @@ -277,7 +304,8 @@ class AutoScalingController {
iamInstanceProfile: configService.defaultIamRole,
spotUrl: configService.spotUrl,
isChaosMonkeyActive: cloudReadyService.isChaosMonkeyActive(userContext.region),
appsWithClusterOptLevel: appsWithClusterOptLevel ?: []
appsWithClusterOptLevel: appsWithClusterOptLevel ?: [],
tags: asgTags
]
}

Expand All @@ -296,6 +324,16 @@ class AutoScalingController {
String subnetPurpose = params.subnetPurpose ?: null
String vpcId = subnets.getVpcIdForSubnetPurpose(subnetPurpose) ?: ''

// Auto Scaling Group Tags
List<Tag> asgTags = []

if (params.tags) {
params.tags.value.each { key, value ->
Tag t = new Tag(key:key, value:value, propagateAtLaunch:params['tags.props.' + key] == 'on' ? true:false, resourceId:groupName, resourceType:"auto-scaling-group")
asgTags.add(t)
}
}

// Auto Scaling Group
Integer minSize = (params.min ?: 0) as Integer
Integer desiredCapacity = (params.desiredCapacity ?: 0) as Integer
Expand All @@ -313,7 +351,7 @@ class AutoScalingController {
withMinSize(minSize).withDesiredCapacity(desiredCapacity).
withMaxSize(maxSize).withDefaultCooldown(defaultCooldown).
withHealthCheckType(healthCheckType).withHealthCheckGracePeriod(healthCheckGracePeriod).
withTerminationPolicies(terminationPolicies)
withTerminationPolicies(terminationPolicies).withTags(asgTags)

// If this ASG lauches VPC instances, we must find the proper subnets and add them.
if (subnetPurpose) {
Expand Down Expand Up @@ -387,6 +425,7 @@ class AutoScalingController {
addToLoadBalancerSuspended: group?.isProcessSuspended(AutoScalingProcessType.AddToLoadBalancer),
manualStaticSizingNeeded: manualStaticSizingNeeded,
vpcZoneIdentifier: group.VPCZoneIdentifier,
tags: group.tags,
]
}

Expand Down Expand Up @@ -429,6 +468,31 @@ class AutoScalingController {
resumeProcesses << processType
}
}
List<Tag> tags = []

if (params.tags) {
params.tags.value.each { key, value ->
Tag t = new Tag(key:key, value:value, propagateAtLaunch:params['tags.props.' + key] == 'on' ? true:false, resourceId:name, resourceType:"auto-scaling-group")
tags.add(t)
}

if (tags.size() > 0){
awsAutoScalingService.updateTags(userContext, tags, name)
}

tags = []
params.tags.delete.each { key, value ->
if (value == 'on'){
Tag t = new Tag(key:key, value:params['tags.values.' + key], propagateAtLaunch:params['tags.props.' + key] == 'on' ? true:false, resourceId:name, resourceType:"auto-scaling-group")
tags.add(t)
}
}

if (tags.size() > 0){
awsAutoScalingService.deleteTags(userContext, tags, name)
}
}

final AutoScalingGroupData autoScalingGroupData = AutoScalingGroupData.forUpdate(
name, lcName, minSize, desiredCapacity, maxSize, defaultCooldown, healthCheckType,
healthCheckGracePeriod, terminationPolicies, availabilityZones
Expand Down
26 changes: 24 additions & 2 deletions grails-app/controllers/com/netflix/asgard/ClusterController.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package com.netflix.asgard
import com.amazonaws.services.autoscaling.model.AutoScalingGroup
import com.amazonaws.services.autoscaling.model.LaunchConfiguration
import com.amazonaws.services.autoscaling.model.ScheduledUpdateGroupAction
import com.amazonaws.services.autoscaling.model.Tag
import com.amazonaws.services.autoscaling.model.TagDescription
import com.amazonaws.services.ec2.model.AvailabilityZone
import com.amazonaws.services.elasticloadbalancing.model.LoadBalancerDescription
import com.amazonaws.services.simpleworkflow.flow.ManualActivityCompletionClient
Expand Down Expand Up @@ -145,6 +147,7 @@ ${lastGroup.loadBalancerNames}"""
SubnetTarget.EC2).sort()
Map<String, Collection<String>> zonesByPurpose = subnets.groupZonesByPurpose(
availabilityZones*.zoneName, SubnetTarget.EC2)
List<TagDescription> tags = lastGroup.getTags()
attributes.putAll([
cluster: cluster,
runningTasks: runningTasks,
Expand All @@ -161,7 +164,8 @@ ${lastGroup.loadBalancerNames}"""
loadBalancersGroupedByVpcId: loadBalancers.groupBy { it.VPCId },
selectedLoadBalancers: selectedLoadBalancers,
spotUrl: configService.spotUrl,
pricing: params.pricing ?: attributes.pricing
pricing: params.pricing ?: attributes.pricing,
tags:tags
])
attributes
}
Expand Down Expand Up @@ -429,13 +433,19 @@ ${loadBalancerNames}"""
Group: ${lastGroup.loadBalancerNames}"""
boolean ebsOptimized = params.containsKey('ebsOptimized') ? params.ebsOptimized?.toBoolean() :
lastLaunchConfig.ebsOptimized

List<Tag> tags = convertTags(nextGroupName)

if (params.noOptionalDefaults != 'true') {
securityGroups = securityGroups ?: lastLaunchConfig.securityGroups
termPolicies = termPolicies ?: lastGroup.terminationPolicies
loadBalancerNames = loadBalancerNames ?: lastGroup.loadBalancerNames
vpcZoneIdentifier = vpcZoneIdentifier ?: subnets.constructNewVpcZoneIdentifierForZones(
lastGroup.vpcZoneIdentifier, selectedZones)
tags = lastGroup.tags
}


log.debug """ClusterController.createNextGroup for Cluster '${cluster.name}' Load Balancers for next \
Group: ${loadBalancerNames}"""
GroupCreateOptions options = new GroupCreateOptions(
Expand Down Expand Up @@ -469,13 +479,25 @@ Group: ${loadBalancerNames}"""
scheduledActions: newScheduledActions,
vpcZoneIdentifier: vpcZoneIdentifier,
spotPrice: spotPrice,
ebsOptimized: ebsOptimized
ebsOptimized: ebsOptimized,
tags: tags
)
def operation = pushService.startGroupCreate(options)
flash.message = "${operation.task.name} has been started."
redirectToTask(operation.taskId)
}
}

private List<Tag> convertTags(String nextGroupName){
List<Tag> tags = []
if (params.tags) {
params.tags.value.each { key, value ->
Tag t = new Tag(key:key, value:value, propagateAtLaunch:params['tags.props.' + key] == 'on' ? true:false, resourceId:nextGroupName, resourceType:"auto-scaling-group")
tags.add(t)
}
}
tags
}

private int convertToIntOrUseDefault(String value, Integer defaultValue) {
value?.toInteger() ?: defaultValue
Expand Down
59 changes: 32 additions & 27 deletions grails-app/services/com/netflix/asgard/AwsAutoScalingService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import com.amazonaws.services.autoscaling.model.Alarm
import com.amazonaws.services.autoscaling.model.AutoScalingGroup
import com.amazonaws.services.autoscaling.model.BlockDeviceMapping
import com.amazonaws.services.autoscaling.model.CreateAutoScalingGroupRequest
import com.amazonaws.services.autoscaling.model.CreateOrUpdateTagsRequest
import com.amazonaws.services.autoscaling.model.DeleteAutoScalingGroupRequest
import com.amazonaws.services.autoscaling.model.DeleteLaunchConfigurationRequest
import com.amazonaws.services.autoscaling.model.DeletePolicyRequest
import com.amazonaws.services.autoscaling.model.DeleteScheduledActionRequest
import com.amazonaws.services.autoscaling.model.DeleteTagsRequest
import com.amazonaws.services.autoscaling.model.DescribeAutoScalingGroupsRequest
import com.amazonaws.services.autoscaling.model.DescribeAutoScalingGroupsResult
import com.amazonaws.services.autoscaling.model.DescribeLaunchConfigurationsRequest
Expand All @@ -47,6 +49,7 @@ import com.amazonaws.services.autoscaling.model.ResumeProcessesRequest
import com.amazonaws.services.autoscaling.model.ScalingPolicy
import com.amazonaws.services.autoscaling.model.ScheduledUpdateGroupAction
import com.amazonaws.services.autoscaling.model.SuspendProcessesRequest
import com.amazonaws.services.autoscaling.model.Tag
import com.amazonaws.services.autoscaling.model.TerminateInstanceInAutoScalingGroupRequest
import com.amazonaws.services.autoscaling.model.UpdateAutoScalingGroupRequest
import com.amazonaws.services.cloudwatch.model.MetricAlarm
Expand Down Expand Up @@ -906,8 +909,9 @@ class AwsAutoScalingService implements CacheInitializer, InitializingBean {

void setExpirationTime(UserContext userContext, String autoScalingGroupName, DateTime expirationTime,
Task existingTask = null) {
Map<String, String> tagNameValuePairs = [(TagNames.EXPIRATION_TIME): Time.format(expirationTime)]
createOrUpdateAutoScalingGroupTags(userContext, autoScalingGroupName, tagNameValuePairs, existingTask)
List<Tag> tags = []
tags.add(new Tag(key:TagNames.EXPIRATION_TIME, value:Time.format(expirationTime), propagateAtLaunch:false, resourceId:autoScalingGroupName, resourceType:"auto-scaling-group"))
updateTags(userContext, tags, autoScalingGroupName, existingTask)
}

void postponeExpirationTime(UserContext userContext, String autoScalingGroupName, Duration extraTime,
Expand All @@ -927,47 +931,48 @@ class AwsAutoScalingService implements CacheInitializer, InitializingBean {
}

void removeExpirationTime(UserContext userContext, String autoScalingGroupName, Task existingTask = null) {
deleteAutoScalingGroupTags(userContext, autoScalingGroupName, [TagNames.EXPIRATION_TIME], existingTask)
List<Tag> tags = []
tags.add(new Tag(key:TagNames.EXPIRATION_TIME, resourceId:autoScalingGroupName, resourceType:"auto-scaling-group"))
deleteTags(userContext, tags, autoScalingGroupName, existingTask)
}

void createOrUpdateAutoScalingGroupTags(UserContext userContext, String autoScalingGroupName, Map<String,
String> tagNameValuePairs, Task existingTask = null) {

// TODO: Re-enable this call after Amazon fixes bugs on their side and tell us it's safe again

/*

// Hopefully Amazon will eventually change CreateOrUpdateTagsRequest to take List<Tag> instead of List<String>
List<String> tagStringsEqualDelimited = tagNameValuePairs.collect { "${it.key}=${it.value}".toString() }

String suffix = tagNameValuePairs.size() == 1 ? '' : 's'
String msg = "Create tag${suffix} ${tagNameValuePairs} on Auto Scaling Group on '${autoScalingGroupName}'"
taskService.runTask(userContext, msg, { Task task ->
CreateOrUpdateTagsRequest request = new CreateOrUpdateTagsRequest(autoScalingGroupName: autoScalingGroupName,
forceOverwriteTags: true, propagate: true, tags: tagStringsEqualDelimited)
CreateOrUpdateTagsRequest cr = new CreateOrUpdateTagsRequest()
cr.setTags(tagStringsEqualDelimited)

awsClient.by(userContext.region).createOrUpdateTags(request)
}, Link.to(EntityType.autoScaling, autoScalingGroupName), existingTask)

*/
}

void deleteAutoScalingGroupTags(UserContext userContext, String autoScalingGroupName, List<String> tagNames,
Task existingTask = null) {

// TODO: Re-enable this call after Amazon fixes bugs on their side and tell us it's safe again

/*

String suffix = tagNames.size() == 1 ? '' : 's'
String msg = "Delete tag${suffix} ${tagNames} on Auto Scaling Group on '${autoScalingGroupName}'"
taskService.runTask(userContext, msg, { Task task ->
DeleteTagsRequest request = new DeleteTagsRequest(autoScalingGroupName: autoScalingGroupName,
tagsToDelete: tagNames)
awsClient.by(userContext.region).deleteTags(request)
}, Link.to(EntityType.autoScaling, autoScalingGroupName), existingTask)

*/
}

}
void updateTags(UserContext userContext, List<Tag> tags, String autoScalingGroupName, Task existingTask = null){
String msg = "Create tags on Auto Scaling Group on '${autoScalingGroupName}'"
taskService.runTask(userContext, msg, {Task task ->
CreateOrUpdateTagsRequest request = new CreateOrUpdateTagsRequest()
request.setTags(tags)
awsClient.by(userContext.region).createOrUpdateTags(request)
}, Link.to(EntityType.autoScaling, autoScalingGroupName), existingTask)
}
void deleteTags(UserContext userContext, List<Tag> tags, String autoScalingGroupName, Task existingTask = null){
String msg = "Delete tags on Auto Scaling Group on '${autoScalingGroupName}'"
taskService.runTask(userContext, msg, {Task task ->
DeleteTagsRequest request = new DeleteTagsRequest()
request.setTags(tags)
awsClient.by(userContext.region).deleteTags(request)
}, Link.to(EntityType.autoScaling, autoScalingGroupName), existingTask)
}

void deleteAutoScalingGroup(UserContext userContext, String name, AsgDeletionMode mode = AsgDeletionMode.ATTEMPT,
Task existingTask = null) {
Expand Down
3 changes: 2 additions & 1 deletion grails-app/services/com/netflix/asgard/JokeService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ class JokeService {
'Something sank your battleship', 'Blame the dog', 'Blame the cat', 'The code broke',
"That wasn't supposed to happen", 'Not enough test coverage', "Let's blame Microsoft",
"Let's blame Oracle", 'I sense a disruption in the force', "Don't blame yourself",
'An interaction between man and machine has failed today'].asImmutable()
'An interaction between man and machine has failed today',
'The Baboon is LOOSE!', 'Your job... It\'s over!'].asImmutable()

private final List<FailureImage> failureImages = ImageAttributions.FAILURE_IMAGES

Expand Down
3 changes: 3 additions & 0 deletions grails-app/services/com/netflix/asgard/PushService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package com.netflix.asgard

import com.amazonaws.services.autoscaling.model.AutoScalingGroup
import com.amazonaws.services.autoscaling.model.LaunchConfiguration
import com.amazonaws.services.autoscaling.model.TagDescription
import com.amazonaws.services.ec2.model.Image
import com.amazonaws.services.ec2.model.SecurityGroup
import com.netflix.asgard.model.InstancePriceType
Expand Down Expand Up @@ -133,6 +134,7 @@ class PushService {
if (!group) {
throw new NoSuchObjectException("Auto scaling group '${name}' not found")
}
List<TagDescription> tags = group.getTags()
Integer relaunchCount = group.instances.size()
LaunchConfiguration lc = awsAutoScalingService.getLaunchConfiguration(userContext,
group.launchConfigurationName)
Expand All @@ -154,6 +156,7 @@ class PushService {
appName: appName,
name: name,
cluster: Relationships.clusterFromGroupName(name),
tags: tags,
variables: Relationships.parts(name),
actionName: actionName,
allTerminationPolicies: awsAutoScalingService.terminationPolicyTypes,
Expand Down
5 changes: 3 additions & 2 deletions grails-app/views/autoScaling/create.gsp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
<g:render template="/loadBalancer/selection"/>
</tbody>
<g:render template="/launchConfiguration/launchConfigOptions" />
<g:render template="/common/editTags" model="[entity: tags]" />
<tbody class="clusterChaosMonkeyOptions ${params.appName in appsWithClusterOptLevel ? '' : 'concealed'}">
<g:render template="/common/chaosMonkeyOptions" />
<tbody>
Expand All @@ -56,8 +57,8 @@
<g:each var="app" in="${appsWithClusterOptLevel}">
<li>${app}</li>
</g:each>
</ul>
</div>
</ul>
</div>
<div class="buttons">
<g:buttonSubmit class="save" action="save">Create New Auto Scaling Group</g:buttonSubmit>
</div>
Expand Down
Loading