package tw.com.parameters; import com.amazonaws.services.cloudformation.model.Parameter; import com.amazonaws.services.cloudformation.model.TemplateParameter; import com.amazonaws.services.ec2.model.AvailabilityZone; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import tw.com.entity.EnvironmentTag; import tw.com.entity.ProjectAndEnv; import tw.com.exceptions.CannotFindVpcException; import tw.com.exceptions.InvalidStackParameterException; import tw.com.repository.CloudFormRepository; import tw.com.repository.VpcRepository; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class AutoDiscoverParams extends PopulatesParameters { private static final Logger logger = LoggerFactory.getLogger(AutoDiscoverParams.class); private File templateFile; private VpcRepository vpcRepository; private CloudFormRepository cfnRepository; public AutoDiscoverParams(File file,VpcRepository vpcRepository,CloudFormRepository cfnRepository) { this.vpcRepository = vpcRepository; this.cfnRepository = cfnRepository; this.templateFile = file; } @Override public void addParameters(Collection<Parameter> result, List<TemplateParameter> declaredParameters, ProjectAndEnv projAndEnv, ProvidesZones providesZones) throws CannotFindVpcException, IOException, InvalidStackParameterException { Map<String, AvailabilityZone> zones = providesZones.getZones(); List<Parameter> autoPopulatedParametes = fetchAutopopulateParametersFor(projAndEnv, declaredParameters, zones); result.addAll(autoPopulatedParametes.stream().collect(Collectors.toList())); } private List<Parameter> fetchAutopopulateParametersFor(ProjectAndEnv projectAndEnv, List<TemplateParameter> declaredParameters, Map<String, AvailabilityZone> zones) throws IOException, InvalidStackParameterException, CannotFindVpcException { logger.info(String.format("Discover and populate parameters for %s and %s", templateFile.getAbsolutePath(), projectAndEnv)); List<Parameter> matches = new LinkedList<>(); for(TemplateParameter templateParam : declaredParameters) { String name = templateParam.getParameterKey(); if (isBuiltInParamater(name)) { continue; } logger.info("Checking if parameter should be auto-populated from an existing resource, param name is " + name); String description = templateParam.getDescription(); if (shouldPopulateFor(description)) { populateParameter(projectAndEnv, matches, name, description, declaredParameters, zones); } } return matches; } private boolean isBuiltInParamater(String name) { boolean result = name.equals(PopulatesParameters.PARAMETER_ENV); if (result) { logger.info("Found built in parameter"); } return result; } private boolean shouldPopulateFor(String description) { if (description==null) { return false; } return description.startsWith(PopulatesParameters.PARAM_PREFIX) && (!description.equals(PopulatesParameters.ENV_TAG)); } private void populateParameter(ProjectAndEnv projectAndEnv, List<Parameter> results, String parameterName, String parameterDescription, List<TemplateParameter> declaredParameters, Map<String, AvailabilityZone> zones) throws InvalidStackParameterException, CannotFindVpcException { if (parameterDescription.equals(PopulatesParameters.CFN_TAG_ON_OUTPUT)) { populateParameterFromVPCTag(projectAndEnv, results, parameterName, declaredParameters); } else if (parameterDescription.startsWith(PopulatesParameters.CFN_TAG_ZONE)) { populateParamForZone(results, declaredParameters, zones, parameterName, parameterDescription); } else { populateParameterFromPhysicalID(projectAndEnv.getEnvTag(), results, parameterName, parameterDescription, declaredParameters); } } private void populateParamForZone(Collection<Parameter> results, List<TemplateParameter> declaredParameters, Map<String, AvailabilityZone> zones, String parameterName, String parameterDescription) { logger.info(String.format("Check parameter for zone %s and target %s", parameterName, parameterDescription)); String target = parameterDescription.replaceFirst(PopulatesParameters.CFN_TAG_ZONE, "").toLowerCase(); logger.debug("Check for zone " + target); if (zones.containsKey(target)) { String zoneName = zones.get(target).getZoneName(); declaredParameters.stream().filter(declaredParameter -> declaredParameter.getParameterKey().equals(parameterName)). forEach(declaredParameter -> { addParameterTo(results, declaredParameters, parameterName, zoneName); logger.info(String.format("Adding zone parameter %s with value %s", parameterName, zoneName)); }); } else { logger.error("Could not find matching zone for target " + target); } } private void populateParameterFromVPCTag(ProjectAndEnv projectAndEnv, List<Parameter> results, String parameterName, List<TemplateParameter> declaredParameters) throws CannotFindVpcException, InvalidStackParameterException { logger.info("Attempt to find VPC matching name: " + parameterName); String value = vpcRepository.getVpcTag(parameterName, projectAndEnv); if (value==null) { String msg = String.format("Failed to find VPC TAG matching: %s", parameterName); logger.error(msg); throw new InvalidStackParameterException(msg); } addParameterTo(results, declaredParameters, parameterName, value); } private void populateParameterFromPhysicalID(EnvironmentTag envTag, List<Parameter> matches, String parameterName, String parameterDescription, List<TemplateParameter> declaredParameters) throws InvalidStackParameterException { String logicalId = parameterDescription.substring(PopulatesParameters.PARAM_PREFIX.length()); logger.info("Attempt to find physical ID for LogicalID: " + logicalId); String value = cfnRepository.findPhysicalIdByLogicalId(envTag, logicalId); if (value==null) { String msg = String.format("Failed to find physicalID to match logicalID: %s required for parameter: %s" , logicalId, parameterName); logger.error(msg); throw new InvalidStackParameterException(msg); } logger.info(String.format("Found physicalID: %s matching logicalID: %s Populating this into parameter %s", value, logicalId, parameterName)); addParameterTo(matches, declaredParameters, parameterName, value); } }