/*******************************************************************************
* Copyright 2012 Urbancode, 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.urbancode.terraform.tasks.aws;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.log4j.Logger;
import com.amazonaws.services.elasticloadbalancing.AmazonElasticLoadBalancing;
import com.amazonaws.services.elasticloadbalancing.model.Listener;
import com.urbancode.terraform.tasks.aws.helpers.AWSHelper;
import com.urbancode.terraform.tasks.common.exceptions.EnvironmentCreationException;
import com.urbancode.terraform.tasks.common.exceptions.EnvironmentDestructionException;
import com.urbancode.x2o.tasks.SubTask;
public class LoadBalancerTask extends SubTask {
//**********************************************************************************************
// CLASS
//**********************************************************************************************
final static private Logger log = Logger.getLogger(LoadBalancerTask.class);
//**********************************************************************************************
// INSTANCE
//**********************************************************************************************
private AmazonElasticLoadBalancing elbClient;
private AWSHelper helper;
private ContextAWS context;
private String loadBalancerName;
private String subnetName;
private List<String> subnetNamesList;
private String appCookieName;
private String DNSName;
private String zones;
private List<String> zonesList;
private HealthCheckTask healthCheck;
private String fullName;
private List<SecurityGroupRefTask> secGroupRefs = new ArrayList<SecurityGroupRefTask>();
private List<ListenerTask> listeners = new ArrayList<ListenerTask>();
//----------------------------------------------------------------------------------------------
public LoadBalancerTask(ContextAWS context) {
this.context = context;
helper = new AWSHelper();
}
//----------------------------------------------------------------------------------------------
private List<String> parseStringToList(String string) {
List<String> result = new ArrayList<String>();
// split on whitespace
String[] tmp = string.split("\\s+");
result = Arrays.asList(tmp);
log.debug("Creating list out of " + string);
for (String s : result) {
s.trim();
log.debug(s);
}
return result;
}
//----------------------------------------------------------------------------------------------
public void setZones(String zones) {
this.zones = zones;
zonesList = parseStringToList(zones);
}
//----------------------------------------------------------------------------------------------
public void setFullName(String fullName) {
this.fullName = fullName;
}
//----------------------------------------------------------------------------------------------
public void setName(String loadBalancerName) {
this.loadBalancerName = loadBalancerName;
}
//----------------------------------------------------------------------------------------------
public void setSubnetName(String subnetName) {
this.subnetName = subnetName;
subnetNamesList = parseStringToList(subnetName);
}
//----------------------------------------------------------------------------------------------
public void setDnsName(String DNSName) {
this.DNSName = DNSName;
}
//----------------------------------------------------------------------------------------------
public void setAppCookieName(String appCookieName) {
this.appCookieName = appCookieName;
}
//----------------------------------------------------------------------------------------------
public String getFullName() {
return fullName;
}
//----------------------------------------------------------------------------------------------
public String getName() {
return loadBalancerName;
}
//----------------------------------------------------------------------------------------------
public String getSubnetName() {
return subnetName;
}
//----------------------------------------------------------------------------------------------
public List<ListenerTask> getListeners() {
return Collections.unmodifiableList(listeners);
}
//----------------------------------------------------------------------------------------------
public List<SecurityGroupRefTask> getSecRefs() {
return Collections.unmodifiableList(secGroupRefs);
}
//----------------------------------------------------------------------------------------------
public String getDnsName() {
return DNSName;
}
//----------------------------------------------------------------------------------------------
public HealthCheckTask getHealthCheck() {
return healthCheck;
}
//----------------------------------------------------------------------------------------------
public String getAppCookieName() {
return appCookieName;
}
//----------------------------------------------------------------------------------------------
public String getZones() {
return zones;
}
//----------------------------------------------------------------------------------------------
public boolean containsZone(String zone) {
boolean result = false;
if (zonesList != null && zonesList.contains(zone)) {
result = true;
}
return result;
}
//----------------------------------------------------------------------------------------------
public boolean containsSubnet(String subnetName) {
boolean result = false;
if (subnetNamesList != null && subnetNamesList.contains(subnetName)) {
result = true;
}
return result;
}
//----------------------------------------------------------------------------------------------
public VpcSecurityGroupRefTask createVpcSecurityGroupRef() {
VpcSecurityGroupRefTask group = new VpcSecurityGroupRefTask(context);
secGroupRefs.add(group);
return group;
}
//----------------------------------------------------------------------------------------------
public HealthCheckTask createHealthCheck() {
healthCheck = new HealthCheckTask(context);
return healthCheck;
}
//----------------------------------------------------------------------------------------------
public ListenerTask createListener() {
ListenerTask listener = new ListenerTask(context);
listeners.add(listener);
return listener;
}
//----------------------------------------------------------------------------------------------
private List<String> resolveSubnetIds(String subnetName)
throws Exception {
List<String> result = new ArrayList<String>();
VpcTask vpc = null;
// make sure we have a context and an EnvironmentAWS
if (context != null && context.getEnvironment() != null
&& context.getEnvironment() instanceof EnvironmentTaskAWS) {
EnvironmentTaskAWS env = (EnvironmentTaskAWS) context.getEnvironment();
// grab the vpc
if (env.getVpc() != null) {
vpc = env.getVpc();
}
else {
throw new NullPointerException("No VPC found for load balancer "
+ fullName);
}
}
// prevent against duplicate Zones in subnets
List<String> usedZones = new ArrayList<String>();
if (subnetNamesList != null) {
for (String name : subnetNamesList) {
SubnetTask tmp = vpc.findSubnetForName(name);
if (tmp != null && tmp.getId() != null) {
log.debug("Adding subnetId " + tmp.getId() + " to load balancer "
+ fullName);
String curZone = tmp.getZone();
if (!usedZones.contains(curZone)) {
usedZones.add(curZone);
result.add(tmp.getId());
}
else {
log.warn("Not adding Subnet " + tmp.getName() + " to load balancer "
+ fullName + " because zone " + tmp.getZone() + " is "
+ "already used on the load balancer.");
}
}
else {
log.error("Could not find subnet " + name + " in the VPC");
}
}
}
else {
log.error("No Subnets listed");
throw new NullPointerException("Loadbalancer must have a list of Subnet names if " +
"launching into VPC");
}
return result;
}
//----------------------------------------------------------------------------------------------
private List<String> resolveSecGroupIds(List<SecurityGroupRefTask> list)
throws Exception {
List<String> result = new ArrayList<String>();
if (list != null && !list.isEmpty()) {
// Check out the env
EnvironmentTaskAWS env = null;
env = (EnvironmentTaskAWS) context.getEnvironment();
for (SecurityGroupRefTask ref : list) {
String id = null;
if (ref instanceof VpcSecurityGroupRefTask) {
VpcSecurityGroupTask tmp =
env.getVpc().findSecurityGroupForName(ref.getSecurityGroupName());
if (tmp != null) {
id = tmp.getId();
}
else {
log.error("Security Group " + ref.getSecurityGroupName() +
" not found in VPC in environment " + env.getName());
}
}
result.add(id);
}
}
return result;
}
//----------------------------------------------------------------------------------------------
private List<String> resolveZones(String zones) {
// prevent duplicate zones
List<String> usedZones = new ArrayList<String>();
if (zonesList != null) {
for (String zone : zonesList) {
if (!usedZones.contains(zone)) {
usedZones.add(zone);
}
else {
log.warn("");
}
}
}
return usedZones;
}
//----------------------------------------------------------------------------------------------
@Override
public void create()
throws EnvironmentCreationException {
if (DNSName == null) {
if (elbClient == null) {
elbClient = context.fetchELBClient();
}
String uuid = context.getEnvironment().fetchSuffix();
fullName = loadBalancerName + ("-" + uuid);
log.debug("Security Group " + loadBalancerName + " has fullname " + fullName);
String stickyPolicyName = "StickyPolicy";
long defaultCookieExp = 60000;
// get amazon ids
List<String> subnetIds = null;
List<String> availZones = null;
List<String> secGroupIds = null;
try {
if (subnetName != null && !subnetName.isEmpty()) {
subnetIds = resolveSubnetIds(subnetName);
}
else {
log.warn("No subnets specified on load balancer " + fullName);
if (zones != null && !zones.isEmpty()) {
availZones = resolveZones(zones);
}
else {
log.warn("No zones specified on load balancer " + fullName);
throw new EnvironmentCreationException("Must specify either zones or "
+ "subnets on load balancer " + fullName);
}
}
secGroupIds = resolveSecGroupIds(getSecRefs());
List<Listener> listeners = new ArrayList<Listener>();
if (getListeners() != null) {
for (ListenerTask task : getListeners()) {
Listener tmp = new Listener(task.getProtocol(),
task.getLoadBalancerPort(), task.getInstancePort());
if (task.isSecure()) {
tmp.setSSLCertificateId(task.getCertId());
}
listeners.add(tmp);
}
}
else {
log.warn("No listeners specified for LoadBalancer: " + fullName
+ "\nThis load balancer is not configured to balance any "
+ "instances.");
}
// launch the load balancer
DNSName = helper.launchLoadBalancer(fullName, subnetIds, secGroupIds,
listeners, availZones, elbClient);
// configure sticky sessions
helper.createStickyPolicy(fullName, stickyPolicyName,
getAppCookieName(), defaultCookieExp, elbClient);
// configure the HealthChecks on the instances for them to be registered
if (getHealthCheck() != null) {
String hcTarget = getHealthCheck().getProtocol() + ":" + getHealthCheck()
.getPort() + getHealthCheck().getPath();
int health = getHealthCheck().getHealthyCount();
int unhealth = getHealthCheck().getUnhealthyCount();
int interval = getHealthCheck().getInterval();
int timeout = getHealthCheck().getTimeout();
helper.setupHealthCheck(fullName, hcTarget, health, unhealth, interval,
timeout, elbClient);
}
else {
log.warn("No HealthCheck specified for load balancer " + fullName
+ "\nYou may not be able to reach the instances behind this "
+ "load balancer.");
}
}
catch (Exception e) {
log.error("Could not create load balancer " + fullName + " completely");
throw new EnvironmentCreationException("Could not start load balancer " +
fullName, e);
}
finally {
elbClient = null;
}
}
}
//----------------------------------------------------------------------------------------------
@Override
public void destroy()
throws EnvironmentDestructionException {
if (elbClient == null) {
elbClient = context.fetchELBClient();
}
try {
helper = context.getAWSHelper();
helper.deleteLoadBalancer(fullName, elbClient);
// will auto-delete policies and listeners associated with loadBalancer
}
catch (Exception e) {
log.error("Could not create load balancer " + fullName + " completely");
throw new EnvironmentDestructionException("Could not destroy load balancer " +
fullName, e);
}
finally {
setDnsName(null);
elbClient = null;
}
}
}