/* * Copyright 2015 herd contributors * * 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.finra.herd.dao.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.finra.herd.dao.Ec2Operations; import com.amazonaws.AmazonServiceException; import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.AvailabilityZone; import com.amazonaws.services.ec2.model.DescribeAvailabilityZonesRequest; import com.amazonaws.services.ec2.model.DescribeAvailabilityZonesResult; import com.amazonaws.services.ec2.model.DescribeInstanceAttributeRequest; import com.amazonaws.services.ec2.model.DescribeInstanceAttributeResult; import com.amazonaws.services.ec2.model.DescribeSpotPriceHistoryRequest; import com.amazonaws.services.ec2.model.DescribeSpotPriceHistoryResult; import com.amazonaws.services.ec2.model.DescribeSubnetsRequest; import com.amazonaws.services.ec2.model.DescribeSubnetsResult; import com.amazonaws.services.ec2.model.GroupIdentifier; import com.amazonaws.services.ec2.model.InstanceAttribute; import com.amazonaws.services.ec2.model.ModifyInstanceAttributeRequest; import com.amazonaws.services.ec2.model.SpotPrice; import com.amazonaws.services.ec2.model.Subnet; /** * Mock implementation of AWS EC2 operations. * * The mock contains several pre-configured subnets, availability zones, regions, and instance types. */ public class MockEc2OperationsImpl implements Ec2Operations { public static final int AVAILABLE_IP_HIGH = 20; public static final int AVAILABLE_IP_LOW = 10; public static final String SPOT_PRICE_VERY_HIGH = "2"; public static final String SPOT_PRICE_HIGH = "1.5"; public static final String SPOT_PRICE_EQUAL = "1"; public static final String SPOT_PRICE_LOW = ".5"; public static final String SPOT_PRICE_VERY_LOW = ".25"; public static String SUBNET_1 = "SUBNET_1"; public static String SUBNET_2 = "SUBNET_2"; public static String SUBNET_3 = "SUBNET_3"; public static String SUBNET_4 = "SUBNET_4"; public static String SUBNET_5 = "SUBNET_5"; public static String AVAILABILITY_ZONE_1 = "AVAILABILITY_ZONE_1"; public static String AVAILABILITY_ZONE_2 = "AVAILABILITY_ZONE_2"; public static String AVAILABILITY_ZONE_3 = "AVAILABILITY_ZONE_3"; public static String AVAILABILITY_ZONE_4 = "AVAILABILITY_ZONE_4"; public static String REGION_1 = "REGION_1"; public static String REGION_2 = "REGION_2"; public static String INSTANCE_TYPE_1 = "INSTANCE_TYPE_1"; public static String INSTANCE_TYPE_2 = "INSTANCE_TYPE_2"; public static String INSTANCE_TYPE_3 = "INSTANCE_TYPE_3"; /* * Pre-configured in-memory EC2 instance information. * These are initialized in the constructor. * Note that on-demand prices are database configurations, therefore are stored in the mock reference data DB script. */ private Map<String, MockSubnet> mockSubnets = new HashMap<>(); private Map<String, MockAvailabilityZone> mockAvailabilityZones = new HashMap<>(); private Set<String> mockInstanceTypes = new HashSet<>(); public MockEc2OperationsImpl() { Map<String, MockEc2Region> mockEc2Regions = new HashMap<>(); // Pre-configure REGION_1 { MockEc2Region mockEc2Region = new MockEc2Region(); mockEc2Region.setName(REGION_1); mockEc2Regions.put(REGION_1, mockEc2Region); // Pre-configure AVAILABILITY_ZONE_1 { MockAvailabilityZone availabilityZone = new MockAvailabilityZone(); availabilityZone.setZoneName(AVAILABILITY_ZONE_1); mockEc2Region.putAvailabilityZone(availabilityZone); // Pre-configured instance types for AVAILABILITY_ZONE_1 { MockSpotPrice spotPrice = new MockSpotPrice(); spotPrice.setType(INSTANCE_TYPE_1); spotPrice.setSpotPrice(SPOT_PRICE_LOW); availabilityZone.putSpotPrice(spotPrice); } { MockSpotPrice spotPrice = new MockSpotPrice(); spotPrice.setType(INSTANCE_TYPE_2); spotPrice.setSpotPrice(SPOT_PRICE_EQUAL); availabilityZone.putSpotPrice(spotPrice); } { MockSpotPrice spotPrice = new MockSpotPrice(); spotPrice.setType(INSTANCE_TYPE_3); spotPrice.setSpotPrice(SPOT_PRICE_HIGH); availabilityZone.putSpotPrice(spotPrice); } // Pre-configured subnets for AVAILABILITY_ZONE_1 { MockSubnet subnet = new MockSubnet(); subnet.setSubnetId(SUBNET_1); subnet.setAvailableIpAddressCount(AVAILABLE_IP_LOW); availabilityZone.putSubnet(subnet); } { MockSubnet subnet = new MockSubnet(); subnet.setSubnetId(SUBNET_2); subnet.setAvailableIpAddressCount(AVAILABLE_IP_HIGH); availabilityZone.putSubnet(subnet); } } // Pre-configure AVAILABILITY_ZONE_2 { MockAvailabilityZone availabilityZone = new MockAvailabilityZone(); availabilityZone.setZoneName(AVAILABILITY_ZONE_2); mockEc2Region.putAvailabilityZone(availabilityZone); // Pre-configured instance types for AVAILABILITY_ZONE_2 { MockSpotPrice spotPrice = new MockSpotPrice(); spotPrice.setType(INSTANCE_TYPE_1); spotPrice.setSpotPrice(SPOT_PRICE_EQUAL); availabilityZone.putSpotPrice(spotPrice); } // Pre-configured subnets for AVAILABILITY_ZONE_2 { MockSubnet subnet = new MockSubnet(); subnet.setSubnetId(SUBNET_3); subnet.setAvailableIpAddressCount(AVAILABLE_IP_HIGH); availabilityZone.putSubnet(subnet); } } // Pre-configure AVAILABILITY_ZONE_3 { MockAvailabilityZone availabilityZone = new MockAvailabilityZone(); availabilityZone.setZoneName(AVAILABILITY_ZONE_3); mockEc2Region.putAvailabilityZone(availabilityZone); // Pre-configured instance types for AVAILABILITY_ZONE_3 { MockSpotPrice spotPrice = new MockSpotPrice(); spotPrice.setType(INSTANCE_TYPE_1); spotPrice.setSpotPrice(SPOT_PRICE_VERY_LOW); availabilityZone.putSpotPrice(spotPrice); } { MockSpotPrice spotPrice = new MockSpotPrice(); spotPrice.setType(INSTANCE_TYPE_2); spotPrice.setSpotPrice(SPOT_PRICE_VERY_HIGH); availabilityZone.putSpotPrice(spotPrice); } // Pre-configured subnets for AVAILABILITY_ZONE_3 { MockSubnet subnet = new MockSubnet(); subnet.setSubnetId(SUBNET_4); subnet.setAvailableIpAddressCount(AVAILABLE_IP_LOW); availabilityZone.putSubnet(subnet); } } } // Pre-configure REGION_2 { MockEc2Region ec2Region = new MockEc2Region(); ec2Region.setName(REGION_2); mockEc2Regions.put(REGION_2, ec2Region); // Pre-configure availability zones for REGION_2 { MockAvailabilityZone availabilityZone = new MockAvailabilityZone(); availabilityZone.setZoneName(AVAILABILITY_ZONE_4); ec2Region.putAvailabilityZone(availabilityZone); // Pre-configure spot prices for AVAILABILITY_ZONE_4 { MockSpotPrice spotPrice = new MockSpotPrice(); spotPrice.setType(INSTANCE_TYPE_1); spotPrice.setSpotPrice(SPOT_PRICE_LOW); availabilityZone.putSpotPrice(spotPrice); } { MockSpotPrice spotPrice = new MockSpotPrice(); spotPrice.setType(INSTANCE_TYPE_2); spotPrice.setSpotPrice(SPOT_PRICE_LOW); availabilityZone.putSpotPrice(spotPrice); } // Pre-configure subnets for AVAILABILITY_ZONE_4 { MockSubnet subnet = new MockSubnet(); subnet.setSubnetId(SUBNET_5); subnet.setAvailableIpAddressCount(AVAILABLE_IP_LOW); availabilityZone.putSubnet(subnet); } } } // Populate subnets from the above configurations for (MockEc2Region ec2Region : mockEc2Regions.values()) { for (MockAvailabilityZone availabilityZone : ec2Region.getAvailabilityZones().values()) { for (MockSubnet subnet : availabilityZone.getSubnets().values()) { mockSubnets.put(subnet.getSubnetId(), subnet); } } } // Populate availability zones from the above configurations for (MockEc2Region ec2Region : mockEc2Regions.values()) { for (MockAvailabilityZone availabilityZone : ec2Region.getAvailabilityZones().values()) { mockAvailabilityZones.put(availabilityZone.getZoneName(), availabilityZone); } } // Populate availability zones from the above configurations for (MockEc2Region ec2Region : mockEc2Regions.values()) { for (MockAvailabilityZone availabilityZone : ec2Region.getAvailabilityZones().values()) { for (MockSpotPrice mockSpotPrice : availabilityZone.getSpotPrices().values()) { mockInstanceTypes.add(mockSpotPrice.getInstanceType()); } } } } @Override public DescribeInstanceAttributeResult describeInstanceAttribute(AmazonEC2Client ec2Client, DescribeInstanceAttributeRequest describeInstanceAttributeRequest) { InstanceAttribute instanceAttribute = new InstanceAttribute(); instanceAttribute.withGroups(new GroupIdentifier().withGroupId("A_TEST_SECURITY_GROUP")); return new DescribeInstanceAttributeResult().withInstanceAttribute(instanceAttribute); } @Override public void modifyInstanceAttribute(AmazonEC2Client ec2Client, ModifyInstanceAttributeRequest modifyInstanceAttributeRequest) { if (modifyInstanceAttributeRequest.getGroups() != null && modifyInstanceAttributeRequest.getGroups().get(0).equals(MockAwsOperationsHelper.AMAZON_SERVICE_EXCEPTION)) { throw new AmazonServiceException(MockAwsOperationsHelper.AMAZON_SERVICE_EXCEPTION); } if (modifyInstanceAttributeRequest.getGroups() != null && modifyInstanceAttributeRequest.getGroups().get(0).equals(MockAwsOperationsHelper.AMAZON_THROTTLING_EXCEPTION)) { AmazonServiceException throttlingException = new AmazonServiceException("test throttling exception"); throttlingException.setErrorCode("ThrottlingException"); throw throttlingException; } } /** * In-memory implementation of describeSubnets. Returns the subnets from the pre-configured subnets. * The method can be ordered to throw an AmazonServiceException with a specified error code by specifying a subnet ID with prefix "throw." followed by a * string indicating the error code to throw. The exception thrown in this manner will always set the status code to 500. */ @Override public DescribeSubnetsResult describeSubnets(AmazonEC2Client ec2Client, DescribeSubnetsRequest describeSubnetsRequest) { List<Subnet> subnets = new ArrayList<>(); List<String> requestedSubnetIds = describeSubnetsRequest.getSubnetIds(); // add all subnets if request is empty (this is AWS behavior) if (requestedSubnetIds.isEmpty()) { requestedSubnetIds.addAll(mockSubnets.keySet()); } for (String requestedSubnetId : requestedSubnetIds) { MockSubnet mockSubnet = mockSubnets.get(requestedSubnetId); // Throw exception if any of the subnet ID do not exist if (mockSubnet == null) { AmazonServiceException amazonServiceException; if (requestedSubnetId.startsWith("throw.")) { String errorCode = requestedSubnetId.substring("throw.".length()); amazonServiceException = new AmazonServiceException(errorCode); amazonServiceException.setErrorCode(errorCode); amazonServiceException.setStatusCode(500); } else { amazonServiceException = new AmazonServiceException("The subnet ID '" + requestedSubnetId + "' does not exist"); amazonServiceException.setErrorCode(Ec2DaoImpl.ERROR_CODE_SUBNET_ID_NOT_FOUND); amazonServiceException.setStatusCode(400); } throw amazonServiceException; } subnets.add(mockSubnet.toAwsObject()); } DescribeSubnetsResult describeSubnetsResult = new DescribeSubnetsResult(); describeSubnetsResult.setSubnets(subnets); return describeSubnetsResult; } @Override public DescribeAvailabilityZonesResult describeAvailabilityZones(AmazonEC2Client ec2Client, DescribeAvailabilityZonesRequest describeAvailabilityZonesRequest) { List<AvailabilityZone> availabilityZones = new ArrayList<>(); List<String> requestedZoneNames = describeAvailabilityZonesRequest.getZoneNames(); // add all AZ if request is empty (this is AWS behavior) if (requestedZoneNames.isEmpty()) { requestedZoneNames.addAll(mockAvailabilityZones.keySet()); } for (String requestedZoneName : requestedZoneNames) { // ignore AZ name which does not exist (this is AWS behavior) MockAvailabilityZone mockAvailabilityZone = mockAvailabilityZones.get(requestedZoneName); if (mockAvailabilityZone != null) { availabilityZones.add(mockAvailabilityZone.toAwsObject()); } } DescribeAvailabilityZonesResult describeAvailabilityZonesResult = new DescribeAvailabilityZonesResult(); describeAvailabilityZonesResult.setAvailabilityZones(availabilityZones); return describeAvailabilityZonesResult; } @Override public DescribeSpotPriceHistoryResult describeSpotPriceHistory(AmazonEC2Client ec2Client, DescribeSpotPriceHistoryRequest describeSpotPriceHistoryRequest) { List<SpotPrice> spotPriceHistories = new ArrayList<>(); String requestedAvailabilityZone = describeSpotPriceHistoryRequest.getAvailabilityZone(); // Get availability zones to search for Set<MockAvailabilityZone> requestedAvailabilityZones = new HashSet<>(); // If requested zone is specified, get and add if (requestedAvailabilityZone != null) { requestedAvailabilityZones.add(mockAvailabilityZones.get(requestedAvailabilityZone)); } // If requested zone is not specified, add all else { requestedAvailabilityZones.addAll(mockAvailabilityZones.values()); } // Get instance types to search for List<String> requestedInstanceTypes = describeSpotPriceHistoryRequest.getInstanceTypes(); // If not specified, add all instance types if (requestedInstanceTypes.isEmpty()) { requestedInstanceTypes.addAll(mockInstanceTypes); } // search for price for all AZ and instance types requested for (MockAvailabilityZone mockAvailabilityZone : requestedAvailabilityZones) { for (String requestedInstanceType : requestedInstanceTypes) { MockSpotPrice mockSpotPrice = mockAvailabilityZone.getSpotPrices().get(requestedInstanceType); if (mockSpotPrice != null) { spotPriceHistories.add(mockSpotPrice.toAwsObject()); } } } DescribeSpotPriceHistoryResult describeSpotPriceHistoryResult = new DescribeSpotPriceHistoryResult(); describeSpotPriceHistoryResult.setSpotPriceHistory(spotPriceHistories); return describeSpotPriceHistoryResult; } }