package com.sequenceiq.cloudbreak.service.cluster.flow.blueprint;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.sequenceiq.cloudbreak.util.JsonUtil;
@Component
public class JacksonBlueprintProcessor implements BlueprintProcessor {
private static final String CONFIGURATIONS_NODE = "configurations";
private static final String SETTINGS_NODE = "settings";
private static final String HOST_GROUPS_NODE = "host_groups";
private static final String BLUEPRINTS = "Blueprints";
private static final String STACK_VERSION = "stack_version";
@Override
public String addConfigEntries(String originalBlueprint, List<BlueprintConfigurationEntry> configurationEntries, boolean override) {
try {
ObjectNode root = (ObjectNode) JsonUtil.readTree(originalBlueprint);
JsonNode configurationsNode = root.path(CONFIGURATIONS_NODE);
if (configurationsNode.isMissingNode()) {
configurationsNode = root.putArray(CONFIGURATIONS_NODE);
}
ArrayNode configurationsArrayNode = (ArrayNode) configurationsNode;
for (BlueprintConfigurationEntry configurationEntry : configurationEntries) {
JsonNode configFileNode = configurationsArrayNode.findPath(configurationEntry.getConfigFile());
if (override || configFileNode.path("properties").findPath(configurationEntry.getKey()).isMissingNode()) {
if (configFileNode.isMissingNode()) {
ObjectNode arrayElementNode = configurationsArrayNode.addObject();
configFileNode = arrayElementNode.putObject(configurationEntry.getConfigFile());
}
JsonNode propertiesNode = configFileNode.path("properties");
if (!propertiesNode.isMissingNode()) {
((ObjectNode) propertiesNode).put(configurationEntry.getKey(), configurationEntry.getValue());
} else {
((ObjectNode) configFileNode).put(configurationEntry.getKey(), configurationEntry.getValue());
}
}
}
return JsonUtil.writeValueAsString(root);
} catch (IOException e) {
throw new BlueprintProcessingException("Failed to add config entries to original blueprint.", e);
}
}
@Override
public String addSettingsEntries(String originalBlueprint, List<BlueprintConfigurationEntry> configurationEntries, boolean override) {
try {
ObjectNode root = (ObjectNode) JsonUtil.readTree(originalBlueprint);
JsonNode configurationsNode = root.path(SETTINGS_NODE);
if (configurationsNode.isMissingNode()) {
configurationsNode = root.putArray(SETTINGS_NODE);
}
ArrayNode configurationsArrayNode = (ArrayNode) configurationsNode;
for (BlueprintConfigurationEntry configurationEntry : configurationEntries) {
JsonNode configFileNode = configurationsArrayNode.findPath(configurationEntry.getConfigFile());
if (override || configFileNode.findPath(configurationEntry.getKey()).isMissingNode()) {
if (configFileNode.isMissingNode()) {
ObjectNode arrayElementNode = configurationsArrayNode.addObject();
ArrayNode recoveryDefinition = arrayElementNode.putArray(configurationEntry.getConfigFile());
ObjectNode recoveryEntry = recoveryDefinition.addObject();
recoveryEntry.put(configurationEntry.getKey(), configurationEntry.getValue());
} else {
ObjectNode recoveryEntry = ((ArrayNode) configFileNode).addObject();
((ObjectNode) recoveryEntry).put(configurationEntry.getKey(), configurationEntry.getValue());
}
}
}
return JsonUtil.writeValueAsString(root);
} catch (IOException e) {
throw new BlueprintProcessingException("Failed to add config entries to original blueprint.", e);
}
}
@Override
public Set<String> getComponentsInHostGroup(String blueprintText, String hostGroup) {
try {
Set<String> services = new HashSet<>();
ObjectNode root = (ObjectNode) JsonUtil.readTree(blueprintText);
ArrayNode hostGroupsNode = (ArrayNode) root.path(HOST_GROUPS_NODE);
Iterator<JsonNode> hostGroups = hostGroupsNode.elements();
while (hostGroups.hasNext()) {
JsonNode hostGroupNode = hostGroups.next();
if (hostGroup.equals(hostGroupNode.path("name").textValue())) {
Iterator<JsonNode> components = hostGroupNode.path("components").elements();
while (components.hasNext()) {
services.add(components.next().path("name").textValue());
}
break;
}
}
return services;
} catch (IOException e) {
throw new BlueprintProcessingException("Failed to get components for hostgroup '" + hostGroup + "' from blueprint.", e);
}
}
@Override
public boolean componentExistsInBlueprint(String component, String blueprintText) {
boolean componentExists = false;
try {
ObjectNode root = (ObjectNode) JsonUtil.readTree(blueprintText);
ArrayNode hostGroupsNode = (ArrayNode) root.path(HOST_GROUPS_NODE);
Iterator<JsonNode> hostGroups = hostGroupsNode.elements();
while (hostGroups.hasNext() && !componentExists) {
JsonNode hostGroupNode = hostGroups.next();
componentExists = componentExistsInHostgroup(component, hostGroupNode);
}
return componentExists;
} catch (IOException e) {
throw new BlueprintProcessingException("Failed to check that component('" + component + "') exists in the blueprint.", e);
}
}
@Override
public String removeComponentFromBlueprint(String component, String blueprintText) {
try {
ObjectNode root = (ObjectNode) JsonUtil.readTree(blueprintText);
ArrayNode hostGroupsNode = (ArrayNode) root.path(HOST_GROUPS_NODE);
Iterator<JsonNode> hostGroups = hostGroupsNode.elements();
while (hostGroups.hasNext()) {
JsonNode hostGroupNode = hostGroups.next();
Iterator<JsonNode> components = hostGroupNode.path("components").elements();
while (components.hasNext()) {
if (component.equals(components.next().path("name").textValue())) {
components.remove();
}
}
}
return JsonUtil.writeValueAsString(root);
} catch (IOException e) {
throw new BlueprintProcessingException("Failed to remove component('" + component + "') from the blueprint.", e);
}
}
@Override
public String modifyHdpVersion(String blueprintText, String hdpVersion) {
try {
ObjectNode root = (ObjectNode) JsonUtil.readTree(blueprintText);
ObjectNode blueprintsNode = (ObjectNode) root.path(BLUEPRINTS);
blueprintsNode.remove(STACK_VERSION);
String[] split = hdpVersion.split("\\.");
blueprintsNode.put(STACK_VERSION, split[0] + "." + split[1]);
return JsonUtil.writeValueAsString(root);
} catch (IOException e) {
throw new BlueprintProcessingException("Failed to modify hdp version.", e);
}
}
@Override
public String addComponentToHostgroups(String component, Collection<String> hostGroupNames, String blueprintText) {
try {
ObjectNode root = (ObjectNode) JsonUtil.readTree(blueprintText);
ArrayNode hostGroupsNode = (ArrayNode) root.path(HOST_GROUPS_NODE);
Iterator<JsonNode> hostGroups = hostGroupsNode.elements();
while (hostGroups.hasNext()) {
JsonNode hostGroupNode = hostGroups.next();
String hostGroupName = hostGroupNode.path("name").textValue();
if (hostGroupNames.contains(hostGroupName) && !componentExistsInHostgroup(component, hostGroupNode)) {
ArrayNode components = (ArrayNode) hostGroupNode.path("components");
components.addPOJO(new ComponentElement(component));
}
}
return JsonUtil.writeValueAsString(root);
} catch (IOException e) {
throw new BlueprintProcessingException("Failed to remove component('" + component + "') from the blueprint.", e);
}
}
private boolean componentExistsInHostgroup(String component, JsonNode hostGroupNode) {
boolean componentExists = false;
Iterator<JsonNode> components = hostGroupNode.path("components").elements();
while (components.hasNext()) {
if (component.equals(components.next().path("name").textValue())) {
componentExists = true;
break;
}
}
return componentExists;
}
private class ComponentElement {
private String name;
private ComponentElement(String component) {
name = component;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}