From 2ddac534880863350b8c6db769134aae41730aa8 Mon Sep 17 00:00:00 2001 From: Daniel Kirillov Date: Wed, 8 Nov 2017 16:17:47 -0500 Subject: [PATCH] feat(provider/ecs): ECS Cluster caching classes and tests. (#2091) * ECS Cluster caching classes and tests. * Base abstraction. --- .../cache/client/EcsClusterCacheClient.java | 47 ++++++++ .../ecs/cache/model/EcsCluster.java | 27 +++++ .../agent/AbstractEcsOnDemandAgent.java | 91 ++++++++++++++ .../agent/EcsClusterCachingAgent.java | 112 ++++++++++++++++++ .../ecs/cache/CommonCacheClient.java | 28 +++++ .../ecs/cache/EcsClusterCacheClientTest.java | 57 +++++++++ .../provider/agent/CommonCachingAgent.java | 74 ++++++++++++ .../provider/agent/EcsClusterCacheTest.java | 69 +++++++++++ .../agent/EcsClusterCachingAgentTest.java | 100 ++++++++++++++++ 9 files changed, 605 insertions(+) create mode 100644 clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/client/EcsClusterCacheClient.java create mode 100644 clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/model/EcsCluster.java create mode 100644 clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/AbstractEcsOnDemandAgent.java create mode 100644 clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/EcsClusterCachingAgent.java create mode 100644 clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/cache/CommonCacheClient.java create mode 100644 clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/cache/EcsClusterCacheClientTest.java create mode 100644 clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/CommonCachingAgent.java create mode 100644 clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/EcsClusterCacheTest.java create mode 100644 clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/EcsClusterCachingAgentTest.java diff --git a/clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/client/EcsClusterCacheClient.java b/clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/client/EcsClusterCacheClient.java new file mode 100644 index 00000000000..6bb55e01fca --- /dev/null +++ b/clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/client/EcsClusterCacheClient.java @@ -0,0 +1,47 @@ +/* + * Copyright 2017 Lookout, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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.cache.client; + +import com.netflix.spinnaker.cats.cache.Cache; +import com.netflix.spinnaker.cats.cache.CacheData; +import com.netflix.spinnaker.clouddriver.ecs.cache.model.EcsCluster; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Map; + +import static com.netflix.spinnaker.clouddriver.ecs.cache.Keys.Namespace.ECS_CLUSTERS; + +public class EcsClusterCacheClient extends AbstractCacheClient{ + + @Autowired + public EcsClusterCacheClient(Cache cacheView) { + super(cacheView, ECS_CLUSTERS.toString()); + } + + @Override + protected EcsCluster convert(CacheData cacheData) { + EcsCluster ecsCluster = new EcsCluster(); + Map attributes = cacheData.getAttributes(); + + ecsCluster.setAccount((String) attributes.get("account")); + ecsCluster.setRegion((String) attributes.get("region")); + ecsCluster.setName((String) attributes.get("clusterName")); + ecsCluster.setArn((String) attributes.get("clusterArn")); + + return ecsCluster; + } +} diff --git a/clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/model/EcsCluster.java b/clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/model/EcsCluster.java new file mode 100644 index 00000000000..e8055164842 --- /dev/null +++ b/clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/cache/model/EcsCluster.java @@ -0,0 +1,27 @@ +/* + * Copyright 2017 Lookout, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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.cache.model; + +import lombok.Data; + +@Data +public class EcsCluster { + String account; + String region; + String name; + String arn; +} diff --git a/clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/AbstractEcsOnDemandAgent.java b/clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/AbstractEcsOnDemandAgent.java new file mode 100644 index 00000000000..1ce2bf9f53e --- /dev/null +++ b/clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/AbstractEcsOnDemandAgent.java @@ -0,0 +1,91 @@ +/* + * Copyright 2017 Lookout, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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.provider.agent; + +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.services.ecs.AmazonECS; +import com.netflix.spectator.api.Registry; +import com.netflix.spinnaker.cats.agent.CacheResult; +import com.netflix.spinnaker.cats.provider.ProviderCache; +import com.netflix.spinnaker.clouddriver.aws.security.AmazonClientProvider; +import com.netflix.spinnaker.clouddriver.cache.OnDemandAgent; +import com.netflix.spinnaker.clouddriver.cache.OnDemandMetricsSupport; +import com.netflix.spinnaker.clouddriver.ecs.EcsCloudProvider; +import groovy.lang.Closure; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +abstract class AbstractEcsOnDemandAgent extends AbstractEcsCachingAgent implements OnDemandAgent { + final OnDemandMetricsSupport metricsSupport; + + AbstractEcsOnDemandAgent(String accountName, String region, AmazonClientProvider amazonClientProvider, AWSCredentialsProvider awsCredentialsProvider, Registry registry) { + super(accountName, region, amazonClientProvider, awsCredentialsProvider); + this.metricsSupport = new OnDemandMetricsSupport(registry, this, EcsCloudProvider.ID + ":" + EcsCloudProvider.ID + ":${OnDemandAgent.OnDemandType.ServerGroup}"); + } + + @Override + public OnDemandMetricsSupport getMetricsSupport() { + return metricsSupport; + } + + @Override + public Collection pendingOnDemandRequests(ProviderCache providerCache) { + return new LinkedList<>(); + } + + @Override + public String getOnDemandAgentType() { + return getAgentType(); + } + + @Override + public boolean handles(OnDemandType type, String cloudProvider) { + return type.equals(OnDemandType.ServerGroup) && cloudProvider.equals(EcsCloudProvider.ID); + } + + @Override + public OnDemandResult handle(ProviderCache providerCache, Map data) { + if (!data.get("account").equals(accountName) || !data.get("region").equals(region)) { + return null; + } + + AmazonECS ecs = amazonClientProvider.getAmazonEcs(accountName, awsCredentialsProvider, region); + + List items = metricsSupport.readData(new Closure>(this, this) { + public List doCall() { + return getItems(ecs, providerCache); + } + }); + + storeOnDemand(providerCache, data); + + CacheResult cacheResult = metricsSupport.transformData(new Closure(this, this) { + public CacheResult doCall() { + return buildCacheResult(getAuthoritativeKeyName(), items, providerCache); + } + }); + + return new OnDemandResult(getAgentType(), cacheResult, null); // TODO(Bruno Carrier) - evictions should happen properly instead of having a null here + } + + void storeOnDemand(ProviderCache providerCache, Map data) { + // TODO: Overwrite if needed. + } +} diff --git a/clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/EcsClusterCachingAgent.java b/clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/EcsClusterCachingAgent.java new file mode 100644 index 00000000000..d83042f5c2d --- /dev/null +++ b/clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/EcsClusterCachingAgent.java @@ -0,0 +1,112 @@ +/* + * Copyright 2017 Lookout, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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.provider.agent; + +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.services.ecs.AmazonECS; +import com.amazonaws.services.ecs.model.ListClustersRequest; +import com.amazonaws.services.ecs.model.ListClustersResult; +import com.netflix.spinnaker.cats.agent.AgentDataType; +import com.netflix.spinnaker.cats.cache.CacheData; +import com.netflix.spinnaker.cats.cache.DefaultCacheData; +import com.netflix.spinnaker.cats.provider.ProviderCache; +import com.netflix.spinnaker.clouddriver.aws.security.AmazonClientProvider; +import com.netflix.spinnaker.clouddriver.ecs.cache.Keys; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static com.netflix.spinnaker.cats.agent.AgentDataType.Authority.AUTHORITATIVE; +import static com.netflix.spinnaker.clouddriver.ecs.cache.Keys.Namespace.ECS_CLUSTERS; + +public class EcsClusterCachingAgent extends AbstractEcsCachingAgent { + private static final Collection types = Collections.unmodifiableCollection(Arrays.asList( + AUTHORITATIVE.forType(ECS_CLUSTERS.toString()) + )); + private final Logger log = LoggerFactory.getLogger(getClass()); + + public EcsClusterCachingAgent(String accountName, String region, AmazonClientProvider amazonClientProvider, AWSCredentialsProvider awsCredentialsProvider) { + super(accountName, region, amazonClientProvider, awsCredentialsProvider); + } + + @Override + public String getAgentType() { + return EcsClusterCachingAgent.class.getSimpleName(); + } + + @Override + public Collection getProvidedDataTypes() { + return types; + } + + @Override + protected List getItems(AmazonECS ecs, ProviderCache providerCache) { + List allClusterArns = new LinkedList<>(); + String nextToken = null; + do { + ListClustersRequest listClustersRequest = new ListClustersRequest(); + if (nextToken != null) { + listClustersRequest.setNextToken(nextToken); + } + + ListClustersResult listClustersResult = ecs.listClusters(listClustersRequest); + allClusterArns.addAll(listClustersResult.getClusterArns()); + + nextToken = listClustersResult.getNextToken(); + } while (nextToken != null && nextToken.length() != 0); + + return allClusterArns; + } + + @Override + protected Map> generateFreshData(Collection clusterArns) { + Collection dataPoints = new LinkedList<>(); + for (String clusterArn : clusterArns) { + String clusterName = StringUtils.substringAfterLast(clusterArn, "/"); + + Map attributes = convertClusterArnToAttributes(accountName, region, clusterArn); + + String key = Keys.getClusterKey(accountName, region, clusterName); + dataPoints.add(new DefaultCacheData(key, attributes, Collections.emptyMap())); + } + + log.info("Caching " + dataPoints.size() + " ECS clusters in " + getAgentType()); + Map> dataMap = new HashMap<>(); + dataMap.put(ECS_CLUSTERS.toString(), dataPoints); + + return dataMap; + } + public static Map convertClusterArnToAttributes(String accountName, String region, String clusterArn){ + String clusterName = StringUtils.substringAfterLast(clusterArn, "/"); + + Map attributes = new HashMap<>(); + attributes.put("account", accountName); + attributes.put("region", region); + attributes.put("clusterName", clusterName); + attributes.put("clusterArn", clusterArn); + + return attributes; + } +} diff --git a/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/cache/CommonCacheClient.java b/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/cache/CommonCacheClient.java new file mode 100644 index 00000000000..ed19c92d83a --- /dev/null +++ b/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/cache/CommonCacheClient.java @@ -0,0 +1,28 @@ +/* + * Copyright 2017 Lookout, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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.cache; + +import com.netflix.spinnaker.cats.cache.Cache; + +import static org.mockito.Mockito.mock; + +class CommonCacheClient { + static final String REGION = "us-west-2"; + static final String ACCOUNT = "test-account"; + + final Cache cacheView = mock(Cache.class); +} diff --git a/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/cache/EcsClusterCacheClientTest.java b/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/cache/EcsClusterCacheClientTest.java new file mode 100644 index 00000000000..f09d8c74c1f --- /dev/null +++ b/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/cache/EcsClusterCacheClientTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2017 Lookout, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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.cache; + +import com.netflix.spinnaker.cats.cache.DefaultCacheData; +import com.netflix.spinnaker.clouddriver.ecs.cache.client.EcsClusterCacheClient; +import com.netflix.spinnaker.clouddriver.ecs.cache.model.EcsCluster; +import com.netflix.spinnaker.clouddriver.ecs.provider.agent.EcsClusterCachingAgent; +import org.junit.Test; +import spock.lang.Subject; + +import java.util.Collections; +import java.util.Map; + +import static com.netflix.spinnaker.clouddriver.ecs.cache.Keys.Namespace.ECS_CLUSTERS; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +public class EcsClusterCacheClientTest extends CommonCacheClient { + @Subject + private final EcsClusterCacheClient client = new EcsClusterCacheClient(cacheView); + + @Test + public void shouldConvert() { + //Given + String clusterName = "test-cluster"; + String clusterArn = "arn:aws:ecs:" + REGION + ":012345678910:cluster/" + clusterName; + String key = Keys.getClusterKey(ACCOUNT, REGION, clusterName); + + Map attributes = EcsClusterCachingAgent.convertClusterArnToAttributes(ACCOUNT, REGION, clusterArn); + + when(cacheView.get(ECS_CLUSTERS.toString(), key)).thenReturn(new DefaultCacheData(key, attributes, Collections.emptyMap())); + + //When + EcsCluster ecsCluster = client.get(key); + + //Then + assertTrue("Expected cluster name to be " + clusterName + " but got " + ecsCluster.getName(), clusterName.equals(ecsCluster.getName())); + assertTrue("Expected cluster ARN to be " + clusterArn + " but got " + ecsCluster.getArn(), clusterArn.equals(ecsCluster.getArn())); + assertTrue("Expected cluster account to be " + ACCOUNT + " but got " + ecsCluster.getAccount(), ACCOUNT.equals(ecsCluster.getAccount())); + assertTrue("Expected cluster region to be " + REGION + " but got " + ecsCluster.getRegion(), REGION.equals(ecsCluster.getRegion())); + } +} diff --git a/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/CommonCachingAgent.java b/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/CommonCachingAgent.java new file mode 100644 index 00000000000..3a567486338 --- /dev/null +++ b/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/CommonCachingAgent.java @@ -0,0 +1,74 @@ +/* + * Copyright 2017 Lookout, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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.provider.agent; + +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.services.ecs.AmazonECS; +import com.netflix.spectator.api.Registry; +import com.netflix.spinnaker.cats.provider.ProviderCache; +import com.netflix.spinnaker.clouddriver.aws.security.AmazonClientProvider; +import org.junit.BeforeClass; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CommonCachingAgent { + static final String REGION = "us-west-2"; + private static final String ECS_SERIVCE = "arn:aws:ecs:" + REGION + ":012345678910:"; + static final String ACCOUNT = "test-account"; + static final String APP_NAME = "testapp"; + static final String ROLE_ARN = ECS_SERIVCE + "service/test-role"; + static final String STATUS = "RUNNING"; + + static final String SERVICE_NAME_1 = APP_NAME + "-stack-detail-v1"; + static final String SERVICE_NAME_2 = APP_NAME + "-stack-detail-v1"; + static final String SERVICE_ARN_1 = ECS_SERIVCE + "service/" + SERVICE_NAME_1; + static final String SERVICE_ARN_2 = ECS_SERIVCE + "service/" + SERVICE_NAME_2; + + static final String CLUSTER_NAME_1 = "test-cluster-1"; + static final String CLUSTER_NAME_2 = "test-cluster-2"; + static final String CLUSTER_ARN_1 = ECS_SERIVCE + "cluster/" + CLUSTER_NAME_1; + static final String CLUSTER_ARN_2 = ECS_SERIVCE + "cluster/" + CLUSTER_NAME_2; + + static final String TASK_ID_1 = "1dc5c17a-422b-4dc4-b493-371970c6c4d6"; + static final String TASK_ID_2 = "1dc5c17a-422b-4dc4-b493-371970c6c4d6"; + static final String TASK_ARN_1 = ECS_SERIVCE + "task/" + TASK_ID_1; + static final String TASK_ARN_2 = ECS_SERIVCE + "task/" + TASK_ID_2; + + static final String CONTAINER_INSTANCE_ARN_1 = ECS_SERIVCE + "container-instance/14e8cce9-0b16-4af4-bfac-a85f7587aa98"; + static final String CONTAINER_INSTANCE_ARN_2 = ECS_SERIVCE + "container-instance/deadbeef-0b16-4af4-bfac-a85f7587aa98"; + + static final String EC2_INSTANCE_ID_1 = "i-042f39dc"; + static final String EC2_INSTANCE_ID_2 = "i-deadbeef"; + + static final String TASK_DEFINITION_ARN_1 = ECS_SERIVCE + "task-definition/hello_world:10"; + static final String TASK_DEFINITION_ARN_2 = ECS_SERIVCE + "task-definition/hello_world:20"; + + static final AmazonECS ecs = mock(AmazonECS.class); + static final AmazonClientProvider clientProvider = mock(AmazonClientProvider.class); + final ProviderCache providerCache = mock(ProviderCache.class); + final AWSCredentialsProvider credentialsProvider = mock(AWSCredentialsProvider.class); + final Registry registry = mock(Registry.class); + + @BeforeClass + public static void setUp() { + when(clientProvider.getAmazonEcs(anyString(), any(AWSCredentialsProvider.class), anyString())).thenReturn(ecs); + } + +} diff --git a/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/EcsClusterCacheTest.java b/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/EcsClusterCacheTest.java new file mode 100644 index 00000000000..3935075ce51 --- /dev/null +++ b/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/EcsClusterCacheTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2017 Lookout, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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.provider.agent; + +import com.amazonaws.services.ecs.model.ListClustersRequest; +import com.amazonaws.services.ecs.model.ListClustersResult; +import com.netflix.spinnaker.cats.agent.CacheResult; +import com.netflix.spinnaker.cats.cache.CacheData; +import com.netflix.spinnaker.clouddriver.ecs.cache.Keys; +import com.netflix.spinnaker.clouddriver.ecs.cache.client.EcsClusterCacheClient; +import com.netflix.spinnaker.clouddriver.ecs.cache.model.EcsCluster; +import org.junit.Assert; +import org.junit.Test; +import spock.lang.Subject; + +import java.util.Collection; + +import static com.netflix.spinnaker.clouddriver.ecs.cache.Keys.Namespace.ECS_CLUSTERS; +import static junit.framework.TestCase.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; + + +public class EcsClusterCacheTest extends CommonCachingAgent { + @Subject + private final EcsClusterCachingAgent agent = new EcsClusterCachingAgent(ACCOUNT, REGION, clientProvider, credentialsProvider); + @Subject + private final EcsClusterCacheClient client = new EcsClusterCacheClient(providerCache); + + @Test + public void shouldRetrieveFromWrittenCache() { + //Given + String key = Keys.getClusterKey(ACCOUNT, REGION, CLUSTER_NAME_1); + ListClustersResult listClustersResult = new ListClustersResult().withClusterArns(CLUSTER_ARN_1); + when(ecs.listClusters(any(ListClustersRequest.class))).thenReturn(listClustersResult); + + //When + CacheResult cacheResult = agent.loadData(providerCache); + when(providerCache.get(ECS_CLUSTERS.toString(), key)).thenReturn(cacheResult.getCacheResults().get(ECS_CLUSTERS.toString()).iterator().next()); + + //Then + Collection cacheData = cacheResult.getCacheResults().get(ECS_CLUSTERS.toString()); + EcsCluster ecsCluster = client.get(key); + + assertTrue("Expected CacheData to be returned but null is returned", cacheData != null); + assertTrue("Expected 1 CacheData but returned " + cacheData.size(), cacheData.size() == 1); + String retrievedKey = cacheData.iterator().next().getId(); + assertTrue("Expected CacheData with ID " + key + " but retrieved ID " + retrievedKey, retrievedKey.equals(key)); + + Assert.assertTrue("Expected cluster name to be " + CLUSTER_NAME_1 + " but got " + ecsCluster.getName(), CLUSTER_NAME_1.equals(ecsCluster.getName())); + Assert.assertTrue("Expected cluster ARN to be " + CLUSTER_ARN_1 + " but got " + ecsCluster.getArn(), CLUSTER_ARN_1.equals(ecsCluster.getArn())); + Assert.assertTrue("Expected cluster account to be " + ACCOUNT + " but got " + ecsCluster.getAccount(), ACCOUNT.equals(ecsCluster.getAccount())); + Assert.assertTrue("Expected cluster region to be " + REGION + " but got " + ecsCluster.getRegion(), REGION.equals(ecsCluster.getRegion())); + } +} diff --git a/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/EcsClusterCachingAgentTest.java b/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/EcsClusterCachingAgentTest.java new file mode 100644 index 00000000000..08c13e65271 --- /dev/null +++ b/clouddriver-ecs/src/test/java/com/netflix/spinnaker/clouddriver/ecs/provider/agent/EcsClusterCachingAgentTest.java @@ -0,0 +1,100 @@ +/* + * Copyright 2017 Lookout, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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.provider.agent; + +import com.amazonaws.services.ecs.model.ListClustersRequest; +import com.amazonaws.services.ecs.model.ListClustersResult; +import com.netflix.spinnaker.cats.agent.CacheResult; +import com.netflix.spinnaker.cats.cache.CacheData; +import com.netflix.spinnaker.clouddriver.ecs.cache.Keys; +import org.junit.Test; +import spock.lang.Subject; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.netflix.spinnaker.clouddriver.ecs.cache.Keys.Namespace.ECS_CLUSTERS; +import static junit.framework.TestCase.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; + + +public class EcsClusterCachingAgentTest extends CommonCachingAgent { + @Subject + private final EcsClusterCachingAgent agent = new EcsClusterCachingAgent(ACCOUNT, REGION, clientProvider, credentialsProvider); + + @Test + public void shouldGetListOfArns() { + //Given + ListClustersResult listClustersResult = new ListClustersResult().withClusterArns(CLUSTER_ARN_1, CLUSTER_ARN_2); + when(ecs.listClusters(any(ListClustersRequest.class))).thenReturn(listClustersResult); + + //When + List clusterArns = agent.getItems(ecs, providerCache); + + //Then + assertTrue("Expected the list to contain 2 ECS cluster ARNs " + clusterArns.size(), clusterArns.size() == 2); + assertTrue("Expected the list to contain " + CLUSTER_ARN_1 + ", but it does not. It contains: " + clusterArns, clusterArns.contains(CLUSTER_ARN_1)); + assertTrue("Expected the list to contain " + CLUSTER_ARN_2 + ", but it does not. It contains: " + clusterArns, clusterArns.contains(CLUSTER_ARN_2)); + } + + @Test + public void shouldGenerateFreshData() { + //Given + Set clusterArns = new HashSet<>(); + clusterArns.add(CLUSTER_ARN_1); + clusterArns.add(CLUSTER_ARN_2); + + Set keys = new HashSet<>(); + keys.add(Keys.getClusterKey(ACCOUNT, REGION, CLUSTER_NAME_1)); + keys.add(Keys.getClusterKey(ACCOUNT, REGION, CLUSTER_NAME_2)); + + //When + Map> dataMap = agent.generateFreshData(clusterArns); + + //Then + assertTrue("Expected the data map to contain 1 namespace, but it contains " + dataMap.keySet().size() + " namespaces.", dataMap.keySet().size() == 1); + assertTrue("Expected the data map to contain " + ECS_CLUSTERS.toString() + " namespace, but it contains " + dataMap.keySet() + " namespaces.", dataMap.containsKey(ECS_CLUSTERS.toString())); + assertTrue("Expected there to be 2 CacheData, instead there is "+ dataMap.get(ECS_CLUSTERS.toString()).size(), dataMap.get(ECS_CLUSTERS.toString()).size() == 2); + + for (CacheData cacheData : dataMap.get(ECS_CLUSTERS.toString())) { + assertTrue("Expected the key to be one of the following keys: " + keys.toString() + ". The key is: " + cacheData.getId() +".", keys.contains(cacheData.getId())); + assertTrue("Expected the cluster ARN to be one of the following ARNs: " + clusterArns.toString() + ". The cluster ARN is: " + cacheData.getAttributes().get("clusterArn") +".", clusterArns.contains(cacheData.getAttributes().get("clusterArn"))); + } + } + + @Test + public void shouldAddToCache() { + //Given + String key = Keys.getClusterKey(ACCOUNT, REGION, CLUSTER_NAME_1); + ListClustersResult listClustersResult = new ListClustersResult().withClusterArns(CLUSTER_ARN_1); + when(ecs.listClusters(any(ListClustersRequest.class))).thenReturn(listClustersResult); + + //When + CacheResult cacheResult = agent.loadData(providerCache); + + //Then + Collection cacheData = cacheResult.getCacheResults().get(ECS_CLUSTERS.toString()); + assertTrue("Expected CacheData to be returned but null is returned", cacheData != null); + assertTrue("Expected 1 CacheData but returned " + cacheData.size(), cacheData.size() == 1); + String retrievedKey = cacheData.iterator().next().getId(); + assertTrue("Expected CacheData with ID " + key + " but retrieved ID " + retrievedKey, retrievedKey.equals(key)); + } +}