package com.sequenceiq.cloudbreak.init; import static com.sequenceiq.cloudbreak.api.model.Status.AVAILABLE; import static com.sequenceiq.cloudbreak.api.model.Status.START_IN_PROGRESS; import static com.sequenceiq.cloudbreak.api.model.Status.STOP_IN_PROGRESS; import static com.sequenceiq.cloudbreak.api.model.Status.UPDATE_IN_PROGRESS; import static com.sequenceiq.cloudbreak.api.model.Status.UPDATE_REQUESTED; import static com.sequenceiq.cloudbreak.api.model.Status.WAIT_FOR_SYNC; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; import com.sequenceiq.cloudbreak.api.model.DetailedStackStatus; import com.sequenceiq.cloudbreak.api.model.InstanceStatus; import com.sequenceiq.cloudbreak.api.model.Status; import com.sequenceiq.cloudbreak.core.flow2.Flow2Handler; import com.sequenceiq.cloudbreak.core.flow2.service.ReactorFlowManager; import com.sequenceiq.cloudbreak.domain.Cluster; import com.sequenceiq.cloudbreak.domain.InstanceMetaData; import com.sequenceiq.cloudbreak.domain.Stack; import com.sequenceiq.cloudbreak.repository.ClusterRepository; import com.sequenceiq.cloudbreak.repository.FlowLogRepository; import com.sequenceiq.cloudbreak.repository.InstanceMetaDataRepository; import com.sequenceiq.cloudbreak.repository.StackRepository; import com.sequenceiq.cloudbreak.repository.StackUpdater; import com.sequenceiq.cloudbreak.service.events.CloudbreakEventService; import com.sequenceiq.cloudbreak.service.flowlog.FlowLogService; import com.sequenceiq.cloudbreak.service.usages.UsageService; @Component public class CloudbreakCleanupService implements ApplicationListener<ContextRefreshedEvent> { private static final Logger LOGGER = LoggerFactory.getLogger(CloudbreakCleanupService.class); @Inject private StackRepository stackRepository; @Inject private StackUpdater stackUpdater; @Inject private ClusterRepository clusterRepository; @Inject private InstanceMetaDataRepository instanceMetaDataRepository; @Inject private CloudbreakEventService eventService; @Inject private FlowLogRepository flowLogRepository; @Inject private FlowLogService flowLogService; @Inject private ReactorFlowManager flowManager; @Inject private UsageService usageService; @Inject private Flow2Handler flow2Handler; public void onApplicationEvent(ContextRefreshedEvent event) { List<Long> stackIdsUnderOperation = restartDistruptedFlows(); usageService.fixUsages(); List<Stack> stacksToSync = resetStackStatus(stackIdsUnderOperation); List<Cluster> clustersToSync = resetClusterStatus(stacksToSync, stackIdsUnderOperation); triggerSyncs(stacksToSync, clustersToSync); } private List<Stack> resetStackStatus(List<Long> excludeStackIds) { return stackRepository.findByStatuses(Arrays.asList(UPDATE_REQUESTED, UPDATE_IN_PROGRESS, WAIT_FOR_SYNC, START_IN_PROGRESS, STOP_IN_PROGRESS, AVAILABLE)) .stream().filter(s -> !excludeStackIds.contains(s.getId()) || WAIT_FOR_SYNC.equals(s.getStatus())) .map(s -> { if (!WAIT_FOR_SYNC.equals(s.getStatus())) { loggingStatusChange("Stack", s.getId(), s.getStatus(), WAIT_FOR_SYNC); stackUpdater.updateStackStatus(s.getId(), DetailedStackStatus.WAIT_FOR_SYNC, s.getStatusReason()); } cleanInstanceMetaData(instanceMetaDataRepository.findAllInStack(s.getId())); return s; }).collect(Collectors.toList()); } private void cleanInstanceMetaData(Set<InstanceMetaData> metadataSet) { for (InstanceMetaData metadata : metadataSet) { if (InstanceStatus.REQUESTED.equals(metadata.getInstanceStatus()) && metadata.getInstanceId() == null) { LOGGER.info("InstanceMetaData [privateId: '{}'] is deleted at CB start.", metadata.getPrivateId()); instanceMetaDataRepository.delete(metadata); } } } private List<Cluster> resetClusterStatus(List<Stack> stacksToSync, List<Long> excludeStackIds) { return clusterRepository.findByStatuses(Arrays.asList(UPDATE_REQUESTED, UPDATE_IN_PROGRESS, WAIT_FOR_SYNC, START_IN_PROGRESS, STOP_IN_PROGRESS)) .stream().filter(c -> !excludeStackIds.contains(c.getStack().getId())) .map(c -> { loggingStatusChange("Cluster", c.getId(), c.getStatus(), WAIT_FOR_SYNC); c.setStatus(WAIT_FOR_SYNC); clusterRepository.save(c); return c; }).filter(c -> !stackToSyncContainsCluster(stacksToSync, c)).collect(Collectors.toList()); } private boolean stackToSyncContainsCluster(List<Stack> stacksToSync, Cluster cluster) { Set<Long> stackIds = stacksToSync.stream().map(Stack::getId).collect(Collectors.toSet()); return stackIds.contains(cluster.getStack().getId()); } private List<Long> restartDistruptedFlows() { List<Long> stackIds = new ArrayList<>(); List<Object[]> runningFlows = flowLogRepository.findAllNonFinalized(); String logMessage = "Restarting flow {}"; for (Object[] flow : runningFlows) { LOGGER.info(logMessage, flow[0]); try { flow2Handler.restartFlow((String) flow[0]); stackIds.add((Long) flow[1]); } catch (Exception e) { LOGGER.error(String.format("Failed torestart flow %s on stack %s", flow[0].toString(), flow[1].toString()), e); flowLogService.terminate((Long) flow[1], (String) flow[0]); } } return stackIds; } private void loggingStatusChange(String type, Long id, Status status, Status deleteFailed) { LOGGER.info("{} {} status is updated from {} to {} at CB start.", type, id, status, deleteFailed); } private void triggerSyncs(List<Stack> stacksToSync, List<Cluster> clustersToSync) { for (Stack stack : stacksToSync) { LOGGER.info("Triggering full sync on stack [name: {}, id: {}].", stack.getName(), stack.getId()); fireEvent(stack); flowManager.triggerFullSync(stack.getId()); } for (Cluster cluster : clustersToSync) { Stack stack = cluster.getStack(); LOGGER.info("Triggering sync on cluster [name: {}, id: {}].", cluster.getName(), cluster.getId()); fireEvent(stack); flowManager.triggerClusterSync(stack.getId()); } } private void fireEvent(Stack stack) { eventService.fireCloudbreakEvent(stack.getId(), UPDATE_IN_PROGRESS.name(), "Couldn't retrieve the cluster's status, starting to sync."); } }