Skip to content
This repository has been archived by the owner on Mar 4, 2021. It is now read-only.

Commit

Permalink
Added a new CrossZoneLoadBalancing conformity rule and a test
Browse files Browse the repository at this point in the history
  • Loading branch information
Marek Radonsky committed Sep 30, 2014
1 parent 4ac9a55 commit ad8b8d5
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 1 deletion.
@@ -0,0 +1,133 @@
/*
*
* Copyright 2013 Netflix, 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.simianarmy.aws.conformity.rule;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.services.elasticloadbalancing.model.LoadBalancerAttributes;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.netflix.simianarmy.client.aws.AWSClient;
import com.netflix.simianarmy.conformity.AutoScalingGroup;
import com.netflix.simianarmy.conformity.Cluster;
import com.netflix.simianarmy.conformity.Conformity;
import com.netflix.simianarmy.conformity.ConformityRule;

/**
* The class implementing a conformity rule that checks if the cross-zone load balancing is enabled
* for all cluster ELBs.
*/
public class CrossZoneLoadBalancing implements ConformityRule {
private static final Logger LOGGER = LoggerFactory.getLogger(CrossZoneLoadBalancing.class);

private final Map<String, AWSClient> regionToAwsClient = Maps.newHashMap();

private AWSCredentialsProvider awsCredentialsProvider;

private static final String RULE_NAME = "CrossZoneLoadBalancing";
private static final String REASON = "Cross-zone load balancing is disabled";

/**
* Constructs an instance with the default AWS credentials provider chain.
* @see com.amazonaws.auth.DefaultAWSCredentialsProviderChain
*/
public CrossZoneLoadBalancing() {
this(new DefaultAWSCredentialsProviderChain());
}

/**
* Constructs an instance with the passed AWS Credential Provider.
* @param awsCredentialsProvider
*/
public CrossZoneLoadBalancing(AWSCredentialsProvider awsCredentialsProvider) {
this.awsCredentialsProvider = awsCredentialsProvider;
}

@Override
public Conformity check(Cluster cluster) {
Collection<String> failedComponents = Lists.newArrayList();
for (AutoScalingGroup asg : cluster.getAutoScalingGroups()) {
for (String lbName : getLoadBalancerNamesForAsg(cluster.getRegion(), asg.getName())) {
if (!isCrossZoneLoadBalancingEnabled(cluster.getRegion(), lbName)) {
LOGGER.info(String.format("ELB %s in %s does not have cross-zone load balancing enabled",
lbName, cluster.getRegion()));
failedComponents.add(lbName);
}
}
}
return new Conformity(getName(), failedComponents);
}

/**
* Gets the cross-zone load balancing option for an ELB. Can be overridden in subclasses.
* @param region the region
* @param lbName the ELB name
* @return {@code true} if cross-zone load balancing is enabled
*/
protected boolean isCrossZoneLoadBalancingEnabled(String region, String lbName) {
LoadBalancerAttributes attrs = getAwsClient(region).describeElasticLoadBalancerAttributes(lbName);
return attrs.getCrossZoneLoadBalancing().isEnabled();
}

@Override
public String getName() {
return RULE_NAME;
}

@Override
public String getNonconformingReason() {
return REASON;
}

/**
* Gets the load balancer names of an ASG. Can be overridden in subclasses.
* @param region the region
* @param asgName the ASG name
* @return the list of load balancer names
*/
protected List<String> getLoadBalancerNamesForAsg(String region, String asgName) {
List<com.amazonaws.services.autoscaling.model.AutoScalingGroup> asgs =
getAwsClient(region).describeAutoScalingGroups(asgName);
if (asgs.isEmpty()) {
LOGGER.error(String.format("Not found ASG with name %s", asgName));
return Collections.emptyList();
} else {
return asgs.get(0).getLoadBalancerNames();
}
}

private AWSClient getAwsClient(String region) {
AWSClient awsClient = regionToAwsClient.get(region);
if (awsClient == null) {
awsClient = new AWSClient(region, awsCredentialsProvider);
regionToAwsClient.put(region, awsClient);
}
return awsClient;
}



}
Expand Up @@ -26,6 +26,7 @@
import com.netflix.simianarmy.aws.conformity.crawler.AWSClusterCrawler;
import com.netflix.simianarmy.aws.conformity.rule.BasicConformityEurekaClient;
import com.netflix.simianarmy.aws.conformity.rule.ConformityEurekaClient;
import com.netflix.simianarmy.aws.conformity.rule.CrossZoneLoadBalancing;
import com.netflix.simianarmy.aws.conformity.rule.InstanceHasHealthCheckUrl;
import com.netflix.simianarmy.aws.conformity.rule.InstanceHasStatusUrl;
import com.netflix.simianarmy.aws.conformity.rule.InstanceInSecurityGroup;
Expand All @@ -42,6 +43,7 @@
import com.netflix.simianarmy.conformity.ConformityMonkey;
import com.netflix.simianarmy.conformity.ConformityRule;
import com.netflix.simianarmy.conformity.ConformityRuleEngine;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -150,6 +152,11 @@ public BasicConformityMonkeyContext() {
ruleEngine.addRule(new InstanceInVPC(getAwsCredentialsProvider()));
}

if (configuration().getBoolOrElse(
"simianarmy.conformity.rule.CrossZoneLoadBalancing.enabled", false)) {
ruleEngine().addRule(new CrossZoneLoadBalancing(getAwsCredentialsProvider()));
}

createClient(region());
regionToAwsClient.put(region(), awsClient());

Expand Down
20 changes: 19 additions & 1 deletion src/main/java/com/netflix/simianarmy/client/aws/AWSClient.java
Expand Up @@ -62,8 +62,11 @@
import com.amazonaws.services.ec2.model.TerminateInstancesRequest;
import com.amazonaws.services.ec2.model.Volume;
import com.amazonaws.services.elasticloadbalancing.AmazonElasticLoadBalancingClient;
import com.amazonaws.services.elasticloadbalancing.model.DescribeLoadBalancerAttributesRequest;
import com.amazonaws.services.elasticloadbalancing.model.DescribeLoadBalancerAttributesResult;
import com.amazonaws.services.elasticloadbalancing.model.DescribeLoadBalancersRequest;
import com.amazonaws.services.elasticloadbalancing.model.DescribeLoadBalancersResult;
import com.amazonaws.services.elasticloadbalancing.model.LoadBalancerAttributes;
import com.amazonaws.services.elasticloadbalancing.model.LoadBalancerDescription;
import com.amazonaws.services.simpledb.AmazonSimpleDB;
import com.amazonaws.services.simpledb.AmazonSimpleDBClient;
Expand Down Expand Up @@ -306,7 +309,22 @@ public List<LoadBalancerDescription> describeElasticLoadBalancers(String... name
return elbs;
}


/**
* Describe a set of specific ELBs.
*
* @param names the ELB names
* @return the ELBs
*/
public LoadBalancerAttributes describeElasticLoadBalancerAttributes(String name) {
LOGGER.info(String.format("Getting attributes for ELB with name '%s' in region %s.", name, region));
AmazonElasticLoadBalancingClient elbClient = elbClient();
DescribeLoadBalancerAttributesRequest request = new DescribeLoadBalancerAttributesRequest().withLoadBalancerName(name);
DescribeLoadBalancerAttributesResult result = elbClient.describeLoadBalancerAttributes(request);
LoadBalancerAttributes attrs = result.getLoadBalancerAttributes();
LOGGER.info(String.format("Got attributes for ELB with name '%s' in region %s.", name, region));
return attrs;
}

/**
* Describe a set of specific auto-scaling instances.
*
Expand Down
@@ -0,0 +1,83 @@
// CHECKSTYLE IGNORE Javadoc
/*
*
* Copyright 2013 Netflix, 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.simianarmy.conformity;

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

import junit.framework.Assert;

import org.apache.commons.lang.StringUtils;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import com.google.common.collect.Maps;
import com.netflix.simianarmy.aws.conformity.rule.CrossZoneLoadBalancing;

public class TestCrossZoneLoadBalancing extends CrossZoneLoadBalancing {
private final Map<String, String> asgToElbs = Maps.newHashMap();
private final Map<String, Boolean> elbsToCZLB = Maps.newHashMap();

@BeforeClass
private void init() {
asgToElbs.put("asg1", "elb1,elb2");
asgToElbs.put("asg2", "elb1");
asgToElbs.put("asg3", "");
elbsToCZLB.put("elb1", true);
}

@Test
public void testDisabledCrossZoneLoadBalancing() {
Cluster cluster = new Cluster("cluster1", "us-east-1", new AutoScalingGroup("asg1"));
Conformity result = check(cluster);
Assert.assertEquals(result.getRuleId(), getName());
Assert.assertEquals(result.getFailedComponents().size(), 1);
Assert.assertEquals(result.getFailedComponents().iterator().next(), "elb2");
}

@Test
public void testEnabledCrossZoneLoadBalancing() {
Cluster cluster = new Cluster("cluster1", "us-east-1", new AutoScalingGroup("asg2"));
Conformity result = check(cluster);
Assert.assertEquals(result.getRuleId(), getName());
Assert.assertEquals(result.getFailedComponents().size(), 0);
}

@Test
public void testAsgWithoutElb() {
Cluster cluster = new Cluster("cluster3", "us-east-1", new AutoScalingGroup("asg3"));
Conformity result = check(cluster);
Assert.assertEquals(result.getRuleId(), getName());
Assert.assertEquals(result.getFailedComponents().size(), 0);
}

@Override
protected List<String> getLoadBalancerNamesForAsg(String region, String asgName) {
return Arrays.asList(StringUtils.split(asgToElbs.get(asgName), ","));
}

@Override
protected boolean isCrossZoneLoadBalancingEnabled(String region, String lbName) {
Boolean enabled = elbsToCZLB.get(lbName);
return (enabled == null) ? false : enabled;
}


}

0 comments on commit ad8b8d5

Please sign in to comment.