package com.sequenceiq.cloudbreak.orchestrator.salt.poller;
import static java.util.Collections.singletonMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import com.sequenceiq.cloudbreak.common.type.RecipeType;
import com.sequenceiq.cloudbreak.orchestrator.OrchestratorBootstrap;
import com.sequenceiq.cloudbreak.orchestrator.exception.CloudbreakOrchestratorFailedException;
import com.sequenceiq.cloudbreak.orchestrator.model.GenericResponse;
import com.sequenceiq.cloudbreak.orchestrator.model.GenericResponses;
import com.sequenceiq.cloudbreak.orchestrator.model.Node;
import com.sequenceiq.cloudbreak.orchestrator.model.RecipeModel;
import com.sequenceiq.cloudbreak.orchestrator.model.SaltPillarProperties;
import com.sequenceiq.cloudbreak.orchestrator.salt.client.SaltConnector;
import com.sequenceiq.cloudbreak.orchestrator.salt.domain.Pillar;
public class PillarSave implements OrchestratorBootstrap {
private static final Logger LOGGER = LoggerFactory.getLogger(PillarSave.class);
private final SaltConnector sc;
private final Pillar pillar;
private Set<String> targets;
private final Set<String> originalTargets;
public PillarSave(SaltConnector sc, Set<String> targets, Set<Node> hosts, boolean useCustomDomain) {
this.sc = sc;
Map<String, Map<String, Object>> fqdn = hosts
.stream()
.collect(Collectors.toMap(Node::getPrivateIp, node -> discovery(node.getHostname(), node.getPublicIp(), useCustomDomain)));
this.pillar = new Pillar("/nodes/hosts.sls", singletonMap("hosts", fqdn), targets);
this.targets = targets;
this.originalTargets = targets;
}
public PillarSave(SaltConnector sc, Set<String> targets, Map<String, List<RecipeModel>> recipes) {
this.sc = sc;
Map<String, Map<String, List<String>>> scripts = new HashMap<>();
for (String hostGroup : recipes.keySet()) {
List<String> pre = recipes.get(hostGroup).stream().
filter(h -> h.getRecipeType() == RecipeType.PRE).map(RecipeModel::getName).collect(Collectors.toList());
List<String> post = recipes.get(hostGroup).stream().
filter(h -> h.getRecipeType() == RecipeType.POST).map(RecipeModel::getName).collect(Collectors.toList());
Map<String, List<String>> prePostScripts = new HashMap<>();
prePostScripts.put("pre", pre);
prePostScripts.put("post", post);
scripts.put(hostGroup, prePostScripts);
}
this.pillar = new Pillar("/recipes/init.sls", singletonMap("recipes", scripts), targets);
this.targets = targets;
this.originalTargets = targets;
}
public PillarSave(SaltConnector sc, Set<String> targets, SaltPillarProperties pillarProperties) {
this.sc = sc;
this.pillar = new Pillar(pillarProperties.getPath(), pillarProperties.getProperties(), targets);
this.targets = targets;
this.originalTargets = targets;
}
private Map<String, Object> discovery(String hostname, String publicAddress, boolean useCustomDomain) {
Map<String, Object> map = new HashMap<>();
map.put("fqdn", hostname);
map.put("hostname", hostname.split("\\.")[0]);
map.put("domain", hostname.replaceFirst(hostname.split("\\.")[0] + ".", ""));
map.put("custom_domain", useCustomDomain);
map.put("public_address", StringUtils.isEmpty(publicAddress) ? Boolean.FALSE : Boolean.TRUE);
return map;
}
@Override
public Boolean call() throws Exception {
LOGGER.info("Distribute pillar configs to: {}", targets);
if (!targets.isEmpty()) {
GenericResponses responses = sc.pillar(targets, pillar);
Set<String> failedTargets = new HashSet<>();
LOGGER.info("Salt pillar save responses: {}", responses);
for (GenericResponse genericResponse : responses.getResponses()) {
if (genericResponse.getStatusCode() != HttpStatus.OK.value()) {
LOGGER.info("Failed pillar save attempt to: " + genericResponse.getAddress());
String address = genericResponse.getAddress().split(":")[0];
failedTargets.addAll(originalTargets.stream().filter(a -> a.equals(address)).collect(Collectors.toList()));
}
}
targets = failedTargets;
if (!targets.isEmpty()) {
LOGGER.info("Missing nodes for pillar save: {}", targets);
throw new CloudbreakOrchestratorFailedException("There are missing nodes for pillar save: " + targets);
}
}
LOGGER.info("Pillar save has been completed on nodes: {}", originalTargets);
return true;
}
}