From 596ca233e347d8f5a7634ddc6d2b2987dc15e70d Mon Sep 17 00:00:00 2001 From: Lili Nader Date: Thu, 20 May 2010 01:16:57 -0400 Subject: [PATCH] Elastic Load Balancer --- .../main/java/org/jclouds/aws/ec2/ELB.java | 39 ++ ...BindELBInstanceIdsToIndexedFormParams.java | 46 ++ .../strategy/EC2LoadBalancerStrategy.java | 92 +++ .../aws/ec2/domain/ElasticLoadBalancer.java | 525 ++++++++++++++++++ .../ec2/functions/ELBRegionToEndpoint.java | 50 ++ .../ElasticLoadBalancerAsyncClient.java | 117 ++++ .../services/ElasticLoadBalancerClient.java | 81 +++ .../CreateLoadBalancerResponseHandler.java | 36 ++ .../DescribeLoadBalancersResponseHandler.java | 241 ++++++++ ...tancesWithLoadBalancerResponseHandler.java | 44 ++ .../ElasticLoadBalancerClientLiveTest.java | 109 ++++ ...scribeLoadBalancerResponseHandlerTest.java | 81 +++ .../resources/ec2/describe_loadbalancers.xml | 63 +++ .../strategy/LoadBalancerStrategy.java | 38 ++ 14 files changed, 1562 insertions(+) create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/ELB.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindELBInstanceIdsToIndexedFormParams.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/compute/strategy/EC2LoadBalancerStrategy.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/domain/ElasticLoadBalancer.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/functions/ELBRegionToEndpoint.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/services/ElasticLoadBalancerAsyncClient.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/services/ElasticLoadBalancerClient.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/xml/CreateLoadBalancerResponseHandler.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeLoadBalancersResponseHandler.java create mode 100644 aws/core/src/main/java/org/jclouds/aws/ec2/xml/RegisterInstancesWithLoadBalancerResponseHandler.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/services/ElasticLoadBalancerClientLiveTest.java create mode 100644 aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeLoadBalancerResponseHandlerTest.java create mode 100644 aws/core/src/test/resources/ec2/describe_loadbalancers.xml create mode 100644 compute/src/main/java/org/jclouds/compute/strategy/LoadBalancerStrategy.java diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/ELB.java b/aws/core/src/main/java/org/jclouds/aws/ec2/ELB.java new file mode 100644 index 0000000000..56a1fb8c53 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/ELB.java @@ -0,0 +1,39 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 org.jclouds.aws.ec2; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** + * Related to a ELB resource. + * + * @author Lili Nader + * + */ +@Retention(value = RetentionPolicy.RUNTIME) +@Target(value = { ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) +@Qualifier +public @interface ELB { + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindELBInstanceIdsToIndexedFormParams.java b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindELBInstanceIdsToIndexedFormParams.java new file mode 100644 index 0000000000..55e2ae2ca6 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/binders/BindELBInstanceIdsToIndexedFormParams.java @@ -0,0 +1,46 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 org.jclouds.aws.ec2.binders; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.inject.Singleton; + +import org.jclouds.aws.ec2.util.EC2Utils; +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.Binder; +import org.jclouds.rest.internal.GeneratedHttpRequest; + +/** + * Binds the String [] to form parameters named with InstanceId.index + * + * @author Adrian Cole + */ +@Singleton +public class BindELBInstanceIdsToIndexedFormParams implements Binder { + + @SuppressWarnings("unchecked") + public void bindToRequest(HttpRequest request, Object input) { + checkArgument(checkNotNull(request, "input") instanceof GeneratedHttpRequest, + "this binder is only valid for GeneratedHttpRequests!"); + EC2Utils.indexStringArrayToFormValuesWithStringFormat((GeneratedHttpRequest) request, "Instances.member.%s.InstanceId", input); + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/compute/strategy/EC2LoadBalancerStrategy.java b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/strategy/EC2LoadBalancerStrategy.java new file mode 100644 index 0000000000..5ac8091fbb --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/compute/strategy/EC2LoadBalancerStrategy.java @@ -0,0 +1,92 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 org.jclouds.aws.ec2.compute.strategy; + +import java.util.Set; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.aws.ec2.EC2Client; +import org.jclouds.aws.ec2.compute.config.EC2ComputeServiceContextModule.GetRegionFromLocation; +import org.jclouds.aws.ec2.domain.AvailabilityZone; +import org.jclouds.aws.ec2.services.ElasticLoadBalancerClient; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.compute.strategy.LoadBalancerStrategy; +import org.jclouds.domain.Location; +import org.jclouds.logging.Logger; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class EC2LoadBalancerStrategy implements LoadBalancerStrategy +{ + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + protected final EC2Client ec2Client; + protected final GetRegionFromLocation getRegionFromLocation; + + @Inject + protected EC2LoadBalancerStrategy(EC2Client ec2Client, GetRegionFromLocation getRegionFromLocation) + { + this.ec2Client = ec2Client; + this.getRegionFromLocation = getRegionFromLocation; + } + + @Override + public String execute(Location location, String name, + String protocol, Integer loadBalancerPort, Integer instancePort, + Set instanceIds) + { + String region = getRegionFromLocation.apply(location); + String dnsName = new String(); + + // TODO: Fix temp hack + String availabilityZone = new String(); + for (String az : AvailabilityZone.zones) + { + if (az.startsWith(region)) + availabilityZone = az; + } + + ElasticLoadBalancerClient elbClient = ec2Client + .getElasticLoadBalancerServices(); + + dnsName = elbClient.createLoadBalancer(region, name, protocol, + loadBalancerPort, instancePort, availabilityZone); + + Set registeredInstanceIds = elbClient + .registerInstancesWithLoadBalancer(region, name, + (String[]) instanceIds.toArray()); + + // deregister instances + boolean changed = registeredInstanceIds.removeAll(instanceIds); + if (changed) + elbClient.deregisterInstancesWithLoadBalancer(region, name, + (String[]) registeredInstanceIds.toArray()); + + return dnsName; + } +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/domain/ElasticLoadBalancer.java b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/ElasticLoadBalancer.java new file mode 100644 index 0000000000..7001ffc98a --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/domain/ElasticLoadBalancer.java @@ -0,0 +1,525 @@ +package org.jclouds.aws.ec2.domain; + +import java.util.HashSet; +import java.util.Set; + + +public class ElasticLoadBalancer implements Comparable +{ + + + private String region; + private String name; + private Set instanceIds; + private Set availabilityZones; + private String dnsName; + private AppCookieStickinessPolicy appCookieStickinessPolicy; + private LBCookieStickinessPolicy lBCookieStickinessPolicy; + private Set loadBalancerListeners; + + public ElasticLoadBalancer() + { + super(); + this.instanceIds = new HashSet(); + this.availabilityZones = new HashSet(); + this.loadBalancerListeners = new HashSet(); + } + + public ElasticLoadBalancer(String region, String name, + Set instanceIds, Set availabilityZones, + String dnsName) + { + super(); + this.region = region; + this.name = name; + this.instanceIds = instanceIds; + this.availabilityZones = availabilityZones; + this.dnsName = dnsName; + this.loadBalancerListeners = new HashSet(); + } + + + public void setRegion(String region) + { + this.region = region; + } + + public void setName(String name) + { + this.name = name; + } + + public void setInstanceIds(Set instanceIds) + { + this.instanceIds = instanceIds; + } + + public void setAvailabilityZones(Set availabilityZones) + { + this.availabilityZones = availabilityZones; + } + + public void setDnsName(String dnsName) + { + this.dnsName = dnsName; + } + + public void setAppCookieStickinessPolicy( + AppCookieStickinessPolicy appCookieStickinessPolicy) + { + this.appCookieStickinessPolicy = appCookieStickinessPolicy; + } + + public void setlBCookieStickinessPolicy( + LBCookieStickinessPolicy lBCookieStickinessPolicy) + { + this.lBCookieStickinessPolicy = lBCookieStickinessPolicy; + } + + public void setLoadBalancerListeners( + Set loadBalancerListeners) + { + this.loadBalancerListeners = loadBalancerListeners; + } + + public String getName() + { + return name; + } + + public Set getInstanceIds() + { + return instanceIds; + } + + public Set getAvailabilityZones() + { + return availabilityZones; + } + + public String getDnsName() + { + return dnsName; + } + + public AppCookieStickinessPolicy getAppCookieStickinessPolicy() + { + return appCookieStickinessPolicy; + } + + public LBCookieStickinessPolicy getlBCookieStickinessPolicy() + { + return lBCookieStickinessPolicy; + } + + public Set getLoadBalancerListeners() + { + return loadBalancerListeners; + } + + public String getRegion() + { + return region; + } + + @Override + public int compareTo(ElasticLoadBalancer that) + { + return name.compareTo(that.name); + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime + * result + + ((appCookieStickinessPolicy == null) ? 0 + : appCookieStickinessPolicy.hashCode()); + result = prime + * result + + ((availabilityZones == null) ? 0 : availabilityZones + .hashCode()); + result = prime * result + ((dnsName == null) ? 0 : dnsName.hashCode()); + result = prime * result + + ((instanceIds == null) ? 0 : instanceIds.hashCode()); + result = prime + * result + + ((lBCookieStickinessPolicy == null) ? 0 + : lBCookieStickinessPolicy.hashCode()); + result = prime + * result + + ((loadBalancerListeners == null) ? 0 : loadBalancerListeners + .hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((region == null) ? 0 : region.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ElasticLoadBalancer other = (ElasticLoadBalancer) obj; + if (appCookieStickinessPolicy == null) + { + if (other.appCookieStickinessPolicy != null) + return false; + } + else if (!appCookieStickinessPolicy + .equals(other.appCookieStickinessPolicy)) + return false; + if (availabilityZones == null) + { + if (other.availabilityZones != null) + return false; + } + else if (!availabilityZones.equals(other.availabilityZones)) + return false; + if (dnsName == null) + { + if (other.dnsName != null) + return false; + } + else if (!dnsName.equals(other.dnsName)) + return false; + if (instanceIds == null) + { + if (other.instanceIds != null) + return false; + } + else if (!instanceIds.equals(other.instanceIds)) + return false; + if (lBCookieStickinessPolicy == null) + { + if (other.lBCookieStickinessPolicy != null) + return false; + } + else if (!lBCookieStickinessPolicy + .equals(other.lBCookieStickinessPolicy)) + return false; + if (loadBalancerListeners == null) + { + if (other.loadBalancerListeners != null) + return false; + } + else if (!loadBalancerListeners.equals(other.loadBalancerListeners)) + return false; + if (name == null) + { + if (other.name != null) + return false; + } + else if (!name.equals(other.name)) + return false; + if (region == null) + { + if (other.region != null) + return false; + } + else if (!region.equals(other.region)) + return false; + return true; + } + + + + + + + public static class AppCookieStickinessPolicy + { + private String policyName; + private String cookieName; + + + public AppCookieStickinessPolicy() + { + super(); + } + + public AppCookieStickinessPolicy(String policyName, String cookieName) + { + super(); + this.policyName = policyName; + this.cookieName = cookieName; + } + + public String getPolicyName() + { + return policyName; + } + + public String getCookieName() + { + return cookieName; + } + + public void setPolicyName(String policyName) + { + this.policyName = policyName; + } + + public void setCookieName(String cookieName) + { + this.cookieName = cookieName; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + + ((cookieName == null) ? 0 : cookieName.hashCode()); + result = prime * result + + ((policyName == null) ? 0 : policyName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AppCookieStickinessPolicy other = (AppCookieStickinessPolicy) obj; + if (cookieName == null) + { + if (other.cookieName != null) + return false; + } + else if (!cookieName.equals(other.cookieName)) + return false; + if (policyName == null) + { + if (other.policyName != null) + return false; + } + else if (!policyName.equals(other.policyName)) + return false; + return true; + } + + + + } + + public static class LBCookieStickinessPolicy + { + private String policyName; + private Integer cookieExpirationPeriod; + + public LBCookieStickinessPolicy() + { + super(); + } + + public LBCookieStickinessPolicy(String policyName, + Integer cookieExpirationPeriod) + { + super(); + this.policyName = policyName; + this.cookieExpirationPeriod = cookieExpirationPeriod; + } + + public String getPolicyName() + { + return policyName; + } + + public Integer getCookieExpirationPeriod() + { + return cookieExpirationPeriod; + } + + public void setPolicyName(String policyName) + { + this.policyName = policyName; + } + + public void setCookieExpirationPeriod(Integer cookieExpirationPeriod) + { + this.cookieExpirationPeriod = cookieExpirationPeriod; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime + * result + + ((cookieExpirationPeriod == null) ? 0 + : cookieExpirationPeriod.hashCode()); + result = prime * result + + ((policyName == null) ? 0 : policyName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LBCookieStickinessPolicy other = (LBCookieStickinessPolicy) obj; + if (cookieExpirationPeriod == null) + { + if (other.cookieExpirationPeriod != null) + return false; + } + else if (!cookieExpirationPeriod + .equals(other.cookieExpirationPeriod)) + return false; + if (policyName == null) + { + if (other.policyName != null) + return false; + } + else if (!policyName.equals(other.policyName)) + return false; + return true; + } + + + + } + + public static class LoadBalancerListener + { + private Set policyNames; + private Integer instancePort; + private Integer loadBalancerPort; + private String protocol; + + public LoadBalancerListener(Set policyNames, + Integer instancePort, Integer loadBalancerPort, String protocol) + { + super(); + this.policyNames = policyNames; + this.instancePort = instancePort; + this.loadBalancerPort = loadBalancerPort; + this.protocol = protocol; + } + + public LoadBalancerListener() + { + super(); + } + + public Set getPolicyNames() + { + return policyNames; + } + + public Integer getInstancePort() + { + return instancePort; + } + + public Integer getLoadBalancerPort() + { + return loadBalancerPort; + } + + public String getProtocol() + { + return protocol; + } + + public void setPolicyNames(Set policyNames) + { + this.policyNames = policyNames; + } + + public void setInstancePort(Integer instancePort) + { + this.instancePort = instancePort; + } + + public void setLoadBalancerPort(Integer loadBalancerPort) + { + this.loadBalancerPort = loadBalancerPort; + } + + public void setProtocol(String protocol) + { + this.protocol = protocol; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + + ((instancePort == null) ? 0 : instancePort.hashCode()); + result = prime + * result + + ((loadBalancerPort == null) ? 0 : loadBalancerPort + .hashCode()); + result = prime * result + + ((policyNames == null) ? 0 : policyNames.hashCode()); + result = prime * result + + ((protocol == null) ? 0 : protocol.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LoadBalancerListener other = (LoadBalancerListener) obj; + if (instancePort == null) + { + if (other.instancePort != null) + return false; + } + else if (!instancePort.equals(other.instancePort)) + return false; + if (loadBalancerPort == null) + { + if (other.loadBalancerPort != null) + return false; + } + else if (!loadBalancerPort.equals(other.loadBalancerPort)) + return false; + if (policyNames == null) + { + if (other.policyNames != null) + return false; + } + else if (!policyNames.equals(other.policyNames)) + return false; + if (protocol == null) + { + if (other.protocol != null) + return false; + } + else if (!protocol.equals(other.protocol)) + return false; + return true; + } + + + + } +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/functions/ELBRegionToEndpoint.java b/aws/core/src/main/java/org/jclouds/aws/ec2/functions/ELBRegionToEndpoint.java new file mode 100644 index 0000000000..e556494040 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/functions/ELBRegionToEndpoint.java @@ -0,0 +1,50 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 org.jclouds.aws.ec2.functions; + +import java.net.URI; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.aws.ec2.ELB; + +import com.google.common.base.Function; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class ELBRegionToEndpoint implements Function { + private final Map regionToEndpoint; + private final URI defaultUri; + + @Inject + public ELBRegionToEndpoint(@ELB Map regionToEndpoint, @ELB URI defaultUri) { + this.regionToEndpoint = regionToEndpoint; + this.defaultUri = defaultUri; + } + + public URI apply(Object from) { + return from == null ? defaultUri : regionToEndpoint.get(from); + } + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/services/ElasticLoadBalancerAsyncClient.java b/aws/core/src/main/java/org/jclouds/aws/ec2/services/ElasticLoadBalancerAsyncClient.java new file mode 100644 index 0000000000..2b77ff6108 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/services/ElasticLoadBalancerAsyncClient.java @@ -0,0 +1,117 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 org.jclouds.aws.ec2.services; + +import static org.jclouds.aws.ec2.reference.EC2Parameters.ACTION; +import static org.jclouds.aws.ec2.reference.EC2Parameters.VERSION; + +import java.util.Set; + +import javax.annotation.Nullable; +import javax.ws.rs.FormParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import org.jclouds.aws.ec2.binders.BindELBInstanceIdsToIndexedFormParams; +import org.jclouds.aws.ec2.domain.ElasticLoadBalancer; +import org.jclouds.aws.ec2.functions.ELBRegionToEndpoint; +import org.jclouds.aws.ec2.xml.CreateLoadBalancerResponseHandler; +import org.jclouds.aws.ec2.xml.DescribeLoadBalancersResponseHandler; +import org.jclouds.aws.ec2.xml.RegisterInstancesWithLoadBalancerResponseHandler; +import org.jclouds.aws.filters.FormSigner; +import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.EndpointParam; +import org.jclouds.rest.annotations.FormParams; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.VirtualHost; +import org.jclouds.rest.annotations.XMLResponseParser; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provides access to EC2 Elastic Load Balancer via REST API. + *

+ * + * @author Lili Nader + */ +@RequestFilters(FormSigner.class) +@FormParams(keys = VERSION, values = "2009-11-25") +@VirtualHost +public interface ElasticLoadBalancerAsyncClient +{ + /** + * @see ElasticLoadBalancerClient#createLoadBalancer + */ + @POST + @Path("/") + @XMLResponseParser(CreateLoadBalancerResponseHandler.class) + @FormParams(keys = ACTION, values = "CreateLoadBalancer") + ListenableFuture createLoadBalancer( + @EndpointParam(parser = ELBRegionToEndpoint.class) @Nullable String region, + @FormParam("LoadBalancerName") String name, + @FormParam("Listeners.member.1.Protocol") String protocol, + @FormParam("Listeners.member.1.LoadBalancerPort") Integer loadBalancerPort, + @FormParam("Listeners.member.1.InstancePort") Integer instancePort, + @FormParam("AvailabilityZones.member.1") String availabilityZone); + + /** + * @see ElasticLoadBalancerClient#deleteLoadBalancer + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "DeleteLoadBalancer") + ListenableFuture deleteLoadBalancer( + @EndpointParam(parser = ELBRegionToEndpoint.class) @Nullable String region, + @FormParam("LoadBalancerName") String name); + + /** + * @see ElasticLoadBalancerClient#registerInstancesWithLoadBalancer + */ + @POST + @Path("/") + @XMLResponseParser(RegisterInstancesWithLoadBalancerResponseHandler.class) + @FormParams(keys = ACTION, values = "RegisterInstancesWithLoadBalancer") + ListenableFuture> registerInstancesWithLoadBalancer( + @EndpointParam(parser = ELBRegionToEndpoint.class) @Nullable String region, + @FormParam("LoadBalancerName") String name, + @BinderParam(BindELBInstanceIdsToIndexedFormParams.class) String... instanceIds); + + /** + * @see ElasticLoadBalancerClient#deregisterInstancesWithLoadBalancer + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "DeregisterInstancesWithLoadBalancer") + ListenableFuture deregisterInstancesWithLoadBalancer( + @EndpointParam(parser = ELBRegionToEndpoint.class) @Nullable String region, + @FormParam("LoadBalancerName") String name, + @BinderParam(BindELBInstanceIdsToIndexedFormParams.class) String... instanceIds); + + /** + * @see ElasticLoadBalancerClient#describeLoadBalancers + */ + @POST + @Path("/") + @XMLResponseParser(DescribeLoadBalancersResponseHandler.class) + @FormParams(keys = ACTION, values = "DescribeLoadBalancers") + ListenableFuture> describeLoadBalancers( + @EndpointParam(parser = ELBRegionToEndpoint.class) @Nullable String region, + @FormParam("LoadBalancerName") @Nullable String name); + +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/services/ElasticLoadBalancerClient.java b/aws/core/src/main/java/org/jclouds/aws/ec2/services/ElasticLoadBalancerClient.java new file mode 100644 index 0000000000..0ee2921ac7 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/services/ElasticLoadBalancerClient.java @@ -0,0 +1,81 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 org.jclouds.aws.ec2.services; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nullable; + +import org.jclouds.aws.ec2.domain.ElasticLoadBalancer; +import org.jclouds.concurrent.Timeout; + +/** + * Provides access to EC2 Elastic Load Balancer via their REST API. + *

+ * + * @author Lili Nader + */ +@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) +public interface ElasticLoadBalancerClient { + + /** + * Creates a load balancer + * + * @param name Name of the load balancer + * @param loadBalancerPort Port for the load balancer to listen on + * @param instancePort Port to forward the request to + * @return dns the DNS name for the load balancer + * @see registerInstancesWithLoadBalancer(@Nullable String region, String name, String... instanceIds); + + /** + * Deregister instances with an existing load balancer + * @param name Load Balancer name + * @param instanceIds Set of instance Ids to deregister with load balancer + * @return + * + * @see describeLoadBalancers(@Nullable String region, @Nullable String name); + +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/xml/CreateLoadBalancerResponseHandler.java b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/CreateLoadBalancerResponseHandler.java new file mode 100644 index 0000000000..caca72762d --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/CreateLoadBalancerResponseHandler.java @@ -0,0 +1,36 @@ +package org.jclouds.aws.ec2.xml; + +import javax.annotation.Resource; + +import org.jclouds.http.functions.ParseSax.HandlerWithResult; +import org.jclouds.logging.Logger; + +public class CreateLoadBalancerResponseHandler extends HandlerWithResult { + + @Resource + protected Logger logger = Logger.NULL; + + private String dnsName; + private StringBuilder currentText = new StringBuilder(); + + protected String currentOrNull() { + String returnVal = currentText.toString().trim(); + return returnVal.equals("") ? null : returnVal; + } + + public void endElement(String uri, String name, String qName) { + if (qName.equals("DNSName")) { + dnsName = currentOrNull(); + } + currentText = new StringBuilder(); + } + + public void characters(char ch[], int start, int length) { + currentText.append(ch, start, length); + } + + @Override + public String getResult() { + return dnsName; + } +} \ No newline at end of file diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeLoadBalancersResponseHandler.java b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeLoadBalancersResponseHandler.java new file mode 100644 index 0000000000..c548158976 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/DescribeLoadBalancersResponseHandler.java @@ -0,0 +1,241 @@ +package org.jclouds.aws.ec2.xml; + +import java.util.Set; + +import javax.annotation.Resource; +import javax.inject.Inject; + +import org.jclouds.aws.ec2.EC2; +import org.jclouds.aws.ec2.domain.ElasticLoadBalancer; +import org.jclouds.aws.ec2.domain.ElasticLoadBalancer.AppCookieStickinessPolicy; +import org.jclouds.aws.ec2.domain.ElasticLoadBalancer.LBCookieStickinessPolicy; +import org.jclouds.aws.ec2.domain.ElasticLoadBalancer.LoadBalancerListener; +import org.jclouds.aws.ec2.util.EC2Utils; +import org.jclouds.date.DateService; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.logging.Logger; +import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.xml.sax.Attributes; + +import com.google.common.collect.Sets; + +public class DescribeLoadBalancersResponseHandler extends + ParseSax.HandlerWithResult> +{ + @Inject + public DescribeLoadBalancersResponseHandler(@EC2 String defaultRegion) + { + this.defaultRegion = defaultRegion; + this.listenerHandler = new LoadBalancerListenerHandler(); + } + + @Inject + protected DateService dateService; + + @Resource + protected Logger logger = Logger.NULL; + + private Set contents = Sets + .newLinkedHashSet(); + private StringBuilder currentText = new StringBuilder(); + private final String defaultRegion; + private final LoadBalancerListenerHandler listenerHandler; + + private boolean inListenerDescriptions = false; + private boolean inInstances = false; + private boolean inAppCookieStickinessPolicies = false; + private boolean inLBCookieStickinessPolicies = false; + private boolean inAvailabilityZones = false; + private boolean inLoadBalancerDescriptions = false; + + private ElasticLoadBalancer elb; + private AppCookieStickinessPolicy appCookieStickinessPolicy; + private LBCookieStickinessPolicy lBCookieStickinessPolicy; + + public void startElement(String uri, String localName, String qName, + Attributes attributes) + { + + if (qName.equals("ListenerDescriptions") || inListenerDescriptions) + { + inListenerDescriptions = true; + } + else if (qName.equals("AppCookieStickinessPolicies")) + { + inAppCookieStickinessPolicies = true; + } + else if (qName.equals("LBCookieStickinessPolicies")) + { + inLBCookieStickinessPolicies = true; + } + else if (qName.equals("LoadBalancerDescriptions")) + { + inLoadBalancerDescriptions = true; + } + else if (qName.equals("Instances")) + { + inInstances = true; + } + else if (qName.equals("AvailabilityZones")) + { + inAvailabilityZones = true; + } + + if (qName.equals("member")) + { + if (!(inListenerDescriptions || inAppCookieStickinessPolicies || inInstances + || inLBCookieStickinessPolicies || inAvailabilityZones)) + { + elb = new ElasticLoadBalancer(); + } + } + } + + public void endElement(String uri, String localName, String qName) + { + logger.info(qName); + + //if end tag is one of below then set inXYZ to false + if (qName.equals("ListenerDescriptions")) + { + inListenerDescriptions = false; + } + else if (qName.equals("AppCookieStickinessPolicies")) + { + inAppCookieStickinessPolicies = false; + } + else if (qName.equals("LBCookieStickinessPolicies")) + { + inLBCookieStickinessPolicies = false; + } + else if (qName.equals("LoadBalancerDescriptions")) + { + inLoadBalancerDescriptions = false; + } + else if (qName.equals("Instances")) + { + inInstances = false; + } + else if (qName.equals("AvailabilityZones")) + { + inAvailabilityZones = false; + } + + + if (qName.equals("DNSName")) + { + elb.setDnsName(currentText.toString().trim()); + } + else if (qName.equals("LoadBalancerName")) + { + elb.setName(currentText.toString().trim()); + } + else if (qName.equals("InstanceId")) + { + elb.getInstanceIds().add(currentText.toString().trim()); + } + + else if (qName.equals("member")) + { + + if (inAvailabilityZones) + { + elb.getAvailabilityZones().add(currentText.toString().trim()); + } + else if (!(inListenerDescriptions || inAppCookieStickinessPolicies || inInstances + || inLBCookieStickinessPolicies || inAvailabilityZones)) + { + try + { + String region = EC2Utils.findRegionInArgsOrNull(request); + if (region == null) + region = defaultRegion; + + elb.setRegion(region); + contents.add(elb); + } + catch (NullPointerException e) + { + logger.warn(e, "malformed load balancer: %s", localName); + } + + this.elb = null; + + } + + } + + currentText = new StringBuilder(); + } + + @Override + public Set getResult() + { + return contents; + } + + public void characters(char ch[], int start, int length) + { + currentText.append(ch, start, length); + } + + @Override + public void setContext(GeneratedHttpRequest request) { + listenerHandler.setContext(request); + super.setContext(request); + } + + public class LoadBalancerListenerHandler extends + ParseSax.HandlerWithResult> + { + private Set listeners = Sets.newHashSet(); + private StringBuilder currentText = new StringBuilder(); + private LoadBalancerListener listener; + + public void startElement(String uri, String name, String qName, + Attributes attrs) + { + if (qName.equals("member")) + { + listener = new LoadBalancerListener(); + } + } + + public void endElement(String uri, String name, String qName) + { + if (qName.equals("Protocol")) + { + listener.setProtocol(currentText.toString().trim()); + } + else if (qName.equals("LoadBalancerPort")) + { + listener.setLoadBalancerPort(Integer.parseInt(currentText + .toString().trim())); + } + else if (qName.equals("InstancePort")) + { + listener.setInstancePort(Integer.parseInt(currentText + .toString().trim())); + } + else if (qName.equals("member")) + { + listeners.add(listener); + } + + currentText = new StringBuilder(); + + } + + @Override + public Set getResult() + { + return listeners; + } + + public void characters(char ch[], int start, int length) + { + currentText.append(ch, start, length); + } + + } +} diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/xml/RegisterInstancesWithLoadBalancerResponseHandler.java b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/RegisterInstancesWithLoadBalancerResponseHandler.java new file mode 100644 index 0000000000..5daeb47a29 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/xml/RegisterInstancesWithLoadBalancerResponseHandler.java @@ -0,0 +1,44 @@ +package org.jclouds.aws.ec2.xml; + +import java.util.Set; + +import javax.annotation.Resource; +import javax.inject.Inject; + +import org.jclouds.http.functions.ParseSax; +import org.jclouds.logging.Logger; + +import com.google.common.collect.Sets; + +public class RegisterInstancesWithLoadBalancerResponseHandler extends + ParseSax.HandlerWithResult> +{ + @Inject + public RegisterInstancesWithLoadBalancerResponseHandler() + { + } + + @Resource + protected Logger logger = Logger.NULL; + + private Set instanceIds = Sets.newLinkedHashSet(); + private StringBuilder currentText = new StringBuilder(); + + + + public void endElement(String uri, String localName, String qName) + { + if(qName.equals("InstanceId")) + instanceIds.add(currentText.toString().trim()); + } + @Override + public Set getResult() + { + return instanceIds; + } + + public void characters(char ch[], int start, int length) + { + currentText.append(ch, start, length); + } +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/services/ElasticLoadBalancerClientLiveTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/services/ElasticLoadBalancerClientLiveTest.java new file mode 100644 index 0000000000..9fa354caae --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/services/ElasticLoadBalancerClientLiveTest.java @@ -0,0 +1,109 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 org.jclouds.aws.ec2.services; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.testng.Assert.assertNotNull; + +import java.util.Set; + +import org.jclouds.aws.domain.Region; +import org.jclouds.aws.ec2.EC2AsyncClient; +import org.jclouds.aws.ec2.EC2Client; +import org.jclouds.aws.ec2.EC2ContextFactory; +import org.jclouds.aws.ec2.domain.AvailabilityZone; +import org.jclouds.aws.ec2.domain.ElasticLoadBalancer; +import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.rest.RestContext; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; + +/** + * Tests behavior of {@code ElasticIPAddressClient} + * + * @author Lili Nader + */ +@Test(groups = "live", sequential = true, testName = "ec2.ElasticLoadBalancerClientLiveTest") +public class ElasticLoadBalancerClientLiveTest +{ + + private ElasticLoadBalancerClient client; + private RestContext context; + + @BeforeGroups(groups = { "live" }) + public void setupClient() + { + String user = checkNotNull(System.getProperty("jclouds.test.user"), + "jclouds.test.user"); + String password = checkNotNull(System.getProperty("jclouds.test.key"), + "jclouds.test.key"); + + context = EC2ContextFactory.createContext(user, password, + new Log4JLoggingModule()).getProviderSpecificContext(); + client = context.getApi().getElasticLoadBalancerServices(); + } + + @Test + void testCreateLoadBalancer() + { + String name = "TestLoadBalancer"; + for (String region : ImmutableSet.of(Region.US_EAST_1)) + { + String dnsName = client.createLoadBalancer(region, name, "http", + 80, 80, AvailabilityZone.US_EAST_1A); + assertNotNull(dnsName); + assert (dnsName.startsWith(name)); + } + } + + @Test + void testDescribeLoadBalancers() + { + String name = "TestDescribeLoadBalancer"; + for (String region : ImmutableSet.of(Region.US_EAST_1)) + { + client.createLoadBalancer(region, name, "http", + 80, 80, AvailabilityZone.US_EAST_1A); + Set allResults = client.describeLoadBalancers( + region, name); + assertNotNull(allResults); + assert (allResults.size() >= 1); + + client.deleteLoadBalancer(region, name); + } + } + + @Test + void testDeleteLoadBalancer() + { + for (String region : ImmutableSet.of(Region.US_EAST_1)) + { + client.deleteLoadBalancer(region, "TestLoadBalancer"); + } + } + + @AfterTest + public void shutdown() + { + context.close(); + } +} diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeLoadBalancerResponseHandlerTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeLoadBalancerResponseHandlerTest.java new file mode 100644 index 0000000000..cd760be32b --- /dev/null +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/xml/DescribeLoadBalancerResponseHandlerTest.java @@ -0,0 +1,81 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 org.jclouds.aws.ec2.xml; + +import static org.easymock.EasyMock.expect; +import static org.easymock.classextension.EasyMock.createMock; +import static org.easymock.classextension.EasyMock.replay; +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; +import java.util.Date; +import java.util.Set; + +import org.jclouds.aws.domain.Region; +import org.jclouds.aws.ec2.domain.AvailabilityZone; +import org.jclouds.aws.ec2.domain.ElasticLoadBalancer; +import org.jclouds.aws.ec2.domain.Image; +import org.jclouds.aws.ec2.domain.ElasticLoadBalancer.AppCookieStickinessPolicy; +import org.jclouds.aws.ec2.domain.ElasticLoadBalancer.LBCookieStickinessPolicy; +import org.jclouds.aws.ec2.domain.ElasticLoadBalancer.LoadBalancerListener; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; + +/** + * Tests behavior of {@code DescribeLoadBalancersResponseHandler} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ec2.DescribeLoadBalancersResponseHandlerTest") +public class DescribeLoadBalancerResponseHandlerTest extends BaseEC2HandlerTest { + + public void testParse() { + InputStream is = getClass().getResourceAsStream("/ec2/describe_loadbalancers.xml"); + + Set contents = Sets.newHashSet(); + ElasticLoadBalancer dummy = new ElasticLoadBalancer(defaultRegion, "my-load-balancer", + ImmutableSet.of("i-5b33e630", "i-8f26d7e4", "i-5933e632"), ImmutableSet.of("us-east-1a"), + "my-load-balancer-1400212309.us-east-1.elb.amazonaws.com"); + contents.add(dummy); + + Set result = parseLoadBalancers(is); + + assertEquals(result, contents); + } + + + private Set parseLoadBalancers(InputStream is) { + DescribeLoadBalancersResponseHandler handler = injector + .getInstance(DescribeLoadBalancersResponseHandler.class); + addDefaultRegionToHandler(handler); + Set result = factory.create(handler).parse(is); + return result; + } + + private void addDefaultRegionToHandler(ParseSax.HandlerWithResult handler) { + GeneratedHttpRequest request = createMock(GeneratedHttpRequest.class); + expect(request.getArgs()).andReturn(new Object[] { null }); + replay(request); + handler.setContext(request); + } +} diff --git a/aws/core/src/test/resources/ec2/describe_loadbalancers.xml b/aws/core/src/test/resources/ec2/describe_loadbalancers.xml new file mode 100644 index 0000000000..787ee8dda1 --- /dev/null +++ b/aws/core/src/test/resources/ec2/describe_loadbalancers.xml @@ -0,0 +1,63 @@ + + + + + my-load-balancer + 2010-03-03T20:54:45.110Z + my-load-balancer-1400212309.us-east-1.elb.amazonaws.com + + 300 + HTTP:80/index.html + 3 + 30 + 5 + + + + + + HTTP + 80 + 80 + + + + + + TCP + 443 + 443 + + + + + + i-5b33e630 + + + i-8f26d7e4 + + + i-5933e632 + + + + + + MyCookie + MyLoadBalancerPolicy + + + + + + us-east-1a + + + + + + 10e2b9c9-3899-11df-a1e2-b5349194c035 + + diff --git a/compute/src/main/java/org/jclouds/compute/strategy/LoadBalancerStrategy.java b/compute/src/main/java/org/jclouds/compute/strategy/LoadBalancerStrategy.java new file mode 100644 index 0000000000..bb98c73894 --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/strategy/LoadBalancerStrategy.java @@ -0,0 +1,38 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 org.jclouds.compute.strategy; + +import java.util.Set; + +import org.jclouds.domain.Location; + +/** + * Creates a load balancer for nodes listed + * + * @author Lili Nader + */ +public interface LoadBalancerStrategy +{ + + String execute(Location loaction, String name, String protocol, + Integer loadBalancerPort, Integer instancePort, + Set instanceIds); + +} \ No newline at end of file