package com.sequenceiq.cloudbreak.core.bootstrap.service.container; import static com.sequenceiq.cloudbreak.common.type.OrchestratorConstants.SWARM; import static com.sequenceiq.cloudbreak.core.bootstrap.service.ClusterDeletionBasedExitCriteriaModel.clusterDeletionBasedExitCriteriaModel; import static com.sequenceiq.cloudbreak.orchestrator.container.DockerContainer.AMBARI_AGENT; import static com.sequenceiq.cloudbreak.orchestrator.container.DockerContainer.AMBARI_DB; import static com.sequenceiq.cloudbreak.orchestrator.container.DockerContainer.AMBARI_SERVER; import static com.sequenceiq.cloudbreak.orchestrator.container.DockerContainer.CONSUL_WATCH; import static com.sequenceiq.cloudbreak.orchestrator.container.DockerContainer.HAVEGED; import static com.sequenceiq.cloudbreak.orchestrator.container.DockerContainer.KERBEROS; import static com.sequenceiq.cloudbreak.orchestrator.container.DockerContainer.LDAP; import static com.sequenceiq.cloudbreak.orchestrator.container.DockerContainer.LOGROTATE; import static com.sequenceiq.cloudbreak.orchestrator.container.DockerContainer.REGISTRATOR; import static com.sequenceiq.cloudbreak.orchestrator.container.DockerContainer.SHIPYARD; import static com.sequenceiq.cloudbreak.orchestrator.container.DockerContainer.SHIPYARD_DB; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import javax.inject.Inject; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.core.convert.ConversionService; import org.springframework.stereotype.Component; import com.sequenceiq.cloudbreak.cloud.scheduler.CancellationException; import com.sequenceiq.cloudbreak.core.CloudbreakException; import com.sequenceiq.cloudbreak.core.bootstrap.service.ContainerConfigService; import com.sequenceiq.cloudbreak.domain.Cluster; import com.sequenceiq.cloudbreak.domain.Container; import com.sequenceiq.cloudbreak.domain.HostGroup; import com.sequenceiq.cloudbreak.domain.InstanceMetaData; import com.sequenceiq.cloudbreak.domain.Orchestrator; import com.sequenceiq.cloudbreak.domain.Stack; import com.sequenceiq.cloudbreak.orchestrator.container.ContainerOrchestrator; import com.sequenceiq.cloudbreak.orchestrator.exception.CloudbreakOrchestratorCancelledException; import com.sequenceiq.cloudbreak.orchestrator.exception.CloudbreakOrchestratorException; import com.sequenceiq.cloudbreak.orchestrator.model.ContainerConstraint; import com.sequenceiq.cloudbreak.orchestrator.model.ContainerInfo; import com.sequenceiq.cloudbreak.orchestrator.model.OrchestrationCredential; import com.sequenceiq.cloudbreak.repository.HostGroupRepository; import com.sequenceiq.cloudbreak.repository.StackRepository; import com.sequenceiq.cloudbreak.service.TlsSecurityService; import com.sequenceiq.cloudbreak.service.cluster.ClusterService; import com.sequenceiq.cloudbreak.service.cluster.ContainerService; @Component public class ClusterContainerRunner { private static final String NONE = "none"; @Inject private ClusterService clusterService; @Inject private StackRepository stackRepository; @Inject private HostGroupRepository hostGroupRepository; @Inject private ContainerConfigService containerConfigService; @Inject private ContainerOrchestratorResolver containerOrchestratorResolver; @Inject private ContainerService containerService; @Inject private ConversionService conversionService; @Inject private TlsSecurityService tlsSecurityService; @Inject private ContainerConstraintFactory constraintFactory; public Map<String, List<Container>> runClusterContainers(Stack stack) throws CloudbreakException { try { String cloudPlatform = StringUtils.isNotEmpty(stack.cloudPlatform()) ? stack.cloudPlatform() : NONE; return initializeClusterContainers(stack, cloudPlatform); } catch (CloudbreakOrchestratorCancelledException e) { throw new CancellationException(e.getMessage()); } catch (CloudbreakOrchestratorException e) { throw new CloudbreakException(e); } } public Map<String, List<Container>> addClusterContainers(Long stackId, String hostGroupName, Integer scalingAdjustment) throws CloudbreakException { try { Stack stack = stackRepository.findOneWithLists(stackId); String cloudPlatform = StringUtils.isNotEmpty(stack.cloudPlatform()) ? stack.cloudPlatform() : NONE; return addClusterContainers(stack, cloudPlatform, hostGroupName, scalingAdjustment); } catch (CloudbreakOrchestratorCancelledException e) { throw new CancellationException(e.getMessage()); } catch (CloudbreakOrchestratorException e) { throw new CloudbreakException(e); } } private Map<String, List<Container>> initializeClusterContainers(Stack stack, String cloudPlatform) throws CloudbreakException, CloudbreakOrchestratorException { Orchestrator orchestrator = stack.getOrchestrator(); Map<String, Object> map = new HashMap<>(); map.putAll(orchestrator.getAttributes().getMap()); map.put("certificateDir", tlsSecurityService.prepareCertDir(stack.getId())); OrchestrationCredential credential = new OrchestrationCredential(orchestrator.getApiEndpoint(), map); ContainerOrchestrator containerOrchestrator = containerOrchestratorResolver.get(orchestrator.getType()); Map<String, List<ContainerInfo>> containers = new HashMap<>(); Cluster cluster = clusterService.retrieveClusterByStackId(stack.getId()); String gatewayHostname = getGatewayHostName(stack); try { if (SWARM.equals(orchestrator.getType())) { ContainerConstraint registratorConstraint = constraintFactory.getRegistratorConstraint(gatewayHostname, cluster.getName(), getGatewayPrivateIp(stack), cluster.getId().toString()); containers.put(REGISTRATOR.name(), containerOrchestrator.runContainer(containerConfigService.get(stack, REGISTRATOR), credential, registratorConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId()))); } ContainerConstraint ambariServerDbConstraint = constraintFactory.getAmbariServerDbConstraint(gatewayHostname, cluster.getName(), cluster.getId().toString()); List<ContainerInfo> dbContainer = containerOrchestrator.runContainer(containerConfigService.get(stack, AMBARI_DB), credential, ambariServerDbConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId())); containers.put(AMBARI_DB.name(), dbContainer); String serverDbHostName = dbContainer.get(0).getHost(); ContainerConstraint ambariServerConstraint = constraintFactory.getAmbariServerConstraint(serverDbHostName, gatewayHostname, cloudPlatform, cluster.getName(), cluster.getId().toString()); List<ContainerInfo> ambariServerContainer = containerOrchestrator.runContainer(containerConfigService.get(stack, AMBARI_SERVER), credential, ambariServerConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId())); containers.put(AMBARI_SERVER.name(), ambariServerContainer); String ambariServerHost = ambariServerContainer.get(0).getHost(); if (cluster.isSecure()) { ContainerConstraint havegedConstraint = constraintFactory.getHavegedConstraint(gatewayHostname, cluster.getName(), cluster.getId().toString()); containers.put(HAVEGED.name(), containerOrchestrator.runContainer(containerConfigService.get(stack, HAVEGED), credential, havegedConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId()))); ContainerConstraint kerberosServerConstraint = constraintFactory.getKerberosServerConstraint(cluster, gatewayHostname, cluster.getId().toString()); containers.put(KERBEROS.name(), containerOrchestrator.runContainer(containerConfigService.get(stack, KERBEROS), credential, kerberosServerConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId()))); } if (cluster.isLdapRequired()) { ContainerConstraint ldapConstraint = constraintFactory.getLdapConstraint(ambariServerHost); containers.put(LDAP.name(), containerOrchestrator.runContainer(containerConfigService.get(stack, LDAP), credential, ldapConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId()))); } if (SWARM.equals(orchestrator.getType()) && cluster.getEnableShipyard()) { ContainerConstraint shipyardDbConstraint = constraintFactory.getShipyardDbConstraint(ambariServerHost); containers.put(SHIPYARD_DB.name(), containerOrchestrator.runContainer(containerConfigService.get(stack, SHIPYARD_DB), credential, shipyardDbConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId()))); ContainerConstraint shipyardConstraint = constraintFactory.getShipyardConstraint(ambariServerHost); containers.put(SHIPYARD.name(), containerOrchestrator.runContainer(containerConfigService.get(stack, SHIPYARD), credential, shipyardConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId()))); } List<String> hostBlackList = new ArrayList<>(); for (HostGroup hostGroup : hostGroupRepository.findHostGroupsInCluster(stack.getCluster().getId())) { ContainerConstraint ambariAgentConstraint = constraintFactory.getAmbariAgentConstraint(ambariServerHost, null, cloudPlatform, hostGroup, null, hostBlackList, cluster.getId().toString()); List<ContainerInfo> containerInfos = containerOrchestrator.runContainer(containerConfigService.get(stack, AMBARI_AGENT), credential, ambariAgentConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId())); containers.put(hostGroup.getName(), containerInfos); hostBlackList.addAll(getHostsFromContainerInfo(containerInfos)); } if (SWARM.equals(orchestrator.getType())) { List<String> hosts = getHosts(stack); ContainerConstraint consulWatchConstraint = constraintFactory.getConsulWatchConstraint(hosts); containers.put(CONSUL_WATCH.name(), containerOrchestrator.runContainer(containerConfigService.get(stack, CONSUL_WATCH), credential, consulWatchConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId()))); ContainerConstraint logrotateConstraint = constraintFactory.getLogrotateConstraint(hosts); containers.put(LOGROTATE.name(), containerOrchestrator.runContainer(containerConfigService.get(stack, LOGROTATE), credential, logrotateConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId()))); } return saveContainers(containers, cluster); } catch (CloudbreakOrchestratorException ex) { if (!containers.isEmpty()) { saveContainers(containers, cluster); } checkCancellation(ex); throw ex; } } private String getGatewayHostName(Stack stack) { String gatewayHostname = ""; if (stack.getInstanceGroups() != null && !stack.getInstanceGroups().isEmpty()) { if (stack.getPrimaryGatewayInstance() != null) { InstanceMetaData gatewayInstance = stack.getPrimaryGatewayInstance(); gatewayHostname = gatewayInstance.getDiscoveryFQDN(); } } return gatewayHostname; } private String getGatewayPrivateIp(Stack stack) { String gatewayHostname = ""; if (stack.getInstanceGroups() != null && !stack.getInstanceGroups().isEmpty()) { InstanceMetaData gatewayInstance = stack.getPrimaryGatewayInstance(); gatewayHostname = gatewayInstance.getPrivateIp(); } return gatewayHostname; } private Map<String, List<Container>> addClusterContainers(Stack stack, String cloudPlatform, String hostGroupName, Integer adjustment) throws CloudbreakException, CloudbreakOrchestratorException { Orchestrator orchestrator = stack.getOrchestrator(); Map<String, Object> map = new HashMap<>(); map.putAll(orchestrator.getAttributes().getMap()); map.put("certificateDir", tlsSecurityService.prepareCertDir(stack.getId())); OrchestrationCredential credential = new OrchestrationCredential(orchestrator.getApiEndpoint(), map); ContainerOrchestrator containerOrchestrator = containerOrchestratorResolver.get(orchestrator.getType()); Map<String, List<ContainerInfo>> containers = new HashMap<>(); Cluster cluster = clusterService.retrieveClusterByStackId(stack.getId()); try { Set<Container> existingContainers = containerService.findContainersInCluster(cluster.getId()); String ambariServerHost = existingContainers.stream() .filter(input -> input.getImage().contains(AMBARI_SERVER.getName())) .findFirst().get().getHost(); final HostGroup hostGroup = hostGroupRepository.findHostGroupInClusterByName(cluster.getId(), hostGroupName); String ambariAgentApp = existingContainers.stream() .filter(input -> hostGroup.getHostNames().contains(input.getHost()) && input.getImage().contains(AMBARI_AGENT.getName())) .findFirst().get().getName(); List<String> hostBlackList = getOtherHostgroupsAgentHostsFromContainer(existingContainers, hostGroupName); ContainerConstraint ambariAgentConstraint = constraintFactory.getAmbariAgentConstraint(ambariServerHost, ambariAgentApp, cloudPlatform, hostGroup, adjustment, hostBlackList, cluster.getId().toString()); containers.put(hostGroup.getName(), containerOrchestrator.runContainer(containerConfigService.get(stack, AMBARI_AGENT), credential, ambariAgentConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId()))); if (SWARM.equals(orchestrator.getType())) { List<String> hosts = ambariAgentConstraint.getHosts(); ContainerConstraint consulWatchConstraint = constraintFactory.getConsulWatchConstraint(hosts); containers.put(CONSUL_WATCH.name(), containerOrchestrator.runContainer(containerConfigService.get(stack, CONSUL_WATCH), credential, consulWatchConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId()))); ContainerConstraint logrotateConstraint = constraintFactory.getLogrotateConstraint(hosts); containers.put(LOGROTATE.name(), containerOrchestrator.runContainer(containerConfigService.get(stack, LOGROTATE), credential, logrotateConstraint, clusterDeletionBasedExitCriteriaModel(stack.getId(), cluster.getId()))); } return saveContainers(containers, cluster); } catch (CloudbreakOrchestratorException ex) { if (!containers.isEmpty()) { saveContainers(containers, cluster); } checkCancellation(ex); throw ex; } } private List<String> getOtherHostgroupsAgentHostsFromContainer(Set<Container> existingContainers, String hostGroupName) { final String hostGroupNamePart = hostGroupName.replace("_", "-"); return existingContainers.stream() .filter(input -> input.getImage().contains(AMBARI_AGENT.getName()) && !input.getName().contains(hostGroupNamePart)) .map(Container::getHost).collect(Collectors.toList()); } private List<String> getHostsFromContainerInfo(List<ContainerInfo> containerInfos) { return containerInfos.stream().map(ContainerInfo::getHost).collect(Collectors.toList()); } private List<String> getHosts(Stack stack) { List<String> hosts = new ArrayList<>(); for (InstanceMetaData instanceMetaData : stack.getRunningInstanceMetaData()) { hosts.add(instanceMetaData.getDiscoveryFQDN()); } return hosts; } private List<Container> convert(List<ContainerInfo> containerInfo, Cluster cluster) { List<Container> containers = new ArrayList<>(); for (ContainerInfo source : containerInfo) { Container container = conversionService.convert(source, Container.class); container.setCluster(cluster); containers.add(container); } return containers; } private Map<String, List<Container>> saveContainers(Map<String, List<ContainerInfo>> containerInfo, Cluster cluster) { Map<String, List<Container>> containers = new HashMap<>(); for (Map.Entry<String, List<ContainerInfo>> containerInfoEntry : containerInfo.entrySet()) { List<Container> hostGroupContainers = convert(containerInfoEntry.getValue(), cluster); containers.put(containerInfoEntry.getKey(), hostGroupContainers); containerService.save(hostGroupContainers); } return containers; } private void checkCancellation(CloudbreakOrchestratorException ex) { if (ex instanceof CloudbreakOrchestratorCancelledException || ExceptionUtils.getRootCause(ex) instanceof CloudbreakOrchestratorCancelledException) { throw new CancellationException("Creation of cluster containers was cancelled."); } } }