feat(ecs): Return application load balancer info from LB provider
MODIFY
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/client/EcsLoadbalancerCacheClient.java
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/client/EcsLoadbalancerCacheClient.java
@@ -25,12 +25,8 @@ import com.netflix.spinnaker.cats.cache.CacheData;
import com.netflix.spinnaker.cats.cache.RelationshipCacheFilter;
import com.netflix.spinnaker.clouddriver.aws.data.Keys;
import com.netflix.spinnaker.clouddriver.ecs.cache.model.EcsLoadBalancerCache;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import com.netflix.spinnaker.clouddriver.ecs.provider.view.EcsAccountMapper;
+import java.util.*;
import java.util.stream.Collectors;
import org.springframework.stereotype.Component;
@@ -39,10 +35,13 @@ public class EcsLoadbalancerCacheClient {
private final Cache cacheView;
private final ObjectMapper objectMapper;
+ private final EcsAccountMapper ecsAccountMapper;
- public EcsLoadbalancerCacheClient(Cache cacheView, ObjectMapper objectMapper) {
+ public EcsLoadbalancerCacheClient(
+ Cache cacheView, ObjectMapper objectMapper, EcsAccountMapper ecsAccountMapper) {
this.cacheView = cacheView;
this.objectMapper = objectMapper;
+ this.ecsAccountMapper = ecsAccountMapper;
}
public List<EcsLoadBalancerCache> find(String account, String region) {
@@ -56,6 +55,12 @@ public class EcsLoadbalancerCacheClient {
private Set<Map<String, Object>> fetchFromCache(String account, String region) {
String accountFilter = account != null ? account : "*";
+ if (!"*".equals(accountFilter)) {
+ String awsAccountName = ecsAccountMapper.fromEcsAccountNameToAwsAccountName(accountFilter);
+ if (awsAccountName != null) {
+ accountFilter = awsAccountName;
+ }
+ }
String regionFilter = region != null ? region : "*";
String searchKey = Keys.getLoadBalancerKey("*", accountFilter, regionFilter, "*", "*") + "*";
@@ -66,12 +71,16 @@ public class EcsLoadbalancerCacheClient {
return fetchLoadBalancerAttributes(loadbalancerKeys);
}
- public Set<EcsLoadBalancerCache> findWithTargetGroups(Set<String> targetGroups) {
- return findAll().stream()
- .filter(
- ecsLoadBalancerCache ->
- targetGroups.containsAll(ecsLoadBalancerCache.getTargetGroups()))
- .collect(Collectors.toSet());
+ public List<EcsLoadBalancerCache> findWithTargetGroups(Set<String> targetGroupKeys) {
+ Set<CacheData> targetGroupCacheData =
+ new HashSet<>(
+ cacheView.getAll(
+ TARGET_GROUPS.getNs(),
+ targetGroupKeys,
+ RelationshipCacheFilter.include(LOAD_BALANCERS.getNs())));
+ Set<String> lbKeys = inferAssociatedLoadBalancers(targetGroupCacheData);
+ Set<Map<String, Object>> loadbalancerAttributes = fetchLoadBalancerAttributes(lbKeys);
+ return convertToLoadbalancer(loadbalancerAttributes);
}
private EcsLoadBalancerCache convertToLoadBalancer(Map<String, Object> targetGroupAttributes) {
@@ -89,8 +98,9 @@ public class EcsLoadbalancerCacheClient {
return ecsTargetGroups;
}
- private Set<Map<String, Object>> fetchLoadBalancerAttributes(Collection<String> targetGroupKeys) {
- Set<CacheData> loadBalancerCache = fetchLoadBalancers(targetGroupKeys);
+ private Set<Map<String, Object>> fetchLoadBalancerAttributes(
+ Collection<String> loadBalancerKeys) {
+ Set<CacheData> loadBalancerCache = fetchLoadBalancers(loadBalancerKeys);
return loadBalancerCache.stream()
.filter(this::hashTargetGroups)
@@ -108,7 +118,8 @@ public class EcsLoadbalancerCacheClient {
Map<String, String> parts = Keys.parse(loadbalancerCache.getId());
attributes.put("region", parts.get("region"));
- attributes.put("account", parts.get("account"));
+ String ecsAccount = ecsAccountMapper.fromAwsAccountNameToEcsAccountName(parts.get("account"));
+ attributes.put("account", ecsAccount);
attributes.put("loadBalancerType", parts.get("loadBalancerType"));
attributes.put(
"targetGroups",
@@ -119,18 +130,12 @@ public class EcsLoadbalancerCacheClient {
return attributes;
}
- private Set<Map<String, Object>> retrieveLoadbalancers(
- Set<String> loadbalancersAssociatedWithTargetGroups) {
- Collection<CacheData> loadbalancers =
- cacheView.getAll(LOAD_BALANCERS.getNs(), loadbalancersAssociatedWithTargetGroups);
- return loadbalancers.stream().map(CacheData::getAttributes).collect(Collectors.toSet());
- }
-
private Set<String> inferAssociatedLoadBalancers(Set<CacheData> targetGroups) {
Set<String> loadbalancersAssociatedWithTargetGroups = new HashSet<>();
for (CacheData targetGroup : targetGroups) {
- Collection<String> relatedLoadbalancer = targetGroup.getRelationships().get("loadbalancer");
+ Collection<String> relatedLoadbalancer =
+ targetGroup.getRelationships().get(LOAD_BALANCERS.ns);
if (relatedLoadbalancer != null && relatedLoadbalancer.size() > 0) {
loadbalancersAssociatedWithTargetGroups.addAll(relatedLoadbalancer);
}

MODIFY
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/client/EcsTargetGroupCacheClient.java
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/client/EcsTargetGroupCacheClient.java
@@ -61,6 +61,10 @@ public class EcsTargetGroupCacheClient {
return targetGroups;
}
+ public Collection<String> getAllKeys() {
+ return cacheView.getIdentifiers(TARGET_GROUPS.ns);
+ }
+
private EcsTargetGroup convertToTargetGroup(Map<String, Object> targetGroupAttributes) {
EcsTargetGroup ecsTargetGroup =
objectMapper.convertValue(targetGroupAttributes, EcsTargetGroup.class);

MODIFY
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/model/EcsLoadBalancerCache.java
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/model/EcsLoadBalancerCache.java
@@ -54,6 +54,6 @@ public class EcsLoadBalancerCache implements LoadBalancer {
@Override
public String getType() {
- return loadBalancerType;
+ return cloudProvider;
}
}

ADD
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/model/loadbalancer/EcsLoadBalancer.java
+/*
+ * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License
+ * for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.netflix.spinnaker.clouddriver.ecs.model.loadbalancer;
+
+import com.amazonaws.services.elasticloadbalancingv2.model.Listener;
+import com.netflix.spinnaker.clouddriver.ecs.EcsCloudProvider;
+import com.netflix.spinnaker.clouddriver.model.LoadBalancer;
+import com.netflix.spinnaker.clouddriver.model.LoadBalancerServerGroup;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import lombok.Data;
+
+@Data
+public class EcsLoadBalancer implements LoadBalancer {
+ // TODO: refactor EcsLoadBalancerCache so can be extended here?
+
+ private String account;
+ private String region;
+ private String loadBalancerArn;
+ private String loadBalancerType;
+ private String cloudProvider = EcsCloudProvider.ID;
+ private List<Listener> listeners;
+ private List<String> availabilityZones;
+ private String ipAddressType;
+ private String loadBalancerName;
+ private String canonicalHostedZoneId;
+ private String vpcId;
+ private String dnsname;
+ private Long createdTime;
+ private List<String> subnets;
+ private List<String> securityGroups;
+ private List<EcsTargetGroup> targetGroups;
+ private Set<LoadBalancerServerGroup> serverGroups;
+ private Map<String, Set<String>> targetGroupServices;
+
+ @Override
+ public String getName() {
+ return loadBalancerName;
+ }
+
+ @Override
+ public String getType() {
+ return cloudProvider;
+ }
+}

MODIFY
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/model/loadbalancer/EcsLoadBalancerDetail.java
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/model/loadbalancer/EcsLoadBalancerDetail.java
@@ -28,7 +28,7 @@ public class EcsLoadBalancerDetail implements Details {
String region;
String name;
String vpcId;
- String type = "aws";
+ String type = "ecs";
String loadBalancerType;
List<String> securityGroups = new LinkedList<>();
List<String> targetGroups = new LinkedList<>();

MODIFY
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/model/loadbalancer/EcsTargetGroup.java
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/model/loadbalancer/EcsTargetGroup.java
@@ -27,6 +27,7 @@ public class EcsTargetGroup implements LoadBalancerProvider.Details {
List<String> loadBalancerNames;
List<String> instances;
+ String targetType;
Integer healthCheckTimeoutSeconds;
String targetGroupArn;
String healthCheckPort;

MODIFY
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/provider/view/EcsLoadBalancerProvider.java
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/provider/view/EcsLoadBalancerProvider.java
package com.netflix.spinnaker.clouddriver.ecs.provider.view;
-import com.netflix.spinnaker.clouddriver.aws.model.AmazonLoadBalancer;
+import static com.netflix.spinnaker.clouddriver.core.provider.agent.Namespace.TARGET_GROUPS;
+
+import com.amazonaws.services.ecs.model.LoadBalancer;
+import com.netflix.spinnaker.clouddriver.aws.AmazonCloudProvider;
+import com.netflix.spinnaker.clouddriver.aws.data.ArnUtils;
import com.netflix.spinnaker.clouddriver.ecs.EcsCloudProvider;
import com.netflix.spinnaker.clouddriver.ecs.cache.client.EcsLoadbalancerCacheClient;
+import com.netflix.spinnaker.clouddriver.ecs.cache.client.EcsTargetGroupCacheClient;
+import com.netflix.spinnaker.clouddriver.ecs.cache.client.ServiceCacheClient;
import com.netflix.spinnaker.clouddriver.ecs.cache.model.EcsLoadBalancerCache;
+import com.netflix.spinnaker.clouddriver.ecs.cache.model.Service;
+import com.netflix.spinnaker.clouddriver.ecs.model.loadbalancer.EcsLoadBalancer;
import com.netflix.spinnaker.clouddriver.ecs.model.loadbalancer.EcsLoadBalancerDetail;
import com.netflix.spinnaker.clouddriver.ecs.model.loadbalancer.EcsLoadBalancerSummary;
-import com.netflix.spinnaker.clouddriver.ecs.security.ECSCredentialsConfig;
+import com.netflix.spinnaker.clouddriver.ecs.model.loadbalancer.EcsTargetGroup;
import com.netflix.spinnaker.clouddriver.model.LoadBalancerProvider;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
+import java.util.stream.Collectors;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
-public class EcsLoadBalancerProvider implements LoadBalancerProvider<AmazonLoadBalancer> {
+public class EcsLoadBalancerProvider implements LoadBalancerProvider<EcsLoadBalancer> {
private final EcsLoadbalancerCacheClient ecsLoadbalancerCacheClient;
- private final ECSCredentialsConfig ecsCredentialsConfig;
+ private final EcsAccountMapper ecsAccountMapper;
+ private final ServiceCacheClient ecsServiceCacheClient;
+ private final EcsTargetGroupCacheClient ecsTargetGroupCacheClient;
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
public EcsLoadBalancerProvider(
EcsLoadbalancerCacheClient ecsLoadbalancerCacheClient,
- ECSCredentialsConfig ecsCredentialsConfig) {
+ EcsAccountMapper ecsAccountMapper,
+ ServiceCacheClient ecsServiceCacheClient,
+ EcsTargetGroupCacheClient ecsTargetGroupCacheClient) {
this.ecsLoadbalancerCacheClient = ecsLoadbalancerCacheClient;
- this.ecsCredentialsConfig = ecsCredentialsConfig;
+ this.ecsAccountMapper = ecsAccountMapper;
+ this.ecsServiceCacheClient = ecsServiceCacheClient;
+ this.ecsTargetGroupCacheClient = ecsTargetGroupCacheClient;
}
@Override
@@ -57,8 +72,7 @@ public class EcsLoadBalancerProvider implements LoadBalancerProvider<AmazonLoadB
List<EcsLoadBalancerCache> loadBalancers = ecsLoadbalancerCacheClient.findAll();
for (EcsLoadBalancerCache lb : loadBalancers) {
- String account = getEcsAccountName(lb.getAccount());
- if (account == null) {
+ if (lb.getAccount() == null) {
continue;
}
@@ -72,7 +86,7 @@ public class EcsLoadBalancerProvider implements LoadBalancerProvider<AmazonLoadB
}
EcsLoadBalancerDetail loadBalancer = new EcsLoadBalancerDetail();
- loadBalancer.setAccount(account);
+ loadBalancer.setAccount(lb.getAccount());
loadBalancer.setRegion(region);
loadBalancer.setName(name);
loadBalancer.setVpcId(lb.getVpcId());
@@ -81,7 +95,7 @@ public class EcsLoadBalancerProvider implements LoadBalancerProvider<AmazonLoadB
loadBalancer.setTargetGroups(lb.getTargetGroups());
summary
- .getOrCreateAccount(account)
+ .getOrCreateAccount(lb.getAccount())
.getOrCreateRegion(region)
.getLoadBalancers()
.add(loadBalancer);
@@ -92,27 +106,113 @@ public class EcsLoadBalancerProvider implements LoadBalancerProvider<AmazonLoadB
@Override
public Item get(String name) {
- return null; // TODO - Implement this.
+ return null; // intentionally null, implement if/when needed in Deck.
}
@Override
public List<Details> byAccountAndRegionAndName(String account, String region, String name) {
- return null; // TODO - Implement this. This is used to show the details view of a load balancer
- // which is not even implemented yet
+ return null; // intentionally null, implement if/when needed in Deck.
}
@Override
- public Set<AmazonLoadBalancer> getApplicationLoadBalancers(String application) {
- return null; // TODO - Implement this. This is used to show load balancers and reveals other
- // buttons
+ public Set<EcsLoadBalancer> getApplicationLoadBalancers(String application) {
+ // Find the load balancers currently in use by ECS services in this application
+ Set<Service> services =
+ ecsServiceCacheClient.getAll().stream()
+ .filter(service -> service.getApplicationName().equals(application))
+ .collect(Collectors.toSet());
+ log.debug("Retrieved {} services for application '{}'", services.size(), application);
+
+ Collection<String> allTargetGroupKeys = ecsTargetGroupCacheClient.getAllKeys();
+ log.debug(
+ "Retrieved {} target group keys for application '{}'",
+ allTargetGroupKeys.size(),
+ application);
+
+ Map<String, Set<String>> targetGroupToServicesMap = new HashMap<>();
+ Set<String> targetGroupKeys = new HashSet<>();
+
+ // find all the target group cache keys
+ for (Service service : services) {
+ String awsAccountName =
+ ecsAccountMapper.fromEcsAccountNameToAwsAccountName(service.getAccount());
+ for (LoadBalancer loadBalancer : service.getLoadBalancers()) {
+ if (loadBalancer.getTargetGroupArn() != null) {
+ String tgArn = loadBalancer.getTargetGroupArn();
+ String keyPrefix =
+ String.format(
+ "%s:%s:%s:%s:%s:",
+ AmazonCloudProvider.ID,
+ TARGET_GROUPS.getNs(),
+ awsAccountName,
+ service.getRegion(),
+ ArnUtils.extractTargetGroupName(tgArn).get());
+ Set<String> matchingKeys =
+ allTargetGroupKeys.stream()
+ .filter(key -> key.startsWith(keyPrefix))
+ .collect(Collectors.toSet());
+ targetGroupKeys.addAll(matchingKeys);
+ // associate target groups with services it contains targets for
+ if (targetGroupToServicesMap.containsKey(tgArn)) {
+ Set<String> serviceList = targetGroupToServicesMap.get(tgArn);
+ serviceList.add(service.getServiceName());
+ targetGroupToServicesMap.put(tgArn, serviceList);
+ } else {
+ Set<String> srcServices = Collections.singleton(service.getServiceName());
+ targetGroupToServicesMap.put(tgArn, srcServices);
+ }
+ }
+ }
}
- private String getEcsAccountName(String awsAccountName) {
- for (ECSCredentialsConfig.Account ecsAccount : ecsCredentialsConfig.getAccounts()) {
- if (ecsAccount.getAwsAccount().equals(awsAccountName)) {
- return ecsAccount.getName();
+ // retrieve matching target groups
+ List<EcsTargetGroup> tgs = ecsTargetGroupCacheClient.find(targetGroupKeys);
+
+ // find the load balancers for those target groups
+ List<EcsLoadBalancerCache> tgLBs =
+ ecsLoadbalancerCacheClient.findWithTargetGroups(targetGroupKeys);
+ log.debug(
+ "Retrieved {} load balancers for {} target group keys.",
+ tgLBs.size(),
+ targetGroupKeys.size());
+
+ Set<EcsLoadBalancer> ecsLoadBalancers = new HashSet<>();
+ for (EcsLoadBalancerCache loadBalancerCache : tgLBs) {
+ List<EcsTargetGroup> matchingTGs =
+ tgs.stream()
+ .filter(tg -> loadBalancerCache.getTargetGroups().contains(tg.getTargetGroupName()))
+ .collect(Collectors.toList());
+ EcsLoadBalancer ecsLB =
+ makeEcsLoadBalancer(loadBalancerCache, matchingTGs, targetGroupToServicesMap);
+ ecsLoadBalancers.add(ecsLB);
}
+
+ return ecsLoadBalancers;
}
- return null;
+
+ private EcsLoadBalancer makeEcsLoadBalancer(
+ EcsLoadBalancerCache elbCacheData,
+ List<EcsTargetGroup> tgCacheData,
+ Map<String, Set<String>> tgToServiceMap) {
+ EcsLoadBalancer ecsLoadBalancer = new EcsLoadBalancer();
+ ecsLoadBalancer.setAccount(elbCacheData.getAccount());
+ ecsLoadBalancer.setRegion(elbCacheData.getRegion());
+ ecsLoadBalancer.setLoadBalancerArn(elbCacheData.getLoadBalancerArn());
+ ecsLoadBalancer.setLoadBalancerName(elbCacheData.getLoadBalancerName());
+ ecsLoadBalancer.setLoadBalancerType(elbCacheData.getLoadBalancerType());
+ ecsLoadBalancer.setCloudProvider(elbCacheData.getCloudProvider());
+ ecsLoadBalancer.setListeners(elbCacheData.getListeners());
+ ecsLoadBalancer.setAvailabilityZones(elbCacheData.getAvailabilityZones());
+ ecsLoadBalancer.setIpAddressType(elbCacheData.getIpAddressType());
+ ecsLoadBalancer.setDnsname(elbCacheData.getDnsname());
+ ecsLoadBalancer.setVpcId(elbCacheData.getVpcId());
+ ecsLoadBalancer.setCreatedTime(elbCacheData.getCreatedTime());
+ ecsLoadBalancer.setSecurityGroups(elbCacheData.getSecurityGroups());
+ ecsLoadBalancer.setSubnets(elbCacheData.getSubnets());
+ ecsLoadBalancer.setTargetGroups(tgCacheData);
+ ecsLoadBalancer.setTargetGroupServices(tgToServiceMap);
+ // TODO: get, add target healths per service/tg
+
+ return ecsLoadBalancer;
}
}

MODIFY
clouddriver-ecs/src/test/groovy/com/netflix/spinnaker/clouddriver/ecs/cache/EcsLoadbalancerCacheClientSpec.groovy
clouddriver-ecs/src/test/groovy/com/netflix/spinnaker/clouddriver/ecs/cache/EcsLoadbalancerCacheClientSpec.groovy
@@ -25,6 +25,7 @@ import com.netflix.spinnaker.clouddriver.aws.data.Keys
import com.netflix.spinnaker.clouddriver.ecs.EcsCloudProvider
import com.netflix.spinnaker.clouddriver.ecs.cache.client.EcsLoadbalancerCacheClient
import com.netflix.spinnaker.clouddriver.ecs.cache.model.EcsLoadBalancerCache
+import com.netflix.spinnaker.clouddriver.ecs.provider.view.EcsAccountMapper
import spock.lang.Specification
import spock.lang.Subject
@@ -32,24 +33,29 @@ class EcsLoadbalancerCacheClientSpec extends Specification {
def cacheView = Mock(Cache)
def objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+ def accountMapper = Mock(EcsAccountMapper)
@Subject
- EcsLoadbalancerCacheClient client = new EcsLoadbalancerCacheClient(cacheView, objectMapper)
+ def client = new EcsLoadbalancerCacheClient(cacheView, objectMapper, accountMapper)
def 'should convert cache data into object'() {
given:
def loadBalancerName = 'test-name'
- def account = 'test-account'
+ def ecsAccount = 'ecsAccount'
+ def awsAccount = 'awsAccount'
def region = 'us-west-1'
def vpcId = 'vpc-id'
def loadBalancerType = 'classic'
def targetGroupName = 'test-target-group'
- def loadbalancerKey = Keys.getLoadBalancerKey(loadBalancerName, account, region, vpcId, loadBalancerType)
- def targetGroupKey = Keys.getTargetGroupKey(targetGroupName, account, region, TargetTypeEnum.Instance.toString(), vpcId)
+ accountMapper.fromEcsAccountNameToAwsAccountName(ecsAccount) >> awsAccount
+ accountMapper.fromAwsAccountNameToEcsAccountName(awsAccount) >> ecsAccount
+
+ def loadbalancerKey = Keys.getLoadBalancerKey(loadBalancerName, awsAccount, region, vpcId, loadBalancerType)
+ def targetGroupKey = Keys.getTargetGroupKey(targetGroupName, awsAccount, region, TargetTypeEnum.Instance.toString(), vpcId)
def givenEcsLoadbalancer = new EcsLoadBalancerCache(
- account: account,
+ account: ecsAccount,
region: region,
loadBalancerArn: 'arn',
loadBalancerType: loadBalancerType,

MODIFY
clouddriver-ecs/src/test/groovy/com/netflix/spinnaker/clouddriver/ecs/cache/EcsTargetGroupCacheClientSpec.groovy
clouddriver-ecs/src/test/groovy/com/netflix/spinnaker/clouddriver/ecs/cache/EcsTargetGroupCacheClientSpec.groovy
@@ -52,6 +52,7 @@ class EcsTargetGroupCacheClientSpec extends Specification {
instances: [],
healthCheckTimeoutSeconds: 30,
targetGroupArn: 'arn',
+ targetType: 'ip',
healthCheckPort: 1337,
matcher: [:],
healthCheckProtocol: 'http',

MODIFY
clouddriver-ecs/src/test/groovy/com/netflix/spinnaker/clouddriver/ecs/provider/view/EcsLoadBalancerProviderSpec.groovy
clouddriver-ecs/src/test/groovy/com/netflix/spinnaker/clouddriver/ecs/provider/view/EcsLoadBalancerProviderSpec.groovy
package com.netflix.spinnaker.clouddriver.ecs.provider.view
+import com.amazonaws.services.ecs.model.LoadBalancer
import com.netflix.spinnaker.clouddriver.ecs.EcsCloudProvider
import com.netflix.spinnaker.clouddriver.ecs.cache.client.EcsLoadbalancerCacheClient
+import com.netflix.spinnaker.clouddriver.ecs.cache.client.EcsTargetGroupCacheClient
+import com.netflix.spinnaker.clouddriver.ecs.cache.client.ServiceCacheClient
import com.netflix.spinnaker.clouddriver.ecs.cache.model.EcsLoadBalancerCache
+import com.netflix.spinnaker.clouddriver.ecs.cache.model.Service
+import com.netflix.spinnaker.clouddriver.ecs.model.loadbalancer.EcsLoadBalancer
+import com.netflix.spinnaker.clouddriver.ecs.model.loadbalancer.EcsTargetGroup
import com.netflix.spinnaker.clouddriver.ecs.security.ECSCredentialsConfig
+import com.netflix.spinnaker.clouddriver.ecs.security.NetflixECSCredentials
import spock.lang.Specification
import spock.lang.Subject
class EcsLoadBalancerProviderSpec extends Specification {
- def client = Mock(EcsLoadbalancerCacheClient)
- def ecsCredentialsConfig = Mock(ECSCredentialsConfig)
+ def ECS_ACCOUNT = 'ecsAccount'
+ def AWS_ACCOUNT = 'awsAccount'
+
+ def mockLBCache = Mock(EcsLoadbalancerCacheClient)
+ def mockServiceCache = Mock(ServiceCacheClient)
+ def mockTargetGroupCache = Mock(EcsTargetGroupCacheClient)
+ def accountMapper = Mock(EcsAccountMapper)
+
@Subject
- def provider = new EcsLoadBalancerProvider(client, ecsCredentialsConfig)
+ def provider = new EcsLoadBalancerProvider(
+ mockLBCache,
+ accountMapper,
+ mockServiceCache,
+ mockTargetGroupCache)
+
+ def setup() {
+ accountMapper.fromEcsAccountNameToAwsAccountName(ECS_ACCOUNT) >> AWS_ACCOUNT
+ }
def 'should retrieve an empty list'() {
when:
def retrievedList = provider.list()
then:
- client.findAll() >> Collections.emptyList()
+ mockLBCache.findAll() >> Collections.emptyList()
retrievedList.size() == 0
}
@@ -42,7 +63,6 @@ class EcsLoadBalancerProviderSpec extends Specification {
given:
def expectedNumberOfLoadbalancers = 2
def givenList = []
- def accounts = []
(1..expectedNumberOfLoadbalancers).forEach() {
givenList << new EcsLoadBalancerCache(
account: 'test-account-' + it,
@@ -64,20 +84,108 @@ class EcsLoadBalancerProviderSpec extends Specification {
targetGroups: ['target-group-' + it],
serverGroups: []
)
-
- accounts << new ECSCredentialsConfig.Account(
- name: 'test-account-' + it,
- awsAccount: 'test-account-' + it
- )
}
- ecsCredentialsConfig.getAccounts() >> accounts
when:
def retrievedList = provider.list()
then:
- client.findAll() >> givenList
+ mockLBCache.findAll() >> givenList
retrievedList.size() == expectedNumberOfLoadbalancers
retrievedList*.getName().containsAll(givenList*.loadBalancerName)
}
+
+ def 'should retrieve application load balancers'() {
+ given:
+ def applicationName = 'myEcsApp'
+ def tgArn1 = 'arn:aws:elasticloadbalancing:us-west-1:1234567890:targetgroup/test-tg-1/2136bac'
+ def tgArn2 = 'arn:aws:elasticloadbalancing:us-west-1:1234567890:targetgroup/test-tg-2/2136bac'
+
+ // define 2 ports to expose on our task
+ LoadBalancer ecsLb1 = new LoadBalancer()
+ ecsLb1.setContainerName("container-name")
+ ecsLb1.setContainerPort(8080)
+ ecsLb1.setTargetGroupArn(tgArn1)
+
+ LoadBalancer ecsLb2 = new LoadBalancer()
+ ecsLb2.setContainerName("container-name")
+ ecsLb2.setContainerPort(443)
+ ecsLb2.setTargetGroupArn(tgArn2)
+
+ // add these to our service
+ Service ecsService = new Service()
+ ecsService.setServiceName('ecs-test-detail-000v')
+ ecsService.setServiceArn('arn:aws:ecs:service/ecs-test-detail-000v')
+ ecsService.setLoadBalancers([ecsLb1, ecsLb2])
+ ecsService.setAccount('test-account')
+ ecsService.setApplicationName(applicationName)
+
+ // mock the cache entries for the TGs and their associated LBs
+ EcsTargetGroup ecsTg1 = new EcsTargetGroup()
+ ecsTg1.setTargetGroupArn(tgArn1)
+ ecsTg1.setTargetGroupName('test-tg-1')
+
+ EcsTargetGroup ecsTg2 = new EcsTargetGroup()
+ ecsTg2.setTargetGroupArn(tgArn2)
+ ecsTg2.setTargetGroupName('test-tg-2')
+
+ EcsLoadBalancerCache ecsLoadBalancerCache1 = new EcsLoadBalancerCache(
+ account: 'test-account',
+ region: 'us-west-2',
+ loadBalancerArn: 'arn:1',
+ loadBalancerType: 'application',
+ cloudProvider: EcsCloudProvider.ID,
+ listeners: [],
+ scheme: 'scheme',
+ availabilityZones: [],
+ ipAddressType: 'ipv4',
+ loadBalancerName: 'load-balancer-name1',
+ canonicalHostedZoneId: 'zone-id',
+ vpcId: 'vpc-id',
+ dnsname: 'dns-name',
+ createdTime: System.currentTimeMillis(),
+ subnets: [],
+ securityGroups: [],
+ targetGroups: [ecsTg1.getTargetGroupName()],
+ serverGroups: []
+ )
+ EcsLoadBalancerCache ecsLoadBalancerCache2 = new EcsLoadBalancerCache(
+ account: 'test-account',
+ region: 'us-west-2',
+ loadBalancerArn: 'arn:2',
+ loadBalancerType: 'application',
+ cloudProvider: EcsCloudProvider.ID,
+ listeners: [],
+ scheme: 'scheme',
+ availabilityZones: [],
+ ipAddressType: 'ipv4',
+ loadBalancerName: 'load-balancer-name2',
+ canonicalHostedZoneId: 'zone-id',
+ vpcId: 'vpc-id',
+ dnsname: 'dns-name',
+ createdTime: System.currentTimeMillis(),
+ subnets: [],
+ securityGroups: [],
+ targetGroups: [ecsTg2.getTargetGroupName()],
+ serverGroups: []
+ )
+
+ when:
+ def loadBalancerList = provider.getApplicationLoadBalancers(applicationName)
+
+ then:
+ mockServiceCache.getAll() >> Collections.singletonList(ecsService)
+ mockTargetGroupCache.getAllKeys() >> ['fake-tg-key-1', 'fake-tg-key-2']
+ mockTargetGroupCache.find(_) >> [ecsTg1, ecsTg2]
+ mockLBCache.findWithTargetGroups(_) >> [ecsLoadBalancerCache1, ecsLoadBalancerCache2]
+
+ loadBalancerList.size() == 2
+ for (EcsLoadBalancer lb : loadBalancerList) {
+ lb.targetGroupServices.size() == 1
+ lb.targetGroups.size() == 1
+
+ def tgArn = lb.targetGroups[0].getTargetGroupArn()
+ lb.targetGroupServices[tgArn][0] == ecsService.getServiceName()
+ }
+ }
}

feat(ecs): Return application load balancer info from LB provider (#4526)

Co-authored-by: Clare Liguori <liguori@amazon.com>