package com.sequenceiq.cloudbreak.service.decorator; import static org.springframework.util.StringUtils.isEmpty; import java.util.Optional; import java.util.Set; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.convert.ConversionService; import org.springframework.stereotype.Component; import com.sequenceiq.cloudbreak.api.model.ConstraintJson; import com.sequenceiq.cloudbreak.api.model.RecipeRequest; import com.sequenceiq.cloudbreak.controller.BadRequestException; import com.sequenceiq.cloudbreak.domain.CbUser; import com.sequenceiq.cloudbreak.domain.Cluster; import com.sequenceiq.cloudbreak.domain.Constraint; import com.sequenceiq.cloudbreak.domain.ConstraintTemplate; import com.sequenceiq.cloudbreak.domain.HostGroup; import com.sequenceiq.cloudbreak.domain.InstanceGroup; import com.sequenceiq.cloudbreak.domain.Recipe; import com.sequenceiq.cloudbreak.domain.Stack; import com.sequenceiq.cloudbreak.repository.ConstraintRepository; import com.sequenceiq.cloudbreak.repository.ConstraintTemplateRepository; import com.sequenceiq.cloudbreak.repository.InstanceGroupRepository; import com.sequenceiq.cloudbreak.service.cluster.ClusterService; import com.sequenceiq.cloudbreak.service.hostgroup.HostGroupService; import com.sequenceiq.cloudbreak.service.recipe.RecipeService; import com.sequenceiq.cloudbreak.service.stack.StackService; @Component public class HostGroupDecorator implements Decorator<HostGroup> { private static final Logger LOGGER = LoggerFactory.getLogger(HostGroupDecorator.class); private enum DecorationData { STACK_ID, USER, CONSTRAINT, RECIPE_IDS, REQUEST_TYPE, RECIPES, PUBLIC_IN_ACCOUNT } @Inject private InstanceGroupRepository instanceGroupRepository; @Inject private ConstraintTemplateRepository constraintTemplateRepository; @Inject private ConstraintRepository constraintRepository; @Inject private HostGroupService hostGroupService; @Inject private StackService stackService; @Inject private RecipeService recipeService; @Inject private ConversionService conversionService; @Inject private ClusterService clusterService; @Override public HostGroup decorate(HostGroup subject, Object... data) { if (null == data || data.length != DecorationData.values().length) { throw new IllegalArgumentException("Invalid decoration data provided. Cluster: " + subject.getName()); } Long stackId = (Long) data[DecorationData.STACK_ID.ordinal()]; CbUser user = (CbUser) data[DecorationData.USER.ordinal()]; ConstraintJson constraintJson = (ConstraintJson) data[DecorationData.CONSTRAINT.ordinal()]; Set<Long> recipeIds = (Set<Long>) data[DecorationData.RECIPE_IDS.ordinal()]; boolean postRequest = (boolean) data[DecorationData.REQUEST_TYPE.ordinal()]; Set<RecipeRequest> recipes = (Set<RecipeRequest>) data[DecorationData.RECIPES.ordinal()]; Boolean publicInAccount = (Boolean) data[DecorationData.PUBLIC_IN_ACCOUNT.ordinal()]; LOGGER.debug("Decorating hostgroup on [{}] request.", postRequest ? "POST" : "PUT"); Constraint constraint = conversionService.convert(constraintJson, Constraint.class); if (postRequest) { constraint = decorateConstraint(stackId, user, constraint, constraintJson.getInstanceGroupName(), constraintJson.getConstraintTemplateName()); subject.setConstraint(constraint); } else { subject = getHostGroup(stackId, constraint, constraintJson, subject, user); } subject.getRecipes().clear(); if (recipeIds != null) { for (Long recipeId : recipeIds) { Recipe recipe = recipeService.get(recipeId); subject.getRecipes().add(recipe); } } else if (recipes != null && !recipes.isEmpty()) { for (RecipeRequest recipe : recipes) { Recipe convert = conversionService.convert(recipe, Recipe.class); convert.setPublicInAccount(publicInAccount); convert = recipeService.create(user, convert); subject.getRecipes().add(convert); } } return subject; } private Constraint decorateConstraint(Long stackId, CbUser user, Constraint constraint, String instanceGroupName, String constraintTemplateName) { if (instanceGroupName != null) { InstanceGroup instanceGroup = instanceGroupRepository.findOneByGroupNameInStack(stackId, instanceGroupName); if (instanceGroup == null) { LOGGER.error("Instance group not found: {}", instanceGroupName); throw new BadRequestException(String.format("Instance group '%s' not found on stack.", instanceGroupName)); } constraint.setInstanceGroup(instanceGroup); } if (constraintTemplateName != null) { ConstraintTemplate constraintTemplate = constraintTemplateRepository.findByNameInAccount(constraintTemplateName, user.getAccount(), user.getUserId()); if (constraintTemplate == null) { throw new BadRequestException(String.format("Couldn't find constraint template with name: %s", constraintTemplateName)); } constraint.setConstraintTemplate(constraintTemplate); } return constraint; } private HostGroup getHostGroup(Long stackId, Constraint constraint, ConstraintJson constraintJson, HostGroup subject, CbUser user) { if (constraintJson == null) { throw new BadRequestException("The constraint field must be set in the reinstall request!"); } HostGroup result = subject; final String instanceGroupName = constraintJson.getInstanceGroupName(); final String constraintTemplateName = constraintJson.getConstraintTemplateName(); Cluster cluster = clusterService.retrieveClusterByStackId(stackId); Constraint decoratedConstraint = decorateConstraint(stackId, user, constraint, instanceGroupName, constraintTemplateName); if (!isEmpty(instanceGroupName)) { result = getHostGroupByInstanceGroupName(decoratedConstraint, subject, cluster, instanceGroupName); } else if (!isEmpty(constraintTemplateName)) { subject.setConstraint(constraintRepository.save(constraint)); } else { throw new BadRequestException("The constraint field must contain the 'constraintTemplateName' or 'instanceGroupName' parameter!"); } return result; } private HostGroup getHostGroupByInstanceGroupName(Constraint constraint, HostGroup subject, Cluster cluster, final String instanceGroupName) { HostGroup result = subject; Set<HostGroup> hostGroups = hostGroupService.getByCluster(cluster.getId()); if (hostGroups.isEmpty()) { Stack stack = cluster.getStack(); if (stack == null) { String msg = String.format("There is no stack associated to cluster (id:'%s', name: '%s')!", cluster.getId(), cluster.getName()); throw new BadRequestException(msg); } else { subject.setConstraint(constraint); } } else { result = getDetailsFromExistingHostGroup(constraint, subject, instanceGroupName, hostGroups); } return result; } private HostGroup getDetailsFromExistingHostGroup(Constraint constraint, HostGroup subject, final String instanceGroupName, Set<HostGroup> hostGroups) { Optional<HostGroup> hostGroupOptional = hostGroups.stream().filter(input -> input.getConstraint().getInstanceGroup().getGroupName().equals(instanceGroupName) ).findFirst(); if (hostGroupOptional.isPresent()) { HostGroup hostGroup = hostGroupOptional.get(); Integer instanceGroupNodeCount = hostGroup.getConstraint().getInstanceGroup().getNodeCount(); if (constraint.getHostCount() > instanceGroupNodeCount) { throw new BadRequestException(String.format("The 'hostCount' of host group '%s' constraint could not be more than '%s'!", subject.getName(), instanceGroupNodeCount)); } hostGroup.getConstraint().setHostCount(constraint.getHostCount()); hostGroup.setName(subject.getName()); return hostGroup; } else { throw new BadRequestException(String.format("Invalid 'instanceGroupName'! Could not find instance group with name: '%s'", instanceGroupName)); } } }