Skip to content

Commit

Permalink
feat(provider/ecs): ECS Target Group cache client and tests. (#2265)
Browse files Browse the repository at this point in the history
* Added EcsTargetGroupCacheClient with a model and tests.

* Changes based on feedback.
  • Loading branch information
dkirillov authored and robzienert committed Jan 9, 2018
1 parent da0ea3e commit a267bd0
Show file tree
Hide file tree
Showing 3 changed files with 248 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* 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.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.spinnaker.cats.cache.Cache;
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.model.loadbalancer.EcsTargetGroup;
import org.springframework.stereotype.Component;

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 java.util.stream.Collectors;

import static com.netflix.spinnaker.clouddriver.core.provider.agent.Namespace.LOAD_BALANCERS;
import static com.netflix.spinnaker.clouddriver.core.provider.agent.Namespace.TARGET_GROUPS;

@Component
public class EcsTargetGroupCacheClient {

private final Cache cacheView;
private final ObjectMapper objectMapper;

public EcsTargetGroupCacheClient(Cache cacheView, ObjectMapper objectMapper) {
this.cacheView = cacheView;
this.objectMapper = objectMapper;
}

public List<EcsTargetGroup> findAll() {
String searchKey = Keys.getTargetGroupKey("*", "*", "*", "*") + "*";
Collection<String> targetGroupKeys = cacheView.filterIdentifiers(TARGET_GROUPS.getNs(), searchKey);

Set<Map<String, Object>> targetGroupAttributes = fetchLoadBalancerAttributes(targetGroupKeys);

List<EcsTargetGroup> targetGroups = convertToTargetGroup(targetGroupAttributes);

return targetGroups;
}

private EcsTargetGroup convertToTargetGroup(Map<String, Object> targetGroupAttributes) {
EcsTargetGroup ecsTargetGroup = objectMapper.convertValue(targetGroupAttributes, EcsTargetGroup.class);
return ecsTargetGroup;
}

private List<EcsTargetGroup> convertToTargetGroup(Collection<Map<String, Object>> targetGroupAttributes) {
List<EcsTargetGroup> ecsTargetGroups = new ArrayList<>();

for (Map<String, Object> attributes : targetGroupAttributes) {
ecsTargetGroups.add(convertToTargetGroup(attributes));
}

return ecsTargetGroups;
}


private Set<Map<String, Object>> fetchLoadBalancerAttributes(Collection<String> targetGroupKeys) {
Set<CacheData> targetGroups = fetchTargetGroups(targetGroupKeys);

Set<Map<String, Object>> targetGroupAttributes = targetGroups.stream()
.filter(this::hashLoadBalancers)
.map(CacheData::getAttributes)
.collect(Collectors.toSet());

return targetGroupAttributes;
}

private boolean hashLoadBalancers(CacheData targetGroupCache) {
return targetGroupCache.getRelationships().get("loadBalancers") != null
&& targetGroupCache.getRelationships().get("loadBalancers").size() > 0;
}

private Set<Map<String, Object>> retrieveTargetGroups(Set<String> targetGroupsAssociatedWithLoadBalancers) {
Collection<CacheData> targetGroupCache = cacheView.getAll(TARGET_GROUPS.getNs(), targetGroupsAssociatedWithLoadBalancers);

Set<Map<String, Object>> targetGroupAttributes = targetGroupCache
.stream()
.map(CacheData::getAttributes)
.collect(Collectors.toSet());

return targetGroupAttributes;
}


private Set<String> inferAssociatedTargetGroups(Set<CacheData> loadBalancers) {
Set<String> targetGroupsAssociatedWithLoadBalancers = new HashSet<>();

for (CacheData loadBalancer : loadBalancers) {
Collection<String> relatedTargetGroups = loadBalancer.getRelationships().get("targetGroups");
if (relatedTargetGroups != null && relatedTargetGroups.size() > 0) {
targetGroupsAssociatedWithLoadBalancers.addAll(relatedTargetGroups);
}
}
return targetGroupsAssociatedWithLoadBalancers;
}

private Set<CacheData> fetchTargetGroups(Collection<String> targetGroupKeys) {
return new HashSet<>(cacheView.getAll(TARGET_GROUPS.getNs(),
targetGroupKeys,
RelationshipCacheFilter.include(LOAD_BALANCERS.getNs())));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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.model.loadbalancer;

import com.netflix.spinnaker.clouddriver.model.LoadBalancerProvider;
import lombok.Data;

import java.util.List;
import java.util.Map;

@Data
public class EcsTargetGroup implements LoadBalancerProvider.Details {
// TODO - add fields we want to persist from the cached value for target groups

List<String> loadBalancerNames;
List<String> instances;
Integer healthCheckTimeoutSeconds;
String targetGroupArn;
String healthCheckPort;
Map<String, String> matcher;
String healthCheckProtocol;
String targetGroupName;
String healthCheckPath;
String protocol;
Integer port;
Integer healthCheckIntervalSeconds;
Integer healthyThresholdCount;
String vpcId;
Integer unhealthyThresholdCount;
Map<String, String> attributes;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* 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.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.netflix.spinnaker.cats.cache.Cache
import com.netflix.spinnaker.cats.cache.DefaultCacheData
import com.netflix.spinnaker.clouddriver.aws.data.Keys
import com.netflix.spinnaker.clouddriver.ecs.cache.client.EcsTargetGroupCacheClient
import com.netflix.spinnaker.clouddriver.ecs.model.loadbalancer.EcsTargetGroup
import spock.lang.Specification
import spock.lang.Subject

class EcsTargetGroupCacheClientSpec extends Specification {
def cacheView = Mock(Cache)
def objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

@Subject
EcsTargetGroupCacheClient client = new EcsTargetGroupCacheClient(cacheView, objectMapper)

def 'should convert cache data into object'() {
given:
def loadBalancerName = 'test-name'
def account = 'test-account'
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, vpcId)

def givenEcsTargetGroup = new EcsTargetGroup(
loadBalancerNames: [loadBalancerName],
instances: [],
healthCheckTimeoutSeconds: 30,
targetGroupArn: 'arn',
healthCheckPort: 1337,
matcher: [:],
healthCheckProtocol: 'http',
targetGroupName: targetGroupName,
healthCheckPath: '/',
protocol: 'http',
port: 1337,
healthCheckIntervalSeconds: 30,
healthyThresholdCount: 5,
vpcId: vpcId,
unhealthyThresholdCount: 5,
attributes: [:],
)

def attributes = objectMapper.convertValue(givenEcsTargetGroup, Map)
def relations = [loadBalancers: [loadbalancerKey]]

when:
def retrievedLoadbalancers = client.findAll()

then:
cacheView.filterIdentifiers(_, _) >> Collections.singleton(targetGroupKey)
cacheView.getAll(_, _, _) >> Collections.singleton(new DefaultCacheData(targetGroupKey, attributes, relations))
retrievedLoadbalancers.size() == 1
retrievedLoadbalancers.get(0) == givenEcsTargetGroup
}
}

0 comments on commit a267bd0

Please sign in to comment.