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

feat(ecs): Copy scaling policies from source server group #3238

Merged
merged 7 commits into from
Jan 17, 2019
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ public class CreateServerGroupDescription extends AbstractECSDescription {

Map<String, List<String>> availabilityZones;

List<MetricAlarm> autoscalingPolicies;
boolean copySourceScalingPoliciesAndActions = true;
Source source = new Source();

List<PlacementStrategy> placementStrategySequence;
String networkMode;
String subnetType;
Expand All @@ -66,4 +68,13 @@ public String getRegion() {
//CreateServerGroupDescription does not contain a region. Instead it has AvailabilityZones
return getAvailabilityZones().keySet().iterator().next();
}

@Data
@EqualsAndHashCode(callSuper = false)
public static class Source {
String account;
String region;
String asgName;
Boolean useSourceCapacity;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@

import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.services.applicationautoscaling.AWSApplicationAutoScaling;
import com.amazonaws.services.applicationautoscaling.model.RegisterScalableTargetRequest;
import com.amazonaws.services.applicationautoscaling.model.ScalableDimension;
import com.amazonaws.services.applicationautoscaling.model.ServiceNamespace;
import com.amazonaws.services.cloudwatch.model.MetricAlarm;
import com.amazonaws.services.applicationautoscaling.model.*;
import com.amazonaws.services.ecs.AmazonECS;
import com.amazonaws.services.ecs.model.*;
import com.amazonaws.services.elasticloadbalancingv2.AmazonElasticLoadBalancing;
Expand All @@ -47,13 +44,7 @@
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;

public class CreateServerGroupAtomicOperation extends AbstractEcsAtomicOperation<CreateServerGroupDescription, DeploymentResult> {
Expand Down Expand Up @@ -96,21 +87,32 @@ public DeploymentResult operate(List priorOutputs) {
String newServerGroupName = serverGroupNameResolver.resolveNextServerGroupName(description.getApplication(),
description.getStack(), description.getFreeFormDetails(), false);

ScalableTarget sourceTarget = getSourceScalableTarget();
Service sourceService = getSourceService();

String ecsServiceRole = inferAssumedRoleArn(credentials);

updateTaskStatus("Creating Amazon ECS Task Definition...");
TaskDefinition taskDefinition = registerTaskDefinition(ecs, ecsServiceRole, newServerGroupName);
updateTaskStatus("Done creating Amazon ECS Task Definition...");

Service service = createService(ecs, taskDefinition, ecsServiceRole, newServerGroupName);

String resourceId = registerAutoScalingGroup(credentials, service);

if (!description.getAutoscalingPolicies().isEmpty()) {
List<String> alarmNames = description.getAutoscalingPolicies().stream()
.map(MetricAlarm::getAlarmName)
.collect(Collectors.toList());
ecsCloudMetricService.associateAsgWithMetrics(description.getCredentialAccount(), getRegion(), alarmNames, service.getServiceName(), resourceId);
Service service = createService(ecs, taskDefinition, ecsServiceRole, newServerGroupName, sourceService);

String resourceId = registerAutoScalingGroup(credentials, service, sourceTarget);

if (description.isCopySourceScalingPoliciesAndActions() && sourceTarget != null) {
updateTaskStatus("Copying scaling policies...");
ecsCloudMetricService.copyScalingPolicies(
description.getCredentialAccount(),
getRegion(),
service.getServiceName(),
resourceId,
description.getSource().getAccount(),
description.getSource().getRegion(),
description.getSource().getAsgName(),
sourceTarget.getResourceId(),
description.getEcsClusterName());
updateTaskStatus("Done copying scaling policies...");
}

return makeDeploymentResult(service);
Expand Down Expand Up @@ -224,11 +226,18 @@ protected RegisterTaskDefinitionRequest makeTaskDefinitionRequest(String ecsServ
}

private Service createService(AmazonECS ecs, TaskDefinition taskDefinition, String ecsServiceRole,
String newServerGroupName) {
String newServerGroupName, Service sourceService) {
Collection<LoadBalancer> loadBalancers = new LinkedList<>();
loadBalancers.add(retrieveLoadBalancer(EcsServerGroupNameResolver.getEcsContainerName(newServerGroupName)));

Integer desiredCount = description.getCapacity().getDesired();
if (sourceService != null &&
description.getSource() != null &&
description.getSource().getUseSourceCapacity() != null &&
description.getSource().getUseSourceCapacity()) {
desiredCount = sourceService.getDesiredCount();
}

String taskDefinitionArn = taskDefinition.getTaskDefinitionArn();

DeploymentConfiguration deploymentConfiguration = new DeploymentConfiguration()
Expand Down Expand Up @@ -288,18 +297,30 @@ private Service createService(AmazonECS ecs, TaskDefinition taskDefinition, Stri
}

private String registerAutoScalingGroup(AmazonCredentials credentials,
Service service) {
Service service,
ScalableTarget sourceTarget) {

AWSApplicationAutoScaling autoScalingClient = getAmazonApplicationAutoScalingClient();
String assumedRoleArn = inferAssumedRoleArn(credentials);

Integer min = description.getCapacity().getMin();
Integer max = description.getCapacity().getMax();

if (sourceTarget != null &&
description.getSource() != null &&
description.getSource().getUseSourceCapacity() != null &&
description.getSource().getUseSourceCapacity()) {
min = sourceTarget.getMinCapacity();
max = sourceTarget.getMaxCapacity();
}

RegisterScalableTargetRequest request = new RegisterScalableTargetRequest()
.withServiceNamespace(ServiceNamespace.Ecs)
.withScalableDimension(ScalableDimension.EcsServiceDesiredCount)
.withResourceId(String.format("service/%s/%s", description.getEcsClusterName(), service.getServiceName()))
.withRoleARN(assumedRoleArn)
.withMinCapacity(description.getCapacity().getMin())
.withMaxCapacity(description.getCapacity().getMax());
.withMinCapacity(min)
.withMaxCapacity(max);

updateTaskStatus("Creating Amazon Application Auto Scaling Scalable Target Definition...");
autoScalingClient.registerScalableTarget(request);
Expand All @@ -308,6 +329,53 @@ private String registerAutoScalingGroup(AmazonCredentials credentials,
return request.getResourceId();
}

private ScalableTarget getSourceScalableTarget() {
if (description.getSource() != null
&& description.getSource().getRegion() != null
&& description.getSource().getAccount() != null
&& description.getSource().getAsgName() != null) {

AWSApplicationAutoScaling autoScalingClient = getSourceAmazonApplicationAutoScalingClient();

DescribeScalableTargetsRequest request = new DescribeScalableTargetsRequest()
.withServiceNamespace(ServiceNamespace.Ecs)
.withScalableDimension(ScalableDimension.EcsServiceDesiredCount)
.withResourceIds(String.format("service/%s/%s", description.getEcsClusterName(), description.getSource().getAsgName()));

DescribeScalableTargetsResult result = autoScalingClient.describeScalableTargets(request);
if (result.getScalableTargets() != null && !result.getScalableTargets().isEmpty()) {
return result.getScalableTargets().get(0);
}

return null;
}

return null;
}

private Service getSourceService() {
if (description.getSource() != null
&& description.getSource().getRegion() != null
&& description.getSource().getAccount() != null
&& description.getSource().getAsgName() != null) {

AmazonECS ecsClient = getSourceAmazonEcsClient();

DescribeServicesRequest request = new DescribeServicesRequest()
.withCluster(description.getEcsClusterName())
.withServices(description.getSource().getAsgName());

DescribeServicesResult result = ecsClient.describeServices(request);
if (result.getServices() != null && !result.getServices().isEmpty()) {
return result.getServices().get(0);
}

return null;
}

return null;
}

private String inferAssumedRoleArn(AmazonCredentials credentials) {
String role;
if (credentials instanceof AssumeRoleAmazonCredentials) {
Expand Down Expand Up @@ -377,6 +445,18 @@ private LoadBalancer retrieveLoadBalancer(String containerName) {
return loadBalancer;
}

private AWSApplicationAutoScaling getSourceAmazonApplicationAutoScalingClient() {
String sourceRegion = description.getSource().getRegion();
NetflixAmazonCredentials sourceCredentials = (NetflixAmazonCredentials)accountCredentialsProvider.getCredentials(description.getSource().getAccount());
return amazonClientProvider.getAmazonApplicationAutoScaling(sourceCredentials, sourceRegion, false);
}

private AmazonECS getSourceAmazonEcsClient() {
String sourceRegion = description.getSource().getRegion();
NetflixAmazonCredentials sourceCredentials = (NetflixAmazonCredentials)accountCredentialsProvider.getCredentials(description.getSource().getAccount());
return amazonClientProvider.getAmazonEcs(sourceCredentials, sourceRegion, false);
}

private AWSApplicationAutoScaling getAmazonApplicationAutoScalingClient() {
AWSCredentialsProvider credentialsProvider = getCredentials().getCredentialsProvider();
NetflixAmazonCredentials credentialAccount = description.getCredentials();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,6 @@ public void validate(List priorDescriptions, Object description, Errors errors)
rejectValue(errors, "placementStrategySequence", "not.nullable");
}

if (createServerGroupDescription.getAutoscalingPolicies() == null) {
rejectValue(errors, "autoscalingPolicies", "not.nullable");
}

if (createServerGroupDescription.getApplication() == null) {
rejectValue(errors, "application", "not.nullable");
}
Expand Down
Loading