blob: 316ae54930aba3dabe43be3c247c59ecb81fe23f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.ignite.spi.discovery.tcp.ipfinder.elb;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.AmazonEC2ClientBuilder;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.elasticloadbalancingv2.AmazonElasticLoadBalancing;
import com.amazonaws.services.elasticloadbalancingv2.AmazonElasticLoadBalancingClientBuilder;
import com.amazonaws.services.elasticloadbalancingv2.model.DescribeTargetHealthRequest;
import com.amazonaws.services.elasticloadbalancingv2.model.TargetDescription;
import com.amazonaws.services.elasticloadbalancingv2.model.TargetHealthDescription;
import org.apache.http.conn.util.InetAddressUtils;
import org.apache.ignite.spi.IgniteSpiConfiguration;
import org.apache.ignite.spi.IgniteSpiException;
import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinderAdapter;
import static com.amazonaws.util.StringUtils.isNullOrEmpty;
/**
* AWS Application load balancer(ALB) based IP finder.
*
* <p>
* For information about Amazon Application load balancer visit:
* <a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html">aws.amazon.com</a>.
* </p>
*
* <h1 class="header">Configuration</h1>
* <h2 class="header">Mandatory</h2>
* <ul>
* <li>{@link #setCredentialsProvider(AWSCredentialsProvider)}</li>
* <li>Application load balancer target group ARN name (see {@link #setTargetGrpARN(String)})</li>
* <li>Application load balancer region (see {@link #setRegion(String)})</li>
* </ul>
*
* <p> The finder will fetch all nodes connected under an Application load balancer and share with its peers for cluster
* awareness.</p>
*
* <p> Note that using AWS Application load balancer service will result in charges to your AWS account.</p>
*
* <p>
* Choose another implementation of {@link org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder} for local
* or home network tests.</p>
*
* <p> Note that this finder is shared.</p>
*
* <p> Note that this finder can only be used on AWS EC2 instances that belong on a Load Balancer based auto scaling group.</p>
*
* @see TcpDiscoveryElbIpFinder
*/
public class TcpDiscoveryAlbIpFinder extends TcpDiscoveryIpFinderAdapter {
/** */
private AmazonElasticLoadBalancing amazonELBClient;
/** */
private String targetGrpARN;
/** */
private AmazonEC2 amazonEC2Client;
/** */
private AWSCredentialsProvider credsProvider;
/** */
private String region;
/**
* Creates Application load balancer IP finder instance.
*/
public TcpDiscoveryAlbIpFinder() {
setShared(true);
}
/** {@inheritDoc} */
@Override public Collection<InetSocketAddress> getRegisteredAddresses() throws IgniteSpiException {
initClients();
DescribeTargetHealthRequest req = new DescribeTargetHealthRequest().withTargetGroupArn(targetGrpARN);
List<TargetHealthDescription> desc = amazonELBClient.describeTargetHealth(req).getTargetHealthDescriptions();
// instance ips
List<String> instanceIps = new LinkedList<>();
// instance ids
List<String> instanceIds = new LinkedList<>();
// Fetch the ids of instances in the given ARN via target health
for (TargetHealthDescription targetHealthDesc : desc) {
TargetDescription target = targetHealthDesc.getTarget();
String targetId = target.getId();
// divide the target ids into ips and instance ids
if (isIPAddress(targetId))
instanceIps.add(targetId);
else
instanceIds.add(targetId);
}
DescribeInstancesRequest descInstReq = new DescribeInstancesRequest().withInstanceIds(instanceIds);
List<Reservation> reservations = amazonEC2Client.describeInstances(descInstReq).getReservations();
// Convert instance ids to instance ips
for (Reservation reservation : reservations) {
List<Instance> instances = reservation.getInstances();
for (Instance instance : instances) {
String ip = instance.getPrivateIpAddress();
instanceIps.add(ip);
}
}
List<InetSocketAddress> addrs = new LinkedList<>();
for (String ip : instanceIps) {
InetSocketAddress addr = new InetSocketAddress(ip, 0);
addrs.add(addr);
}
return addrs;
}
/**
* Checks if the given id is a valid IP address
*
* @param id ip to be checked.
*/
private boolean isIPAddress(String id) {
return InetAddressUtils.isIPv4Address(id) ||
InetAddressUtils.isIPv4MappedIPv64Address(id) ||
InetAddressUtils.isIPv6Address(id) ||
InetAddressUtils.isIPv6HexCompressedAddress(id) ||
InetAddressUtils.isIPv6StdAddress(id);
}
/**
* Initializing the IP finder.
*/
private void initClients() {
if (credsProvider == null || isNullOrEmpty(targetGrpARN) || isNullOrEmpty(region))
throw new IgniteSpiException("One or more configuration parameters are invalid [setCredentialsProvider=" +
credsProvider + ", setRegion=" + region + ", setTargetGroupARN=" +
targetGrpARN + "]");
if (amazonEC2Client == null)
amazonEC2Client = AmazonEC2ClientBuilder.standard().withRegion(region).withCredentials(credsProvider)
.build();
if (amazonELBClient == null)
amazonELBClient = AmazonElasticLoadBalancingClientBuilder.standard().withRegion(region)
.withCredentials(credsProvider).build();
}
/**
* Sets AWS Application Load Balancer's target group ARN. For details refer to Amazon API reference.
*
* @param targetGrpARN Target group ARN attached to an AWS Application Load Balancer.
*/
@IgniteSpiConfiguration(optional = false)
public void setTargetGrpARN(String targetGrpARN) {
this.targetGrpARN = targetGrpARN;
}
/**
* Sets AWS Application Load Balancer's region.
*
* For details refer to Amazon API reference.
*
* @param region AWS Elastic Load Balancer region (e.g: us-east-1)
*/
@IgniteSpiConfiguration(optional = false)
public void setRegion(String region) {
this.region = region;
}
/**
* Sets AWS credentials provider.
*
* For details refer to Amazon API reference.
*
* @param credsProvider AWS credentials provider.
*/
@IgniteSpiConfiguration(optional = false)
public void setCredentialsProvider(AWSCredentialsProvider credsProvider) {
this.credsProvider = credsProvider;
}
/** {@inheritDoc} */
@Override public void registerAddresses(Collection<InetSocketAddress> addrs) throws IgniteSpiException {
//No-op, Application load balancer will take care of registration.
}
/** {@inheritDoc} */
@Override public void unregisterAddresses(Collection<InetSocketAddress> addrs) throws IgniteSpiException {
// No-op, Application load balancer will take care of this process.
}
}