/************************************************************************* * Copyright 2013-2014 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. ************************************************************************/ package com.eucalyptus.cloudformation.template; import com.eucalyptus.cloudformation.CloudFormationException; import com.eucalyptus.cloudformation.ValidationErrorException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import java.time.Duration; import java.time.format.DateTimeParseException; import java.util.Collection; import java.util.List; import java.util.concurrent.TimeUnit; /** * Created by ethomas on 4/1/16. */ public class UpdatePolicy { public static class AutoScalingRollingUpdate { int maxBatchSize = 1; int minInstancesInService = 0; int minSuccessfulInstancesPercent = 100; String pauseTime; List<String> suspendProcesses = Lists.newArrayList(); boolean waitOnResourceSignals = false; public int getMaxBatchSize() { return maxBatchSize; } public void setMaxBatchSize(int maxBatchSize) { this.maxBatchSize = maxBatchSize; } public int getMinInstancesInService() { return minInstancesInService; } public void setMinInstancesInService(int minInstancesInService) { this.minInstancesInService = minInstancesInService; } public int getMinSuccessfulInstancesPercent() { return minSuccessfulInstancesPercent; } public void setMinSuccessfulInstancesPercent(int minSuccessfulInstancesPercent) { this.minSuccessfulInstancesPercent = minSuccessfulInstancesPercent; } public String getPauseTime() { return pauseTime; } public void setPauseTime(String pauseTime) { this.pauseTime = pauseTime; } public List<String> getSuspendProcesses() { return suspendProcesses; } public void setSuspendProcesses(List<String> suspendProcesses) { this.suspendProcesses = suspendProcesses; } public boolean isWaitOnResourceSignals() { return waitOnResourceSignals; } public void setWaitOnResourceSignals(boolean waitOnResourceSignals) { this.waitOnResourceSignals = waitOnResourceSignals; } } private AutoScalingRollingUpdate autoScalingRollingUpdate; public AutoScalingRollingUpdate getAutoScalingRollingUpdate() { return autoScalingRollingUpdate; } public void setAutoScalingRollingUpdate(AutoScalingRollingUpdate autoScalingRollingUpdate) { this.autoScalingRollingUpdate = autoScalingRollingUpdate; } private static int parseIntOrDefault(JsonNode parentNode, String fieldKey, int defaultValue) throws CloudFormationException { if (parentNode != null) { String strVal = JsonHelper.getString(parentNode, fieldKey); if (strVal != null) { try { return Integer.parseInt(strVal); } catch (NumberFormatException ex) { throw new ValidationErrorException("Encountered non-numeric value for property " + fieldKey); } } } return defaultValue; } public static UpdatePolicy parse(String updatePolicyJson) throws CloudFormationException { if (updatePolicyJson == null) return null; UpdatePolicy updatePolicy = new UpdatePolicy(); JsonNode updatePolicyJsonNode = JsonHelper.getJsonNodeFromString(updatePolicyJson); if (!updatePolicyJsonNode.isObject()) { throw new ValidationErrorException("UpdatePolicy is not a JSON object"); } JsonNode autoScalingRollingUpdateJsonNode = JsonHelper.checkObject(updatePolicyJsonNode,"AutoScalingRollingUpdate"); if (autoScalingRollingUpdateJsonNode != null) { AutoScalingRollingUpdate autoScalingRollingUpdate = new AutoScalingRollingUpdate(); updatePolicy.setAutoScalingRollingUpdate(autoScalingRollingUpdate); autoScalingRollingUpdate.setMaxBatchSize(parseIntOrDefault(autoScalingRollingUpdateJsonNode, "MaxBatchSize", autoScalingRollingUpdate.getMaxBatchSize())); if (autoScalingRollingUpdate.getMaxBatchSize() < 1) { throw new ValidationErrorException("MaxBatchSize must be a number greater than 0"); } autoScalingRollingUpdate.setMinInstancesInService(parseIntOrDefault(autoScalingRollingUpdateJsonNode, "MinInstancesInService", autoScalingRollingUpdate.getMinInstancesInService())); autoScalingRollingUpdate.setMinSuccessfulInstancesPercent(parseIntOrDefault(autoScalingRollingUpdateJsonNode, "MinSuccessfulInstancesPercent", autoScalingRollingUpdate.getMinSuccessfulInstancesPercent())); autoScalingRollingUpdate.setWaitOnResourceSignals(Boolean.parseBoolean(JsonHelper.getString(autoScalingRollingUpdateJsonNode, "WaitOnResourceSignals"))); if (!autoScalingRollingUpdate.isWaitOnResourceSignals() && JsonHelper.getString(autoScalingRollingUpdateJsonNode, "MinSuccessfulInstancesPercent") != null) { throw new ValidationErrorException("Can not set MinSuccessfulInstancesPercent greater than zero and not set WaitOnResourceSignals"); } String pauseTime = JsonHelper.getString(autoScalingRollingUpdateJsonNode, "PauseTime"); if (pauseTime == null) pauseTime = autoScalingRollingUpdate.isWaitOnResourceSignals() ? "PT5M" : "PT0S"; try { Duration.parse(pauseTime).getSeconds(); autoScalingRollingUpdate.setPauseTime(pauseTime); } catch (DateTimeParseException ex) { throw new ValidationErrorException("PauseTime must be an ISO-8601 compliant duration"); } JsonNode suspendProcessesNode = JsonHelper.checkArray(autoScalingRollingUpdateJsonNode, "SuspendProcesses"); if (suspendProcessesNode != null) { for (int i = 0; i < suspendProcessesNode.size(); i++) { JsonNode childNode = ((ArrayNode) suspendProcessesNode).get(i); if (!childNode.isTextual()) { throw new ValidationErrorException("SuspendProcesses must be a list of strings"); } String suspendedProcessName = childNode.asText(); autoScalingRollingUpdate.getSuspendProcesses().add(suspendedProcessName); } } } return updatePolicy; } }