package com.sequenceiq.cloudbreak.service.cluster.flow;
import static com.sequenceiq.cloudbreak.api.model.Status.DELETE_COMPLETED;
import static com.sequenceiq.cloudbreak.util.JsonUtil.readValue;
import static com.sequenceiq.cloudbreak.util.JsonUtil.writeValueAsString;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.transaction.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.sequenceiq.cloudbreak.api.model.FileSystemConfiguration;
import com.sequenceiq.cloudbreak.api.model.FileSystemType;
import com.sequenceiq.cloudbreak.core.CloudbreakException;
import com.sequenceiq.cloudbreak.core.bootstrap.service.container.ContainerOrchestratorResolver;
import com.sequenceiq.cloudbreak.domain.Cluster;
import com.sequenceiq.cloudbreak.domain.Constraint;
import com.sequenceiq.cloudbreak.domain.Container;
import com.sequenceiq.cloudbreak.domain.FileSystem;
import com.sequenceiq.cloudbreak.domain.HostGroup;
import com.sequenceiq.cloudbreak.domain.Orchestrator;
import com.sequenceiq.cloudbreak.orchestrator.container.ContainerOrchestrator;
import com.sequenceiq.cloudbreak.orchestrator.exception.CloudbreakOrchestratorException;
import com.sequenceiq.cloudbreak.orchestrator.model.ContainerInfo;
import com.sequenceiq.cloudbreak.orchestrator.model.OrchestrationCredential;
import com.sequenceiq.cloudbreak.repository.ClusterRepository;
import com.sequenceiq.cloudbreak.repository.ConstraintRepository;
import com.sequenceiq.cloudbreak.repository.ContainerRepository;
import com.sequenceiq.cloudbreak.repository.HostGroupRepository;
import com.sequenceiq.cloudbreak.repository.HostMetadataRepository;
import com.sequenceiq.cloudbreak.service.ComponentConfigProvider;
import com.sequenceiq.cloudbreak.service.TlsSecurityService;
import com.sequenceiq.cloudbreak.service.cluster.flow.filesystem.FileSystemConfigurator;
import com.sequenceiq.cloudbreak.service.stack.flow.TerminationFailedException;
@Component
@Transactional
public class ClusterTerminationService {
private static final Logger LOGGER = LoggerFactory.getLogger(ClusterTerminationService.class);
private static final String DELIMITER = "_";
@Inject
private ClusterRepository clusterRepository;
@Inject
private HostGroupRepository hostGroupRepository;
@Resource
private Map<FileSystemType, FileSystemConfigurator> fileSystemConfigurators;
@Inject
private HostMetadataRepository hostMetadataRepository;
@Inject
private ConstraintRepository constraintRepository;
@Inject
private ContainerRepository containerRepository;
@Inject
private ContainerOrchestratorResolver containerOrchestratorResolver;
@Inject
private TlsSecurityService tlsSecurityService;
@Inject
private ComponentConfigProvider componentConfigProvider;
public Boolean deleteClusterContainers(Long clusterId) {
Cluster cluster = clusterRepository.findById(clusterId);
if (cluster == null) {
LOGGER.warn("Failed to delete containers of cluster (id:'{}'), because the cluster could not be found in the database.", clusterId);
return Boolean.TRUE;
}
return deleteClusterContainers(cluster);
}
public Boolean deleteClusterContainers(Cluster cluster) {
try {
Orchestrator orchestrator = cluster.getStack().getOrchestrator();
ContainerOrchestrator containerOrchestrator = containerOrchestratorResolver.get(orchestrator.getType());
try {
Map<String, Object> map = new HashMap<>();
map.putAll(orchestrator.getAttributes().getMap());
map.put("certificateDir", tlsSecurityService.prepareCertDir(cluster.getStack().getId()));
OrchestrationCredential credential = new OrchestrationCredential(orchestrator.getApiEndpoint(), map);
Set<Container> containers = containerRepository.findContainersInCluster(cluster.getId());
List<ContainerInfo> containerInfo = containers.stream()
.map(c -> new ContainerInfo(c.getContainerId(), c.getName(), c.getHost(), c.getImage())).collect(Collectors.toList());
containerOrchestrator.deleteContainer(containerInfo, credential);
containerRepository.delete(containers);
deleteClusterHostGroupsWithItsMetadata(cluster);
} catch (CloudbreakException | CloudbreakOrchestratorException e) {
throw new TerminationFailedException(String.format("Failed to delete containers of cluster (id:'%s',name:'%s').",
cluster.getId(), cluster.getName()), e);
}
return Boolean.TRUE;
} catch (CloudbreakException e) {
return Boolean.FALSE;
}
}
public void finalizeClusterTermination(Long clusterId) {
Cluster cluster = clusterRepository.findById(clusterId);
Long stackId = cluster.getStack().getId();
String terminatedName = cluster.getName() + DELIMITER + new Date().getTime();
cluster.setName(terminatedName);
FileSystem fs = cluster.getFileSystem();
if (fs != null) {
deleteFileSystemResources(stackId, fs);
}
cluster.setBlueprint(null);
cluster.setStack(null);
cluster.setSssdConfig(null);
cluster.setRdsConfigs(new HashSet<>());
cluster.setStatus(DELETE_COMPLETED);
deleteClusterHostGroupsWithItsMetadata(cluster);
componentConfigProvider.deleteComponentsForStack(stackId);
}
private void deleteClusterHostGroupsWithItsMetadata(Cluster cluster) {
Set<HostGroup> hostGroups = hostGroupRepository.findHostGroupsInCluster(cluster.getId());
List<Constraint> constraintsToDelete = new LinkedList<>();
for (HostGroup hg : hostGroups) {
hg.getRecipes().clear();
Constraint constraint = hg.getConstraint();
if (constraint != null) {
constraintsToDelete.add(constraint);
}
}
hostGroupRepository.delete(hostGroups);
constraintRepository.delete(constraintsToDelete);
cluster.getHostGroups().clear();
cluster.getContainers().clear();
clusterRepository.save(cluster);
}
private Map<String, String> deleteFileSystemResources(Long stackId, FileSystem fileSystem) {
try {
FileSystemConfigurator fsConfigurator = fileSystemConfigurators.get(FileSystemType.valueOf(fileSystem.getType()));
String json = writeValueAsString(fileSystem.getProperties());
FileSystemConfiguration fsConfiguration = (FileSystemConfiguration) readValue(json, FileSystemType.valueOf(fileSystem.getType()).getClazz());
fsConfiguration.addProperty(FileSystemConfiguration.STORAGE_CONTAINER, "cloudbreak" + stackId);
return fsConfigurator.deleteResources(fsConfiguration);
} catch (IOException e) {
throw new TerminationFailedException("File system resources could not be deleted: ", e);
}
}
}