package com.sequenceiq.cloudbreak.service.stack;
import static com.sequenceiq.cloudbreak.api.model.InstanceGroupType.isGateway;
import static com.sequenceiq.cloudbreak.api.model.Status.AVAILABLE;
import static com.sequenceiq.cloudbreak.api.model.Status.STOPPED;
import static com.sequenceiq.cloudbreak.api.model.Status.STOP_REQUESTED;
import static com.sequenceiq.cloudbreak.api.model.StatusRequest.FULL_SYNC;
import static com.sequenceiq.cloudbreak.common.type.CloudConstants.BYOS;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.inject.Inject;
import javax.transaction.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.sequenceiq.cloudbreak.api.model.AutoscaleStackResponse;
import com.sequenceiq.cloudbreak.api.model.DetailedStackStatus;
import com.sequenceiq.cloudbreak.api.model.InstanceGroupAdjustmentJson;
import com.sequenceiq.cloudbreak.api.model.InstanceStatus;
import com.sequenceiq.cloudbreak.api.model.StackResponse;
import com.sequenceiq.cloudbreak.api.model.StatusRequest;
import com.sequenceiq.cloudbreak.cloud.model.CloudbreakDetails;
import com.sequenceiq.cloudbreak.cloud.model.StackTemplate;
import com.sequenceiq.cloudbreak.common.type.APIResourceType;
import com.sequenceiq.cloudbreak.common.type.CbUserRole;
import com.sequenceiq.cloudbreak.common.type.ComponentType;
import com.sequenceiq.cloudbreak.controller.BadRequestException;
import com.sequenceiq.cloudbreak.controller.CloudbreakApiException;
import com.sequenceiq.cloudbreak.controller.NotFoundException;
import com.sequenceiq.cloudbreak.controller.validation.blueprint.BlueprintValidator;
import com.sequenceiq.cloudbreak.controller.validation.network.NetworkConfigurationValidator;
import com.sequenceiq.cloudbreak.core.CloudbreakException;
import com.sequenceiq.cloudbreak.core.CloudbreakImageNotFoundException;
import com.sequenceiq.cloudbreak.core.CloudbreakSecuritySetupException;
import com.sequenceiq.cloudbreak.core.bootstrap.service.container.ContainerOrchestratorResolver;
import com.sequenceiq.cloudbreak.core.flow2.service.ReactorFlowManager;
import com.sequenceiq.cloudbreak.domain.Blueprint;
import com.sequenceiq.cloudbreak.domain.CbUser;
import com.sequenceiq.cloudbreak.domain.Cluster;
import com.sequenceiq.cloudbreak.domain.Component;
import com.sequenceiq.cloudbreak.domain.HostGroup;
import com.sequenceiq.cloudbreak.domain.InstanceGroup;
import com.sequenceiq.cloudbreak.domain.InstanceMetaData;
import com.sequenceiq.cloudbreak.domain.Orchestrator;
import com.sequenceiq.cloudbreak.domain.SecurityConfig;
import com.sequenceiq.cloudbreak.domain.Stack;
import com.sequenceiq.cloudbreak.domain.StackValidation;
import com.sequenceiq.cloudbreak.domain.StopRestrictionReason;
import com.sequenceiq.cloudbreak.domain.json.Json;
import com.sequenceiq.cloudbreak.logger.MDCBuilder;
import com.sequenceiq.cloudbreak.orchestrator.container.ContainerOrchestrator;
import com.sequenceiq.cloudbreak.orchestrator.exception.CloudbreakOrchestratorException;
import com.sequenceiq.cloudbreak.orchestrator.model.OrchestrationCredential;
import com.sequenceiq.cloudbreak.repository.ClusterRepository;
import com.sequenceiq.cloudbreak.repository.InstanceGroupRepository;
import com.sequenceiq.cloudbreak.repository.InstanceMetaDataRepository;
import com.sequenceiq.cloudbreak.repository.OrchestratorRepository;
import com.sequenceiq.cloudbreak.repository.SecurityConfigRepository;
import com.sequenceiq.cloudbreak.repository.SecurityRuleRepository;
import com.sequenceiq.cloudbreak.repository.StackRepository;
import com.sequenceiq.cloudbreak.repository.StackUpdater;
import com.sequenceiq.cloudbreak.service.ComponentConfigProvider;
import com.sequenceiq.cloudbreak.service.DuplicateKeyValueException;
import com.sequenceiq.cloudbreak.service.TlsSecurityService;
import com.sequenceiq.cloudbreak.service.events.CloudbreakEventService;
import com.sequenceiq.cloudbreak.service.image.ImageNameUtil;
import com.sequenceiq.cloudbreak.service.image.ImageService;
import com.sequenceiq.cloudbreak.service.messages.CloudbreakMessagesService;
import com.sequenceiq.cloudbreak.service.stack.connector.adapter.ServiceProviderConnectorAdapter;
import com.sequenceiq.cloudbreak.service.stack.flow.TerminationService;
import com.sequenceiq.cloudbreak.util.PasswordUtil;
@Service
@Transactional
public class StackService {
private static final Logger LOGGER = LoggerFactory.getLogger(StackService.class);
@Inject
private StackRepository stackRepository;
@Inject
private StackUpdater stackUpdater;
@Inject
private ImageService imageService;
@Inject
private ClusterRepository clusterRepository;
@Inject
private InstanceMetaDataRepository instanceMetaDataRepository;
@Inject
private InstanceGroupRepository instanceGroupRepository;
@Inject
private OrchestratorRepository orchestratorRepository;
@Inject
private TlsSecurityService tlsSecurityService;
@Inject
private TerminationService terminationService;
@Inject
private ReactorFlowManager flowManager;
@Inject
private BlueprintValidator blueprintValidator;
@Inject
private NetworkConfigurationValidator networkConfigurationValidator;
@Inject
private SecurityRuleRepository securityRuleRepository;
@Inject
private CloudbreakEventService eventService;
@Inject
private CloudbreakMessagesService cloudbreakMessagesService;
@Inject
private ServiceProviderConnectorAdapter connector;
@Inject
private ImageNameUtil imageNameUtil;
@Inject
private ContainerOrchestratorResolver containerOrchestratorResolver;
@Inject
private ComponentConfigProvider componentConfigProvider;
@Inject
private SecurityConfigRepository securityConfigRepository;
@Value("${cb.nginx.port:9443}")
private int nginxPort;
@Value("${info.app.version:}")
private String cbVersion;
@Autowired
@Qualifier("conversionService")
private ConversionService conversionService;
public Set<StackResponse> retrievePrivateStacks(CbUser user) {
return convertStacks(stackRepository.findForUser(user.getUserId()));
}
public Set<StackResponse> retrieveAccountStacks(CbUser user) {
if (user.getRoles().contains(CbUserRole.ADMIN)) {
return convertStacks(stackRepository.findAllInAccount(user.getAccount()));
} else {
return convertStacks(stackRepository.findPublicInAccountForUser(user.getUserId(), user.getAccount()));
}
}
public Set<Stack> retrieveAccountStacks(String account) {
return stackRepository.findAllInAccount(account);
}
public Set<Stack> retrieveOwnerStacks(String owner) {
return stackRepository.findForUser(owner);
}
public StackResponse getJsonById(Long id) {
Stack stack = get(id);
return conversionService.convert(stack, StackResponse.class);
}
@PostAuthorize("hasPermission(returnObject,'read')")
public Stack get(Long id) {
Stack stack = stackRepository.findOne(id);
if (stack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", id));
}
return stack;
}
@PreAuthorize("#oauth2.hasScope('cloudbreak.autoscale')")
public Stack getForAutoscale(Long id) {
Stack stack = stackRepository.findOne(id);
if (stack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", id));
}
return stack;
}
@PreAuthorize("#oauth2.hasScope('cloudbreak.autoscale')")
public Set<AutoscaleStackResponse> getAllForAutoscale() {
Set<Stack> aliveOnes = stackRepository.findAliveOnes();
return convertStacksForAutoscale(aliveOnes);
}
public Stack findLazy(Long id) {
Stack stack = stackRepository.findByIdLazy(id);
if (stack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", id));
}
return stack;
}
public Stack getById(Long id) {
Stack retStack = stackRepository.findOneWithLists(id);
if (retStack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", id));
}
return retStack;
}
public StackResponse getByIdJson(Long id) {
Stack retStack = stackRepository.findOneWithLists(id);
if (retStack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", id));
}
return conversionService.convert(retStack, StackResponse.class);
}
public StackResponse get(String ambariAddress) {
Stack stack = stackRepository.findByAmbari(ambariAddress);
if (stack == null) {
throw new NotFoundException(String.format("Stack not found by Ambari address: '%s' not found", ambariAddress));
}
return conversionService.convert(stack, StackResponse.class);
}
public Stack getPrivateStack(String name, CbUser cbUser) {
Stack stack = stackRepository.findByNameInUser(name, cbUser.getUserId());
if (stack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", name));
}
return stack;
}
public StackResponse getPrivateStackJson(String name, CbUser cbUser) {
Stack stack = getPrivateStack(name, cbUser);
if (stack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", name));
}
return conversionService.convert(stack, StackResponse.class);
}
public StackResponse getPrivateStackJsonByName(String name, CbUser cbUser) {
Stack stack = stackRepository.findByNameInUser(name, cbUser.getUserId());
if (stack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", name));
}
return conversionService.convert(stack, StackResponse.class);
}
@PostAuthorize("hasPermission(returnObject,'read')")
public StackResponse getPublicStackJsonByName(String name, CbUser cbUser) {
Stack stack = stackRepository.findOneByName(name, cbUser.getAccount());
if (stack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", name));
}
return conversionService.convert(stack, StackResponse.class);
}
@PostAuthorize("hasPermission(returnObject,'read')")
public Stack getPublicStack(String name, CbUser cbUser) {
Stack stack = stackRepository.findOneByName(name, cbUser.getAccount());
if (stack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", name));
}
return stack;
}
public void delete(String name, CbUser user, Boolean deleteDependencies) {
Stack stack = stackRepository.findByNameInAccount(name, user.getAccount(), user.getUserId());
if (stack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", name));
}
delete(stack, user, deleteDependencies);
}
public void forceDelete(String name, CbUser user, Boolean deleteDependencies) {
Stack stack = stackRepository.findByNameInAccount(name, user.getAccount(), user.getUserId());
if (stack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", name));
}
forceDelete(stack, user, deleteDependencies);
}
@Transactional(Transactional.TxType.NEVER)
public Stack create(CbUser user, Stack stack, String ambariVersion, String hdpVersion, String imageCatalog, Optional<String> customImage) {
Stack savedStack;
stack.setOwner(user.getUserId());
stack.setAccount(user.getAccount());
stack.setGatewayPort(nginxPort);
setPlatformVariant(stack);
MDCBuilder.buildMdcContext(stack);
try {
if (stack.getOrchestrator() != null) {
orchestratorRepository.save(stack.getOrchestrator());
}
String template = connector.getTemplate(stack);
savedStack = stackRepository.save(stack);
addTemplateForStack(stack, template);
MDCBuilder.buildMdcContext(savedStack);
instanceGroupRepository.save(savedStack.getInstanceGroups());
if (!BYOS.equals(savedStack.cloudPlatform())) {
tlsSecurityService.copyClientKeys(stack.getId());
SecurityConfig securityConfig = tlsSecurityService.storeSSHKeys(stack.getId());
securityConfig.setSaltPassword(PasswordUtil.generatePassword());
securityConfig.setSaltBootPassword(PasswordUtil.generatePassword());
securityConfig.setKnoxMasterSecret(PasswordUtil.generatePassword());
securityConfig.setStack(stack);
securityConfigRepository.save(securityConfig);
savedStack.setSecurityConfig(securityConfig);
imageService.create(savedStack, connector.getPlatformParameters(stack), ambariVersion, hdpVersion, imageCatalog, customImage);
flowManager.triggerProvisioning(savedStack.getId());
} else {
savedStack = stackUpdater.updateStackStatus(stack.getId(), DetailedStackStatus.PROVISIONED);
savedStack.setCreated(new Date().getTime());
save(savedStack);
savedStack = stackRepository.findById(savedStack.getId());
}
addCloudbreakDetailsForStack(savedStack);
} catch (DataIntegrityViolationException ex) {
throw new DuplicateKeyValueException(APIResourceType.STACK, stack.getName(), ex);
} catch (CloudbreakSecuritySetupException e) {
LOGGER.error("Storing of security credentials failed", e);
throw new CloudbreakApiException("Storing security credentials failed", e);
} catch (CloudbreakImageNotFoundException e) {
LOGGER.error("Cloudbreak Image not found", e);
throw new CloudbreakApiException(e.getMessage(), e);
}
return savedStack;
}
private void setPlatformVariant(Stack stack) {
stack.setPlatformVariant(connector.checkAndGetPlatformVariant(stack).value());
}
public void delete(Long id, CbUser user, Boolean deleteDependencies) {
Stack stack = stackRepository.findByIdInAccount(id, user.getAccount());
if (stack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", id));
}
delete(stack, user, deleteDependencies);
}
public void forceDelete(Long id, CbUser user, Boolean deleteDependencies) {
Stack stack = stackRepository.findByIdInAccount(id, user.getAccount());
if (stack == null) {
throw new NotFoundException(String.format("Stack '%s' not found", id));
}
forceDelete(stack, user, deleteDependencies);
}
public void removeInstance(CbUser user, Long stackId, String instanceId) {
Stack stack = get(stackId);
InstanceMetaData instanceMetaData = instanceMetaDataRepository.findByInstanceId(stackId, instanceId);
if (instanceMetaData == null) {
throw new NotFoundException(String.format("Metadata for instance %s not found.", instanceId));
}
if (!stack.isPublicInAccount() && !stack.getOwner().equals(user.getUserId())) {
throw new BadRequestException(String.format("Private stack (%s) only modifiable by the owner.", stackId));
}
flowManager.triggerStackRemoveInstance(stackId, instanceId);
}
@Transactional(Transactional.TxType.NEVER)
public void updateStatus(Long stackId, StatusRequest status) {
Stack stack = getById(stackId);
Cluster cluster = null;
if (stack.getCluster() != null) {
cluster = clusterRepository.findOneWithLists(stack.getCluster().getId());
}
if (BYOS.equals(stack.cloudPlatform()) && status.equals(FULL_SYNC)) {
stackUpdater.updateStackStatus(stack.getId(), DetailedStackStatus.PROVISIONED);
return;
} else if (BYOS.equals(stack.cloudPlatform())) {
LOGGER.warn("The status of a 'Bring your own stack' type of infrastructure cannot be changed.");
return;
}
switch (status) {
case SYNC:
sync(stack, status, false);
break;
case FULL_SYNC:
sync(stack, status, true);
break;
case REPAIR_FAILED_NODES:
repairFailedNodes(stack);
break;
case STOPPED:
stop(stack, cluster, status);
break;
case STARTED:
start(stack, cluster, status);
break;
default:
throw new BadRequestException("Cannot update the status of stack because status request not valid.");
}
}
private Set<StackResponse> convertStacks(Set<Stack> stacks) {
return (Set<StackResponse>) conversionService.convert(stacks, TypeDescriptor.forObject(stacks),
TypeDescriptor.collection(Set.class, TypeDescriptor.valueOf(StackResponse.class)));
}
private Set<AutoscaleStackResponse> convertStacksForAutoscale(Set<Stack> stacks) {
return (Set<AutoscaleStackResponse>) conversionService.convert(stacks, TypeDescriptor.forObject(stacks),
TypeDescriptor.collection(Set.class, TypeDescriptor.valueOf(AutoscaleStackResponse.class)));
}
private void repairFailedNodes(Stack stack) {
LOGGER.warn("Received request to replace failed nodes: " + stack.getId());
flowManager.triggerManualRepairFlow(stack.getId());
}
private void sync(Stack stack, StatusRequest statusRequest, boolean full) {
if (!stack.isDeleteInProgress() && !stack.isStackInDeletionPhase() && !stack.isModificationInProgress()) {
if (full) {
flowManager.triggerFullSync(stack.getId());
} else {
flowManager.triggerStackSync(stack.getId());
}
} else {
LOGGER.warn("Stack could not be synchronized in {} state!", stack.getStatus());
}
}
private void stop(Stack stack, Cluster cluster, StatusRequest statusRequest) {
if (cluster != null && cluster.isStopInProgress()) {
stackUpdater.updateStackStatus(stack.getId(), DetailedStackStatus.STOP_REQUESTED, "Stopping of cluster infrastructure has been requested.");
String message = cloudbreakMessagesService.getMessage(Msg.STACK_STOP_REQUESTED.code());
eventService.fireCloudbreakEvent(stack.getId(), STOP_REQUESTED.name(), message);
} else {
StopRestrictionReason reason = stack.isInfrastructureStoppable();
if (stack.isStopped()) {
String statusDesc = cloudbreakMessagesService.getMessage(Msg.STACK_STOP_IGNORED.code());
LOGGER.info(statusDesc);
eventService.fireCloudbreakEvent(stack.getId(), STOPPED.name(), statusDesc);
} else if (reason != StopRestrictionReason.NONE) {
throw new BadRequestException(
String.format("Cannot stop a stack '%s'. Reason: %s", stack.getId(), reason.getReason()));
} else if (!stack.isAvailable() && !stack.isStopFailed()) {
throw new BadRequestException(
String.format("Cannot update the status of stack '%s' to STOPPED, because it isn't in AVAILABLE state.", stack.getId()));
} else if ((cluster != null && !cluster.isStopped()) && !stack.isStopFailed()) {
throw new BadRequestException(
String.format("Cannot update the status of stack '%s' to STOPPED, because the cluster is not in STOPPED state.", stack.getId()));
} else {
stackUpdater.updateStackStatus(stack.getId(), DetailedStackStatus.STOP_REQUESTED);
flowManager.triggerStackStop(stack.getId());
}
}
}
private void start(Stack stack, Cluster cluster, StatusRequest statusRequest) {
if (stack.isAvailable()) {
String statusDesc = cloudbreakMessagesService.getMessage(Msg.STACK_START_IGNORED.code());
LOGGER.info(statusDesc);
eventService.fireCloudbreakEvent(stack.getId(), AVAILABLE.name(), statusDesc);
} else if ((!stack.isStopped() || (cluster != null && !cluster.isStopped())) && !stack.isStartFailed()) {
throw new BadRequestException(
String.format("Cannot update the status of stack '%s' to STARTED, because it isn't in STOPPED state.", stack.getId()));
} else if (stack.isStopped() || stack.isStartFailed()) {
stackUpdater.updateStackStatus(stack.getId(), DetailedStackStatus.START_REQUESTED);
flowManager.triggerStackStart(stack.getId());
}
}
public void updateNodeCount(Long stackId, InstanceGroupAdjustmentJson instanceGroupAdjustmentJson) {
Stack stack = get(stackId);
validateStackStatus(stack);
validateInstanceGroup(stack, instanceGroupAdjustmentJson.getInstanceGroup());
validateScalingAdjustment(instanceGroupAdjustmentJson, stack);
if (instanceGroupAdjustmentJson.getWithClusterEvent()) {
validateHostGroupAdjustment(instanceGroupAdjustmentJson, stack, instanceGroupAdjustmentJson.getScalingAdjustment());
}
if (instanceGroupAdjustmentJson.getScalingAdjustment() > 0) {
stackUpdater.updateStackStatus(stackId, DetailedStackStatus.UPSCALE_REQUESTED);
flowManager.triggerStackUpscale(stack.getId(), instanceGroupAdjustmentJson);
} else {
stackUpdater.updateStackStatus(stackId, DetailedStackStatus.DOWNSCALE_REQUESTED);
flowManager.triggerStackDownscale(stack.getId(), instanceGroupAdjustmentJson);
}
}
public InstanceMetaData updateMetaDataStatus(Long id, String hostName, InstanceStatus status) {
InstanceMetaData metaData = instanceMetaDataRepository.findHostInStack(id, hostName);
if (metaData == null) {
throw new NotFoundException(String.format("Metadata not found on stack:'%s' with hostname: '%s'.", id, hostName));
}
metaData.setInstanceStatus(status);
return instanceMetaDataRepository.save(metaData);
}
public void validateStack(StackValidation stackValidation) {
if (stackValidation.getNetwork() != null) {
networkConfigurationValidator.validateNetworkForStack(stackValidation.getNetwork(), stackValidation.getInstanceGroups());
}
blueprintValidator.validateBlueprintForStack(stackValidation.getBlueprint(), stackValidation.getHostGroups(), stackValidation.getInstanceGroups());
}
public void validateOrchestrator(Orchestrator orchestrator) {
try {
ContainerOrchestrator containerOrchestrator = containerOrchestratorResolver.get(orchestrator.getType());
if (containerOrchestrator != null) {
containerOrchestrator.validateApiEndpoint(new OrchestrationCredential(orchestrator.getApiEndpoint(), orchestrator.getAttributes().getMap()));
}
} catch (CloudbreakException e) {
throw new BadRequestException(String.format("Invalid orchestrator type: %s", e.getMessage()));
} catch (CloudbreakOrchestratorException e) {
throw new BadRequestException(String.format("Error occurred when trying to reach orchestrator API: %s", e.getMessage()));
}
}
@Transactional(Transactional.TxType.NEVER)
public Stack save(Stack stack) {
return stackRepository.save(stack);
}
public List<Stack> getAllAlive() {
return stackRepository.findAllAlive();
}
private void validateScalingAdjustment(InstanceGroupAdjustmentJson instanceGroupAdjustmentJson, Stack stack) {
if (0 == instanceGroupAdjustmentJson.getScalingAdjustment()) {
throw new BadRequestException(String.format("Requested scaling adjustment on stack '%s' is 0. Nothing to do.", stack.getId()));
}
if (0 > instanceGroupAdjustmentJson.getScalingAdjustment()) {
InstanceGroup instanceGroup = stack.getInstanceGroupByInstanceGroupName(instanceGroupAdjustmentJson.getInstanceGroup());
if (-1 * instanceGroupAdjustmentJson.getScalingAdjustment() > instanceGroup.getNodeCount()) {
throw new BadRequestException(String.format("There are %s instances in instance group '%s'. Cannot remove %s instances.",
instanceGroup.getNodeCount(), instanceGroup.getGroupName(),
-1 * instanceGroupAdjustmentJson.getScalingAdjustment()));
}
int removableHosts = instanceMetaDataRepository.findRemovableInstances(stack.getId(), instanceGroupAdjustmentJson.getInstanceGroup()).size();
if (removableHosts < -1 * instanceGroupAdjustmentJson.getScalingAdjustment()) {
throw new BadRequestException(
String.format("There are %s unregistered instances in instance group '%s' but %s were requested. Decommission nodes from the cluster!",
removableHosts, instanceGroup.getGroupName(), instanceGroupAdjustmentJson.getScalingAdjustment() * -1));
}
}
}
private void validateHostGroupAdjustment(final InstanceGroupAdjustmentJson instanceGroupAdjustmentJson, Stack stack, Integer adjustment) {
Blueprint blueprint = stack.getCluster().getBlueprint();
HostGroup hostGroup = stack.getCluster().getHostGroups().stream().filter(input -> {
return input.getConstraint().getInstanceGroup().getGroupName().equals(instanceGroupAdjustmentJson.getInstanceGroup());
}).findFirst().get();
if (hostGroup == null) {
throw new BadRequestException(String.format("Instancegroup '%s' not found or not part of stack '%s'",
instanceGroupAdjustmentJson.getInstanceGroup(), stack.getName()));
}
blueprintValidator.validateHostGroupScalingRequest(blueprint, hostGroup, adjustment);
}
private void validateStackStatus(Stack stack) {
if (!stack.isAvailable()) {
throw new BadRequestException(String.format("Stack '%s' is currently in '%s' state. Node count can only be updated if it's running.", stack.getId(),
stack.getStatus()));
}
}
private void validateInstanceGroup(Stack stack, String instanceGroupName) {
InstanceGroup instanceGroup = stack.getInstanceGroupByInstanceGroupName(instanceGroupName);
if (instanceGroup == null) {
throw new BadRequestException(String.format("Stack '%s' does not have an instanceGroup named '%s'.", stack.getId(), instanceGroupName));
}
if (isGateway(instanceGroup.getInstanceGroupType())) {
throw new BadRequestException("The Ambari server instance group modification is not enabled.");
}
}
private void delete(Stack stack, CbUser user, Boolean deleteDependencies) {
LOGGER.info("Stack delete requested.");
if (!user.getUserId().equals(stack.getOwner()) && !user.getRoles().contains(CbUserRole.ADMIN)) {
throw new BadRequestException("Stacks can be deleted only by account admins or owners.");
}
if (!stack.isDeleteCompleted()) {
flowManager.triggerTermination(stack.getId(), deleteDependencies);
} else {
LOGGER.info("Stack is already deleted.");
}
}
private void forceDelete(Stack stack, CbUser user, Boolean deleteDependencies) {
LOGGER.info("Stack forced delete requested.");
if (!user.getUserId().equals(stack.getOwner()) && !user.getRoles().contains(CbUserRole.ADMIN)) {
throw new BadRequestException("Stacks can be force deleted only by account admins or owners.");
}
if (!stack.isDeleteCompleted()) {
flowManager.triggerForcedTermination(stack.getId(), deleteDependencies);
} else {
LOGGER.info("Stack is already deleted.");
}
}
private enum Msg {
STACK_STOP_IGNORED("stack.stop.ignored"),
STACK_START_IGNORED("stack.start.ignored"),
STACK_STOP_REQUESTED("stack.stop.requested");
private String code;
Msg(String msgCode) {
code = msgCode;
}
public String code() {
return code;
}
}
private void addTemplateForStack(Stack stack, String template) {
StackTemplate stackTemplate = new StackTemplate(template, cbVersion);
try {
Component stackTemplateComponent = new Component(ComponentType.STACK_TEMPLATE, ComponentType.STACK_TEMPLATE.name(), new Json(stackTemplate), stack);
componentConfigProvider.store(stackTemplateComponent);
} catch (JsonProcessingException e) {
LOGGER.error("Could not create Cloudbreak details component.", e);
}
}
private void addCloudbreakDetailsForStack(Stack stack) {
CloudbreakDetails cbDetails = new CloudbreakDetails(cbVersion);
try {
Component cbDetailsComponent = new Component(ComponentType.CLOUDBREAK_DETAILS, ComponentType.CLOUDBREAK_DETAILS.name(), new Json(cbDetails), stack);
componentConfigProvider.store(cbDetailsComponent);
} catch (JsonProcessingException e) {
LOGGER.error("Could not create Cloudbreak details component.", e);
}
}
}