package tw.com.commandline; import com.amazonaws.services.cloudformation.model.Parameter; import org.apache.commons.cli.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import tw.com.AwsFacade; import tw.com.entity.Tagging; import tw.com.exceptions.InvalidStackParameterException; import tw.com.providers.SNSEventSource; import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.List; public class CommandFlags { private static final Logger logger = LoggerFactory.getLogger(CommandFlags.class); private Option projectParam; private Option envParam; private Option regionParam; private Option buildNumberParam; private Option keysValuesParam; private Option snsParam; private Option capIAMParam; private Option commentParam; private Option artifactParam; private Option bucketParam; private String executableName; private Options commandLineOptions; private String project; private String env; private String region; private Integer buildNumber = null; private Boolean sns; private Boolean capabilityIAM; private String comment; private String s3bucket; private Collection<Parameter> cfnParams; private Collection<Parameter> artifacts; public CommandFlags(String executableName, Options commandLineOptions) { this.executableName = executableName; this.commandLineOptions = commandLineOptions; createOptions(); addOptions(); } public void addOptions() { commandLineOptions.addOption(projectParam); commandLineOptions.addOption(envParam); commandLineOptions.addOption(regionParam); commandLineOptions.addOption(keysValuesParam); commandLineOptions.addOption(buildNumberParam); commandLineOptions.addOption(snsParam); commandLineOptions.addOption(capIAMParam); commandLineOptions.addOption(commentParam); commandLineOptions.addOption(artifactParam); commandLineOptions.addOption(bucketParam); } public void populateFlags(CommandLine commandLine, HelpFormatter formatter) throws MissingArgumentException, InvalidStackParameterException { project = checkForArgument(commandLine, formatter, projectParam, AwsFacade.PROJECT_TAG, false); env = checkForArgument(commandLine, formatter, envParam, AwsFacade.ENVIRONMENT_TAG, false); region = checkForArgument(commandLine, formatter, regionParam, Main.ENV_VAR_EC2_REGION, true); String buildNumberAsString = checkForArgument(commandLine, formatter, buildNumberParam, AwsFacade.BUILD_TAG, false); if (!buildNumberAsString.isEmpty()) { buildNumber = Integer.parseInt(buildNumberAsString); } sns = checkForArgumentPresent(commandLine, snsParam); capabilityIAM = checkForArgumentPresent(commandLine, capIAMParam); comment = checkForArgument(commandLine, formatter, commentParam, "", false); cfnParams = checkForKeyValueParameters(commandLine, keysValuesParam); artifacts = checkForKeyValueParameters(commandLine, artifactParam); boolean bucketRequired = (!artifacts.isEmpty()); s3bucket = checkForArgument(commandLine, formatter, bucketParam, AwsFacade.ENV_S3_BUCKET, bucketRequired); } @SuppressWarnings("static-access") private void createOptions() { projectParam = createParam("project", "Name of the cfnassist project, or use env var: " + AwsFacade.PROJECT_TAG); envParam = createParam("env", "Name of cfnassit environment, or use env var: " + AwsFacade.ENVIRONMENT_TAG); regionParam = createParam("region", "AWS Region name, or use env var: " + Main.ENV_VAR_EC2_REGION); keysValuesParam = createParamMultiArgs("parameters", "Provide paramters for cfn scripts, format as per cfn commandline tools"); buildNumberParam = createParam("build", "A Build number/id to tag the deployed stacks with, or use env var: " + AwsFacade.BUILD_TAG); snsParam = createParamNoArg("sns", String.format("Use SNS to publish updates from cloud formation, uses the topic %s" , SNSEventSource.SNS_TOPIC_NAME)); capIAMParam = createParamNoArg("capabilityIAM", "Pass capability IAM to create stack (needed if you get capability missing exceptions)"); commentParam = createParam("comment", "Add a comment within the tag " + Tagging.COMMENT_TAG); artifactParam = createParamMultiArgs("artifacts","Provide files to be uploaded to S3 bucket, param values will " + "be replaced with the S3 URLs and passed into the template file"); bucketParam = createParam("bucket", "Bucket name to use for S3 artifacts"); } private Option createParamMultiArgs(String name, String description) { return Option.builder(name). argName(name).desc(description).valueSeparator(';').hasArgs().build(); } private Option createParamNoArg(String name, String description) { return Option.builder(name). argName(name).desc(description).hasArg(false).build(); } private Option createParam(String name, String description) { return Option.builder(). argName(name). longOpt(name). desc(description). hasArg(true). build(); } private String checkForArgument(CommandLine cmd, HelpFormatter formatter, Option option, String environmentalVar, boolean required) throws MissingArgumentException { String argName = option.getArgName(); logger.debug("Checking for arg " + argName); if (cmd.hasOption(argName)) { String optionValue = cmd.getOptionValue(argName); logger.info("Got value " + optionValue); return optionValue; } if (!environmentalVar.isEmpty()) { logger.info(String.format("Argument not given %s, try environmental var %s", argName, environmentalVar)); String fromEnv = System.getenv(environmentalVar); if (fromEnv!=null) { logger.info("Got value " + fromEnv); return fromEnv; } } if (required) { formatter.printHelp( executableName, commandLineOptions); throw new MissingArgumentException(option); } return ""; } private Collection<Parameter> checkForKeyValueParameters(CommandLine cmd, Option commandFlag) throws InvalidStackParameterException { LinkedList<Parameter> results = new LinkedList<>(); String argName = commandFlag.getArgName(); logger.debug("Checking for arg " + argName); if (!cmd.hasOption(argName)) { logger.debug("Additional parameters not supplied"); return results; } logger.info("Process additional parameters"); List<String> valuesList = Arrays.asList(cmd.getOptionValues(argName)); logger.debug(String.format("Found %s arguments inside of parameter", valuesList.size())); for(String keyValue : valuesList) { String[] parts = keyValue.split("="); if (parts.length!=2) { String msg = "Unable to process parameters given, problem with " + keyValue; logger.error(msg); throw new InvalidStackParameterException(msg); } Parameter pair = new Parameter(); pair.setParameterKey(parts[0]); pair.setParameterValue(parts[1]); results.add(pair); logger.info("Add parameter " + keyValue); } return results; } private Boolean checkForArgumentPresent(CommandLine commandLine, Option option) { String argName = option.getArgName(); logger.debug("Checking for arg " + argName); return commandLine.hasOption(argName); } public String getProject() { return project; } public String getEnv() { return env; } public String getRegion() { return region; } public Integer getBuildNumber() { return buildNumber; } public Boolean getSns() { return sns; } public String getComment() { return comment; } public Collection<Parameter> getAdditionalParameters() { return cfnParams; } public boolean haveBuildNumber() { return buildNumber!=null; } public boolean haveComment() { return !comment.isEmpty(); } public boolean haveSnsEnable() { return sns; } public Collection<Parameter> getUploadParams() { return artifacts; } public String getS3Bucket() { return s3bucket; } public boolean haveS3Bucket() { return !s3bucket.isEmpty(); } public boolean haveCapabilityIAM() { return capabilityIAM; } }