package core.aws.client; import com.amazonaws.AmazonServiceException; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.regions.Regions; import com.amazonaws.services.ec2.AmazonEC2; import com.amazonaws.services.ec2.AmazonEC2ClientBuilder; import com.amazonaws.services.ec2.model.Address; import com.amazonaws.services.ec2.model.AllocateAddressRequest; import com.amazonaws.services.ec2.model.AllocateAddressResult; import com.amazonaws.services.ec2.model.AssociateAddressRequest; import com.amazonaws.services.ec2.model.AttachInternetGatewayRequest; import com.amazonaws.services.ec2.model.CreateNatGatewayRequest; import com.amazonaws.services.ec2.model.CreateSubnetRequest; import com.amazonaws.services.ec2.model.CreateVpcRequest; import com.amazonaws.services.ec2.model.DeleteNatGatewayRequest; import com.amazonaws.services.ec2.model.DescribeAddressesRequest; import com.amazonaws.services.ec2.model.DescribeAddressesResult; import com.amazonaws.services.ec2.model.DescribeNatGatewaysRequest; import com.amazonaws.services.ec2.model.DescribeNatGatewaysResult; import com.amazonaws.services.ec2.model.DescribeRouteTablesRequest; import com.amazonaws.services.ec2.model.DescribeSubnetsRequest; import com.amazonaws.services.ec2.model.DescribeVpcsRequest; import com.amazonaws.services.ec2.model.DescribeVpcsResult; import com.amazonaws.services.ec2.model.DisassociateAddressRequest; import com.amazonaws.services.ec2.model.Filter; import com.amazonaws.services.ec2.model.InternetGateway; import com.amazonaws.services.ec2.model.ModifyVpcAttributeRequest; import com.amazonaws.services.ec2.model.NatGateway; import com.amazonaws.services.ec2.model.ReleaseAddressRequest; import com.amazonaws.services.ec2.model.RouteTable; import com.amazonaws.services.ec2.model.Vpc; import core.aws.util.Runner; import core.aws.util.Threads; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.time.Duration; import java.util.Collection; import java.util.List; /** * @author neo */ public class EC2VPC { public final AmazonEC2 ec2; private final Logger logger = LoggerFactory.getLogger(getClass()); public EC2VPC(AWSCredentialsProvider credentials, Regions region) { ec2 = AmazonEC2ClientBuilder.standard().withRegion(region).withCredentials(credentials).build(); } public Vpc createVPC() throws InterruptedException { logger.info("create VPC"); String vpcId = ec2.createVpc(new CreateVpcRequest().withCidrBlock("10.0.0.0/16")).getVpc().getVpcId(); while (true) { Threads.sleepRoughly(Duration.ofSeconds(20)); DescribeVpcsResult result = ec2.describeVpcs(new DescribeVpcsRequest().withVpcIds(vpcId)); Vpc remoteVPC = result.getVpcs().get(0); if ("available".equals(remoteVPC.getState())) { enableVPCDNS(vpcId); return remoteVPC; } } } public com.amazonaws.services.ec2.model.Subnet createSubnet(CreateSubnetRequest request) { logger.info("create subnet, request={}", request); return ec2.createSubnet(request).getSubnet(); } public List<com.amazonaws.services.ec2.model.Subnet> describeSubnets(List<String> subnetIds) { return ec2.describeSubnets(new DescribeSubnetsRequest().withSubnetIds(subnetIds)).getSubnets(); } public Vpc describeVPC(String vpcId) { logger.info("describe vpc, vpcId={}", vpcId); DescribeVpcsResult result = ec2.describeVpcs(new DescribeVpcsRequest().withVpcIds(vpcId)); return result.getVpcs().get(0); } public InternetGateway createInternetGateway(String vpcId) { logger.info("create internet gateway, vpcId={}", vpcId); InternetGateway internetGateway = ec2.createInternetGateway().getInternetGateway(); ec2.attachInternetGateway(new AttachInternetGatewayRequest().withVpcId(vpcId).withInternetGatewayId(internetGateway.getInternetGatewayId())); return internetGateway; } private void enableVPCDNS(String vpcId) { logger.info("enable VPC DNS support, vpcId={}", vpcId); ec2.modifyVpcAttribute(new ModifyVpcAttributeRequest().withVpcId(vpcId) .withEnableDnsHostnames(true)); } public String assignEIP(final String instanceId) throws Exception { final AllocateAddressResult address = ec2.allocateAddress(new AllocateAddressRequest().withDomain("vpc")); logger.info("associate eip to instance, instanceId={}, ip={}", instanceId, address.getPublicIp()); new Runner<>() .retryInterval(Duration.ofSeconds(10)) .maxAttempts(3) .retryOn(e -> e instanceof AmazonServiceException) .run(() -> { ec2.associateAddress(new AssociateAddressRequest().withInstanceId(instanceId).withAllocationId(address.getAllocationId())); return null; }); return address.getPublicIp(); } public void releaseEIP(List<String> instanceIds) { logger.info("release EIP for instances, instanceIds={}", instanceIds); DescribeAddressesResult result = ec2.describeAddresses(new DescribeAddressesRequest().withFilters(new Filter("instance-id").withValues(instanceIds))); for (Address address : result.getAddresses()) { logger.info("release EIP, ip={}, instanceId={}", address.getPublicIp(), address.getInstanceId()); ec2.disassociateAddress(new DisassociateAddressRequest().withAssociationId(address.getAssociationId())); ec2.releaseAddress(new ReleaseAddressRequest().withAllocationId(address.getAllocationId())); } } public List<RouteTable> describeRouteTables(Collection<String> routeTableIds) { logger.info("describe route tables, routeTableIds={}", routeTableIds); return ec2.describeRouteTables(new DescribeRouteTablesRequest().withRouteTableIds(routeTableIds)).getRouteTables(); } public NatGateway createNATGateway(String subnetId, String ip) { logger.info("create nat gateway, subnetId={}, ip={}", subnetId, ip); List<Address> addresses = AWS.vpc.ec2.describeAddresses(new DescribeAddressesRequest().withPublicIps(ip)).getAddresses(); if (addresses.isEmpty()) throw new Error("cannot find eip, ip=" + ip); Address address = addresses.get(0); if (address.getAssociationId() != null) throw new Error("eip must not associated with other resource, ip=" + ip); CreateNatGatewayRequest request = new CreateNatGatewayRequest() .withSubnetId(subnetId) .withAllocationId(address.getAllocationId()); String gatewayId = ec2.createNatGateway(request).getNatGateway().getNatGatewayId(); NatGateway gateway; while (true) { Threads.sleepRoughly(Duration.ofSeconds(30)); gateway = describeNATGateway(gatewayId); String state = gateway.getState(); if ("pending".equals(state)) continue; if ("available".equals(state)) { break; } else { throw new Error("failed to create nat gateway, gatewayId=" + gatewayId + ", state=" + state); } } return gateway; } public void deleteNATGateway(String gatewayId) { logger.info("delete nat gateway, natGatewayId={}", gatewayId); AWS.vpc.ec2.deleteNatGateway(new DeleteNatGatewayRequest().withNatGatewayId(gatewayId)); while (true) { Threads.sleepRoughly(Duration.ofSeconds(30)); NatGateway gateway = AWS.vpc.describeNATGateway(gatewayId); String state = gateway.getState(); if ("deleting".equals(state)) continue; if ("deleted".equals(state)) { break; } else { throw new Error("failed to delete nat gateway, gatewayId=" + gatewayId + ", state=" + state); } } } private NatGateway describeNATGateway(String gatewayId) { DescribeNatGatewaysResult result = ec2.describeNatGateways(new DescribeNatGatewaysRequest().withNatGatewayIds(gatewayId)); return result.getNatGateways().get(0); } }