/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.amazontools; import com.amazonaws.services.autoscaling.model.AutoScalingGroup; import com.amazonaws.services.autoscaling.model.CreateOrUpdateTagsRequest; import com.amazonaws.services.autoscaling.model.DescribeAutoScalingGroupsResult; import com.amazonaws.services.ec2.model.AssociateAddressRequest; import com.amazonaws.services.ec2.model.CreateTagsRequest; import com.amazonaws.services.ec2.model.Tag; import com.liferay.petra.json.web.service.client.JSONWebServiceClient; import jargs.gnu.CmdLineParser; import java.awt.Desktop; import java.io.File; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.json.JSONArray; import org.json.JSONObject; /** * @author Ivica Cardic */ public class AsgardAMIDeployer extends BaseAMITool { public static void main(String[] args) throws Exception { CmdLineParser cmdLineParser = new CmdLineParser(); CmdLineParser.Option baseDirOption = cmdLineParser.addStringOption( "base.dir"); CmdLineParser.Option imageNameOption = cmdLineParser.addStringOption( "image.name"); CmdLineParser.Option openAsgardURLOption = cmdLineParser.addBooleanOption("open.asgard.url"); CmdLineParser.Option parallelDeploymentOption = cmdLineParser.addBooleanOption("parallel.deployment"); CmdLineParser.Option propertiesFileNameOption = cmdLineParser.addStringOption("properties.file.name"); cmdLineParser.parse(args); try { new AsgardAMIDeployer( (String)cmdLineParser.getOptionValue(baseDirOption), (String)cmdLineParser.getOptionValue(imageNameOption), (Boolean)cmdLineParser.getOptionValue( openAsgardURLOption, Boolean.FALSE), (Boolean)cmdLineParser.getOptionValue( parallelDeploymentOption, Boolean.FALSE), (String)cmdLineParser.getOptionValue(propertiesFileNameOption)); } catch (Exception e) { e.printStackTrace(); System.exit(-1); return; } System.exit(0); } public AsgardAMIDeployer( String baseDirName, String imageName, boolean openAsgardURLOption, boolean parallelDeployment, String propertiesFileName) throws Exception { super(propertiesFileName); _baseDirName = baseDirName; _imageName = imageName; _parallelDeployment = parallelDeployment; _jsonWebServiceClient = getJSONWebServiceClient( properties.getProperty("asgard.host.name"), Integer.valueOf(properties.getProperty("asgard.host.port")), _baseDirName + File.separator + properties.getProperty("asgard.key.store.path"), properties.getProperty("asgard.key.store.password"), properties.getProperty("asgard.login"), properties.getProperty("asgard.password")); System.out.println("Creating Auto Scaling Group"); String autoScalingGroupName = createAutoScalingGroup(); System.out.println( "Created Auto Scaling Group " + autoScalingGroupName); int minSize = -1; if (!_parallelDeployment) { minSize = Integer.parseInt( properties.getProperty("instance.min.size")); } System.out.println( "Checking Auto Scaling Group " + autoScalingGroupName); List<String> instanceIds = checkAutoScalingGroup( autoScalingGroupName, minSize); associateElasticIpAddresses(instanceIds); deactivateOldScalingGroup(autoScalingGroupName); if (openAsgardURLOption) { openAsgardURL(); } System.out.println( "Deployed Auto Scaling Group " + autoScalingGroupName); } protected void associateElasticIpAddresses(List<String> instanceIds) { System.out.println("Associating Elastic IP addresses"); if (!properties.containsKey("elastic.ip.addresses")) { return; } String elasticIpAddressesString = properties.getProperty( "elastic.ip.addresses"); if ((elasticIpAddressesString == null) || (elasticIpAddressesString.length() == 0)) { return; } String[] elasticIpAddresses = elasticIpAddressesString.split(","); for (int i = 0; (i < elasticIpAddresses.length) && (i < instanceIds.size()); i++) { System.out.println( "Associating IP address " + elasticIpAddresses[i] + " with instance " + instanceIds.get(i)); AssociateAddressRequest associateAddressRequest = new AssociateAddressRequest(); associateAddressRequest.setInstanceId(instanceIds.get(i)); associateAddressRequest.setPublicIp(elasticIpAddresses[i]); amazonEC2Client.associateAddress(associateAddressRequest); } } protected List<String> checkAutoScalingGroup( String autoScalingGroupName, int size) throws Exception { String asgardClusterName = properties.getProperty( "asgard.cluster.name"); String availabilityZone = properties.getProperty("availability.zone"); boolean deployed = false; JSONObject loadBalancerJSONObject = null; for (int i = 1;; i++) { String json = _jsonWebServiceClient.doGet( "/" + availabilityZone + "/loadBalancer/show/" + asgardClusterName + ".json", Collections.<String, String>emptyMap()); loadBalancerJSONObject = new JSONObject(json); List<JSONObject> instanceStateJSONObjects = getInstanceStateJSONObjects( loadBalancerJSONObject, autoScalingGroupName); if (size != -1) { if (instanceStateJSONObjects.size() < size) { System.out.println( "Not enough instances started. Waiting " + i + "..."); sleep(15); continue; } } if (!isInService(loadBalancerJSONObject, autoScalingGroupName)) { System.out.println( "Instances not in service. Waiting " + i + "..."); sleep(15); } else { deployed = true; break; } } if (!deployed) { Map<String, String> parameters = new HashMap<>(); parameters.put("name", autoScalingGroupName); _jsonWebServiceClient.doPost( "/" + availabilityZone + "/cluster/delete", parameters); throw new RuntimeException( "Unable to deploy Auto Scaling Group " + autoScalingGroupName); } List<String> instanceIds = new ArrayList<>(); List<JSONObject> instanceStateJSONObjects = getInstanceStateJSONObjects( loadBalancerJSONObject, autoScalingGroupName); for (int i = 0; i < instanceStateJSONObjects.size(); i++) { JSONObject instanceStateJSONObject = instanceStateJSONObjects.get( i); String instanceId = instanceStateJSONObject.getString("instanceId"); instanceIds.add(instanceId); } return instanceIds; } protected String createAutoScalingGroup() throws Exception { DescribeAutoScalingGroupsResult describeAutoScalingGroupsResult = amazonAutoScalingClient.describeAutoScalingGroups(); List<AutoScalingGroup> autoScalingGroups = describeAutoScalingGroupsResult.getAutoScalingGroups(); int oldAutoScalingGroupsSize = autoScalingGroups.size(); String availabilityZone = properties.getProperty("availability.zone"); Map<String, String> parameters = new HashMap<>(); parameters.put("checkHealth", "true"); parameters.put("imageId", getImageId(_imageName)); String asgardClusterName = properties.getProperty( "asgard.cluster.name"); parameters.put("name", asgardClusterName); parameters.put("trafficAllowed", "true"); if (!_parallelDeployment) { parameters.put("desiredCapacity", "1"); parameters.put("min", "1"); } _jsonWebServiceClient.doPost( "/" + availabilityZone + "/cluster/createNextGroup", parameters); for (int i = 0; i < 30; i++) { describeAutoScalingGroupsResult = amazonAutoScalingClient.describeAutoScalingGroups(); autoScalingGroups = describeAutoScalingGroupsResult.getAutoScalingGroups(); int newAutoScalingGroupsSize = autoScalingGroups.size(); if (oldAutoScalingGroupsSize == newAutoScalingGroupsSize) { sleep(15); } else { break; } } String autoScalingGroupName = null; boolean created = false; int maxSize = 0; for (int i = 0; i < 30; i++) { String json = _jsonWebServiceClient.doGet( "/" + availabilityZone + "/cluster/show/" + asgardClusterName + ".json", Collections.<String, String>emptyMap()); JSONArray autoScalingGroupsJSONArray = new JSONArray(json); JSONObject autoScalingGroupJSONObject = autoScalingGroupsJSONArray.getJSONObject( autoScalingGroupsJSONArray.length() - 1); autoScalingGroupName = autoScalingGroupJSONObject.getString( "autoScalingGroupName"); maxSize = autoScalingGroupJSONObject.getInt("maxSize"); List<String> instanceIds = new ArrayList<>(); JSONArray instancesJSONArray = autoScalingGroupJSONObject.getJSONArray("instances"); for (int j = 0; j < instancesJSONArray.length(); j++) { JSONObject instanceJSONObject = instancesJSONArray.getJSONObject(j); instanceIds.add(instanceJSONObject.getString("instanceId")); } if (instanceIds.isEmpty() || !isInService(autoScalingGroupJSONObject)) { sleep(15); } else { CreateTagsRequest createTagsRequest = new CreateTagsRequest(); createTagsRequest.setResources(instanceIds); List<Tag> tags = new ArrayList<>(); Tag tag = new Tag(); tag.withKey("Name"); tag.withValue(properties.getProperty("instance.name")); tags.add(tag); createTagsRequest.setTags(tags); amazonEC2Client.createTags(createTagsRequest); CreateOrUpdateTagsRequest createOrUpdateTagsRequest = new CreateOrUpdateTagsRequest(); com.amazonaws.services.autoscaling.model.Tag autoScalingTag = new com.amazonaws.services.autoscaling.model.Tag(); autoScalingTag.setKey("Name"); autoScalingTag.setPropagateAtLaunch(true); autoScalingTag.setResourceId(autoScalingGroupName); autoScalingTag.setResourceType("auto-scaling-group"); autoScalingTag.setValue( properties.getProperty("instance.name")); createOrUpdateTagsRequest.withTags(autoScalingTag); amazonAutoScalingClient.createOrUpdateTags( createOrUpdateTagsRequest); created = true; break; } } if (!created) { throw new RuntimeException( "Unable to create Auto Scaling Group " + autoScalingGroupName); } if (!_parallelDeployment) { int minSize = Integer.parseInt( properties.getProperty("instance.min.size")); if (minSize > 1) { checkAutoScalingGroup(autoScalingGroupName, 1); parameters.clear(); parameters.put("maxSize", String.valueOf(maxSize)); parameters.put("minSize", String.valueOf(minSize)); parameters.put("name", autoScalingGroupName); _jsonWebServiceClient.doPost( "/" + availabilityZone + "/cluster/resize", parameters); for (int i = 0; i < 30; i++) { String json = _jsonWebServiceClient.doGet( "/" + availabilityZone + "/cluster/show/" + asgardClusterName + ".json", Collections.<String, String>emptyMap()); JSONArray autoScalingGroupsJSONArray = new JSONArray(json); JSONObject autoScalingGroupJSONObject = autoScalingGroupsJSONArray.getJSONObject( autoScalingGroupsJSONArray.length() - 1); JSONArray instancesJSONArray = autoScalingGroupJSONObject.getJSONArray("instances"); if (instancesJSONArray.length() == 1) { sleep(15); } else { break; } } } } return autoScalingGroupName; } protected void deactivateOldScalingGroup(String autoScalingGroupName) throws Exception { String asgardClusterName = properties.getProperty( "asgard.cluster.name"); String availabilityZone = properties.getProperty("availability.zone"); String json = _jsonWebServiceClient.doGet( "/" + availabilityZone + "/cluster/list.json", Collections.<String, String>emptyMap()); JSONArray clustersJSONArray = new JSONArray(json); for (int i = 0; i < clustersJSONArray.length(); i++) { JSONObject clusterJSONObject = clustersJSONArray.getJSONObject(i); String curAsgardClusterName = clusterJSONObject.getString( "cluster"); if (!asgardClusterName.equals(curAsgardClusterName)) { continue; } JSONArray autoScalingGroupsJSONArray = clusterJSONObject.getJSONArray("autoScalingGroups"); for (int j = 0; j < autoScalingGroupsJSONArray.length(); j++) { String curAutoScalingGroupName = autoScalingGroupsJSONArray.getString(j); if (autoScalingGroupName.equals(curAutoScalingGroupName)) { continue; } Map<String, String> parameters = new HashMap<>(); parameters.put("name", curAutoScalingGroupName); _jsonWebServiceClient.doPost( "/" + availabilityZone + "/cluster/deactivate", parameters); } } } protected List<JSONObject> getInstanceStateJSONObjects( JSONObject loadBalancerJSONObject, String autoScalingGroupName) { List<JSONObject> instanceStateJSONObjects = new ArrayList<>(); JSONArray instanceStatesJSONArray = loadBalancerJSONObject.getJSONArray( "instanceStates"); for (int i = 0; i < instanceStatesJSONArray.length(); i++) { JSONObject instanceStateJSONObject = instanceStatesJSONArray.getJSONObject(i); Object instanceStateAutoScalingGroupName = instanceStateJSONObject.get("autoScalingGroupName"); if (autoScalingGroupName.equals( instanceStateAutoScalingGroupName)) { instanceStateJSONObjects.add(instanceStateJSONObject); } } return instanceStateJSONObjects; } protected boolean isInService(JSONObject autoScalingGroupJSONObject) { JSONArray instancesJSONArray = autoScalingGroupJSONObject.getJSONArray( "instances"); for (int i = 0; i < instancesJSONArray.length(); i++) { JSONObject instance = instancesJSONArray.getJSONObject(i); String lifecycleState = instance.getString("lifecycleState"); if (!lifecycleState.equals("InService")) { return false; } } return true; } protected boolean isInService( JSONObject loadBalancerJSONObject, String autoScalingGroupName) { List<JSONObject> instanceStateJSONObjects = getInstanceStateJSONObjects( loadBalancerJSONObject, autoScalingGroupName); if (instanceStateJSONObjects.isEmpty()) { return false; } for (int i = 0; i < instanceStateJSONObjects.size(); i++) { JSONObject instanceStateJSONObject = instanceStateJSONObjects.get( i); String state = instanceStateJSONObject.getString("state"); if (!state.equals("InService")) { return false; } } return true; } protected void openAsgardURL() throws Exception { Desktop desktop = Desktop.getDesktop(); StringBuilder sb = new StringBuilder(); sb.append(properties.getProperty("asgard.host.protocol")); sb.append("://"); sb.append(properties.getProperty("asgard.host.name")); sb.append(":"); sb.append(properties.getProperty("asgard.host.port")); sb.append("/"); sb.append(properties.getProperty("availability.zone")); sb.append("/cluster/show/"); sb.append(properties.getProperty("asgard.cluster.name")); desktop.browse(URI.create(sb.toString())); } private String _baseDirName; private String _imageName; private JSONWebServiceClient _jsonWebServiceClient; private boolean _parallelDeployment; }