Java tutorial
/* * Copyright (c) 2016 Nike, Inc. * * 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 com.nike.cerberus.operation.core; import com.amazonaws.AmazonServiceException; import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Sets; import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.domain.cloudformation.BaseParameters; import com.nike.cerberus.domain.cloudformation.CmsParameters; import com.nike.cerberus.domain.cloudformation.ConsulParameters; import com.nike.cerberus.domain.cloudformation.GatewayParameters; import com.nike.cerberus.domain.cloudformation.LaunchConfigParameters; import com.nike.cerberus.domain.cloudformation.SslConfigParametersDelegate; import com.nike.cerberus.domain.cloudformation.TagParameters; import com.nike.cerberus.domain.cloudformation.VaultParameters; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2UserDataService; import com.nike.cerberus.store.ConfigStore; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; import java.util.HashMap; import java.util.Map; import static com.nike.cerberus.ConfigConstants.CERT_PART_PUBKEY; import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; /** * Operation for updating stacks. */ public class UpdateStackOperation implements Operation<UpdateStackCommand> { private final Logger logger = LoggerFactory.getLogger(getClass()); private final Map<StackName, Class<? extends LaunchConfigParameters>> stackParameterMap; private final Map<StackName, String> stackTemplatePathMap; private final ConfigStore configStore; private final CloudFormationService cloudFormationService; private final ObjectMapper cloudformationObjectMapper; private final Ec2UserDataService ec2UserDataService; @Inject public UpdateStackOperation(final ConfigStore configStore, final CloudFormationService cloudFormationService, @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper, final Ec2UserDataService ec2UserDataService) { this.configStore = configStore; this.cloudFormationService = cloudFormationService; this.cloudformationObjectMapper = cloudformationObjectMapper; this.ec2UserDataService = ec2UserDataService; stackParameterMap = new HashMap<>(); stackParameterMap.put(StackName.CONSUL, ConsulParameters.class); stackParameterMap.put(StackName.VAULT, VaultParameters.class); stackParameterMap.put(StackName.CMS, CmsParameters.class); stackParameterMap.put(StackName.GATEWAY, GatewayParameters.class); stackTemplatePathMap = new HashMap<>(); stackTemplatePathMap.put(StackName.BASE, ConfigConstants.BASE_STACK_TEMPLATE_PATH); stackTemplatePathMap.put(StackName.CONSUL, ConfigConstants.CONSUL_STACK_TEMPLATE_PATH); stackTemplatePathMap.put(StackName.VAULT, ConfigConstants.VAULT_STACK_TEMPLATE_PATH); stackTemplatePathMap.put(StackName.CMS, ConfigConstants.CMS_STACK_TEMPLATE_PATH); stackTemplatePathMap.put(StackName.GATEWAY, ConfigConstants.GATEWAY_STACK_TEMPLATE_PATH); } @Override public void run(final UpdateStackCommand command) { final String stackId = configStore.getStackId(command.getStackName()); final Class<? extends LaunchConfigParameters> parametersClass = stackParameterMap .get(command.getStackName()); final Map<String, String> parameters; if (parametersClass != null) { parameters = getUpdateLaunchConfigParameters(command.getStackName(), command, parametersClass); } else if (StackName.BASE == command.getStackName()) { parameters = getUpdatedBaseStackParameters(command); } else { throw new IllegalArgumentException("The specified stack does not support the update stack command!"); } parameters.putAll(command.getDynamicParameters()); try { logger.info("Starting the update for {}.", command.getStackName().getName()); if (command.isOverwriteTemplate()) { cloudFormationService.updateStack(stackId, parameters, stackTemplatePathMap.get(command.getStackName()), true); } else { cloudFormationService.updateStack(stackId, parameters, true); } final StackStatus endStatus = cloudFormationService.waitForStatus(stackId, Sets.newHashSet(StackStatus.UPDATE_COMPLETE, StackStatus.UPDATE_COMPLETE_CLEANUP_IN_PROGRESS, StackStatus.UPDATE_ROLLBACK_COMPLETE)); if (endStatus == StackStatus.ROLLBACK_COMPLETE) { final String errorMessage = String.format("Unexpected end status: %s", endStatus.name()); logger.error(errorMessage); throw new UnexpectedCloudFormationStatusException(errorMessage); } logger.info("Update complete."); } catch (AmazonServiceException ase) { if (ase.getStatusCode() == 400 && StringUtils.equalsIgnoreCase(ase.getErrorMessage(), "No updates are to be performed.")) { logger.warn("CloudFormation reported no changes detected."); } else { throw ase; } } } @Override public boolean isRunnable(final UpdateStackCommand command) { boolean isRunnable = true; final String stackId = configStore.getStackId(command.getStackName()); if (StringUtils.isBlank(stackId)) { logger.error("The stack name specified has been created for this environment yet!"); isRunnable = false; } else if (!cloudFormationService.isStackPresent(stackId)) { logger.error("CloudFormation doesn't have the specified stack: {}", stackId); isRunnable = false; } return isRunnable; } private Map<String, String> getUpdateLaunchConfigParameters(final StackName stackName, final UpdateStackCommand command, final Class<? extends LaunchConfigParameters> parametersClass) { final LaunchConfigParameters launchConfigParameters = configStore.getStackParameters(stackName, parametersClass); launchConfigParameters.getLaunchConfigParameters() .setUserData(ec2UserDataService.getUserData(stackName, command.getOwnerGroup())); if (StringUtils.isNotBlank(command.getAmiId())) { launchConfigParameters.getLaunchConfigParameters().setAmiId(command.getAmiId()); } if (StringUtils.isNotBlank(command.getInstanceSize())) { launchConfigParameters.getLaunchConfigParameters().setInstanceSize(command.getInstanceSize()); } if (StringUtils.isNotBlank(command.getKeyPairName())) { launchConfigParameters.getLaunchConfigParameters().setKeyPairName(command.getKeyPairName()); } if (StringUtils.isNotBlank(command.getOwnerEmail())) { launchConfigParameters.getTagParameters().setTagEmail(command.getOwnerEmail()); } if (StringUtils.isNotBlank(command.getCostcenter())) { launchConfigParameters.getTagParameters().setTagCostcenter(command.getCostcenter()); } updateSslConfigParameters(stackName, launchConfigParameters); final TypeReference<Map<String, String>> typeReference = new TypeReference<Map<String, String>>() { }; return cloudformationObjectMapper.convertValue(launchConfigParameters, typeReference); } private void updateSslConfigParameters(final StackName stackName, final LaunchConfigParameters launchConfigParameters) { final SslConfigParametersDelegate sslConfigParameters = launchConfigParameters.getSslConfigParameters(); if (StringUtils.isNotBlank(sslConfigParameters.getCertPublicKey())) { sslConfigParameters.setCertPublicKey(configStore.getCertPart(stackName, CERT_PART_PUBKEY).get()); } if (StringUtils.isNotBlank(sslConfigParameters.getSslCertificateId())) { sslConfigParameters.setSslCertificateId(configStore.getServerCertificateId(stackName).get()); } if (StringUtils.isNotBlank(sslConfigParameters.getSslCertificateArn())) { sslConfigParameters.setSslCertificateArn(configStore.getServerCertificateArn(stackName).get()); } } private Map<String, String> getUpdatedBaseStackParameters(final UpdateStackCommand command) { final TagParameters tagParameters = configStore.getStackParameters(command.getStackName(), BaseParameters.class); if (StringUtils.isNotBlank(command.getOwnerEmail())) { tagParameters.getTagParameters().setTagEmail(command.getOwnerEmail()); } if (StringUtils.isNotBlank(command.getCostcenter())) { tagParameters.getTagParameters().setTagCostcenter(command.getCostcenter()); } final TypeReference<Map<String, String>> typeReference = new TypeReference<Map<String, String>>() { }; return cloudformationObjectMapper.convertValue(tagParameters, typeReference); } }