package io.cattle.platform.servicediscovery.api.service.impl;
import static io.cattle.platform.core.model.tables.CertificateTable.*;
import static io.cattle.platform.core.model.tables.InstanceTable.*;
import static io.cattle.platform.core.model.tables.ServiceTable.*;
import static io.cattle.platform.core.model.tables.StackTable.*;
import static io.cattle.platform.core.model.tables.VolumeTemplateTable.*;
import io.cattle.platform.core.addon.LbConfig;
import io.cattle.platform.core.addon.ServiceLink;
import io.cattle.platform.core.constants.InstanceConstants;
import io.cattle.platform.core.constants.LoadBalancerConstants;
import io.cattle.platform.core.constants.NetworkConstants;
import io.cattle.platform.core.constants.ServiceConstants;
import io.cattle.platform.core.dao.DataDao;
import io.cattle.platform.core.model.Certificate;
import io.cattle.platform.core.model.Instance;
import io.cattle.platform.core.model.Service;
import io.cattle.platform.core.model.ServiceConsumeMap;
import io.cattle.platform.core.model.Stack;
import io.cattle.platform.core.model.VolumeTemplate;
import io.cattle.platform.core.util.LBMetadataUtil.LBConfigMetadataStyle;
import io.cattle.platform.docker.constants.DockerInstanceConstants;
import io.cattle.platform.json.JsonMapper;
import io.cattle.platform.object.ObjectManager;
import io.cattle.platform.object.process.ObjectProcessManager;
import io.cattle.platform.object.util.DataAccessor;
import io.cattle.platform.object.util.DataUtils;
import io.cattle.platform.servicediscovery.api.dao.ServiceConsumeMapDao;
import io.cattle.platform.servicediscovery.api.resource.ServiceDiscoveryConfigItem;
import io.cattle.platform.servicediscovery.api.service.RancherConfigToComposeFormatter;
import io.cattle.platform.servicediscovery.api.service.ServiceDiscoveryApiService;
import io.cattle.platform.servicediscovery.api.util.ServiceDiscoveryUtil;
import io.cattle.platform.token.CertSet;
import io.cattle.platform.token.impl.RSAKeyProvider;
import java.io.ByteArrayOutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.TransformerUtils;
import org.apache.commons.lang3.StringUtils;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.LineBreak;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.representer.Representer;
@Named
public class ServiceDiscoveryApiServiceImpl implements ServiceDiscoveryApiService {
@Inject
ObjectManager objectManager;
@Inject
ObjectProcessManager objectProcessManager;
@Inject
ServiceConsumeMapDao consumeMapDao;
@Inject
List<RancherConfigToComposeFormatter> formatters;
@Inject
JsonMapper jsonMapper;
@Inject
RSAKeyProvider keyProvider;
@Inject
DataDao dataDao;
private final static String COMPOSE_PREFIX = "version: '2'\r\n";
@Override
public void addServiceLink(Service service, ServiceLink serviceLink) {
consumeMapDao.createServiceLink(service, serviceLink);
}
@Override
public void removeServiceLink(Service service, ServiceLink serviceLink) {
ServiceConsumeMap map = consumeMapDao.findMapToRemove(service.getId(), serviceLink.getServiceId());
if (map != null) {
objectProcessManager.scheduleProcessInstance(ServiceConstants.PROCESS_SERVICE_CONSUME_MAP_REMOVE,
map, null);
}
}
@Override
public List<? extends Service> listStackServices(long stackId) {
return objectManager.find(Service.class, SERVICE.STACK_ID, stackId, SERVICE.REMOVED,
null);
}
@Override
public Map.Entry<String, String> buildComposeConfig(List<? extends Service> services, Stack stack) {
return new SimpleEntry<>(buildDockerComposeConfig(services, stack), buildRancherComposeConfig(services));
}
@Override
public String buildDockerComposeConfig(List<? extends Service> services, Stack stack) {
List<? extends VolumeTemplate> volumes = objectManager.find(VolumeTemplate.class, VOLUME_TEMPLATE.STACK_ID,
stack.getId(),
VOLUME_TEMPLATE.REMOVED, null);
Map<String, Object> dockerComposeData = createComposeData(services, true, volumes);
if (dockerComposeData.isEmpty()) {
return COMPOSE_PREFIX;
} else {
return COMPOSE_PREFIX + convertToYml(dockerComposeData);
}
}
@Override
public String buildRancherComposeConfig(List<? extends Service> services) {
Map<String, Object> dockerComposeData = createComposeData(services, false, new ArrayList<VolumeTemplate>());
if (dockerComposeData.isEmpty()) {
return COMPOSE_PREFIX;
} else {
return COMPOSE_PREFIX + convertToYml(dockerComposeData);
}
}
private String convertToYml(Map<String, Object> dockerComposeData) {
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
options.setLineBreak(LineBreak.WIN);
Representer representer = new SkipNullRepresenter();
representer.addClassTag(LBConfigMetadataStyle.class, Tag.MAP);
Yaml yaml = new Yaml(representer, options);
String yamlStr = yaml.dump(dockerComposeData);
return yamlStr.replaceAll("[$]", "\\$\\$");
}
private class SkipNullRepresenter extends Representer {
@Override
protected NodeTuple representJavaBeanProperty(Object javaBean, Property property,
Object propertyValue, Tag customTag) {
if (propertyValue == null) {
return null;
} else {
return super
.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
}
}
}
@SuppressWarnings("unchecked")
private Map<String, Object> createComposeData(List<? extends Service> servicesToExport, boolean forDockerCompose, List<? extends VolumeTemplate> volumes) {
Map<String, Object> servicesData = new HashMap<String, Object>();
Collection<Long> servicesToExportIds = CollectionUtils.collect(servicesToExport,
TransformerUtils.invokerTransformer("getId"));
Map<String, Object> volumesData = new HashMap<String, Object>();
for (Service service : servicesToExport) {
List<String> launchConfigNames = ServiceDiscoveryUtil.getServiceLaunchConfigNames(service);
for (String launchConfigName : launchConfigNames) {
boolean isPrimaryConfig = launchConfigName
.equals(ServiceConstants.PRIMARY_LAUNCH_CONFIG_NAME);
Map<String, Object> cattleServiceData = ServiceDiscoveryUtil.getLaunchConfigWithServiceDataAsMap(
service, launchConfigName);
Map<String, Object> composeServiceData = new HashMap<>();
excludeRancherHash(cattleServiceData);
formatScale(service, cattleServiceData);
formatLBConfig(service, cattleServiceData);
setupServiceType(service, cattleServiceData);
for (String cattleService : cattleServiceData.keySet()) {
translateRancherToCompose(forDockerCompose, cattleServiceData, composeServiceData, cattleService, service, false);
}
if (forDockerCompose) {
populateLinksForService(service, servicesToExportIds, composeServiceData);
populateNetworkForService(service, launchConfigName, composeServiceData);
populateVolumesForService(service, launchConfigName, composeServiceData);
addExtraComposeParameters(service, launchConfigName, composeServiceData);
populateSidekickLabels(service, composeServiceData, isPrimaryConfig);
populateLoadBalancerServiceLabels(service, launchConfigName, composeServiceData);
populateSelectorServiceLabels(service, launchConfigName, composeServiceData);
populateLogConfig(cattleServiceData, composeServiceData);
populateTmpfs(cattleServiceData, composeServiceData);
populateUlimit(cattleServiceData, composeServiceData);
populateBlkioOptions(cattleServiceData, composeServiceData);
translateV1VolumesToV2(cattleServiceData, composeServiceData, volumesData);
}
if (!composeServiceData.isEmpty()) {
servicesData.put(isPrimaryConfig ? service.getName() : launchConfigName, composeServiceData);
}
}
}
for (VolumeTemplate volume : volumes) {
Map<String, Object> cattleVolumeData = new HashMap<>();
cattleVolumeData.putAll(DataUtils.getFields(volume));
cattleVolumeData.put(ServiceConstants.FIELD_VOLUME_EXTERNAL, volume.getExternal());
cattleVolumeData.put(ServiceConstants.FIELD_VOLUME_DRIVER, volume.getDriver());
cattleVolumeData.put(ServiceConstants.FIELD_VOLUME_PER_CONTAINER, volume.getPerContainer());
Map<String, Object> composeVolumeData = new HashMap<>();
for (String cattleVolume : cattleVolumeData.keySet()) {
translateRancherToCompose(forDockerCompose, cattleVolumeData, composeVolumeData, cattleVolume,
null, true);
}
if (!composeVolumeData.isEmpty()) {
volumesData.put(volume.getName(), composeVolumeData);
}
}
Map<String, Object> data = new HashMap<String, Object>();
if (!servicesData.isEmpty()) {
data.put("services", servicesData);
}
if (!volumesData.isEmpty()) {
data.put("volumes", volumesData);
}
return data;
}
@SuppressWarnings("unchecked")
private void excludeRancherHash(Map<String, Object> composeServiceData) {
if (composeServiceData.get(InstanceConstants.FIELD_LABELS) != null) {
Map<String, String> labels = new HashMap<>();
labels.putAll((HashMap<String, String>) composeServiceData.get(InstanceConstants.FIELD_LABELS));
String serviceHash = labels.get(ServiceConstants.LABEL_SERVICE_HASH);
if (serviceHash != null) {
labels.remove(ServiceConstants.LABEL_SERVICE_HASH);
composeServiceData.put(InstanceConstants.FIELD_LABELS, labels);
}
}
if (composeServiceData.get(InstanceConstants.FIELD_METADATA) != null) {
Map<String, String> metadata = new HashMap<>();
metadata.putAll((HashMap<String, String>) composeServiceData.get(InstanceConstants.FIELD_METADATA));
String serviceHash = metadata.get(ServiceConstants.LABEL_SERVICE_HASH);
if (serviceHash != null) {
metadata.remove(ServiceConstants.LABEL_SERVICE_HASH);
composeServiceData.put(InstanceConstants.FIELD_METADATA, metadata);
}
}
}
@SuppressWarnings("unchecked")
protected void formatScale(Service service, Map<String, Object> composeServiceData) {
if (composeServiceData.get(InstanceConstants.FIELD_LABELS) != null) {
Map<String, String> labels = ((HashMap<String, String>) composeServiceData.get(InstanceConstants.FIELD_LABELS));
String globalService = labels.get(ServiceConstants.LABEL_SERVICE_GLOBAL);
if (Boolean.valueOf(globalService) == true) {
composeServiceData.remove(ServiceConstants.FIELD_SCALE);
}
}
}
protected void formatLBConfig(Service service, Map<String, Object> composeServiceData) {
if (composeServiceData.get(ServiceConstants.FIELD_LB_CONFIG) != null) {
LbConfig lbConfig = DataAccessor.field(service, ServiceConstants.FIELD_LB_CONFIG, jsonMapper,
LbConfig.class);
Map<Long, Service> serviceIdsToService = new HashMap<>();
Map<Long, Stack> stackIdsToStack = new HashMap<>();
Map<Long, Certificate> certIdsToCert = new HashMap<>();
for (Service svc : objectManager.find(Service.class, SERVICE.ACCOUNT_ID,
service.getAccountId(), SERVICE.REMOVED, null)) {
serviceIdsToService.put(svc.getId(), svc);
}
for (Stack stack : objectManager.find(Stack.class,
STACK.ACCOUNT_ID,
service.getAccountId(), STACK.REMOVED, null)) {
stackIdsToStack.put(stack.getId(), stack);
}
for (Certificate cert : objectManager.find(Certificate.class,
CERTIFICATE.ACCOUNT_ID, service.getAccountId(), CERTIFICATE.REMOVED, null)) {
certIdsToCert.put(cert.getId(), cert);
}
composeServiceData.put(ServiceConstants.FIELD_LB_CONFIG,
new LBConfigMetadataStyle(lbConfig.getPortRules(), lbConfig.getCertificateIds(),
lbConfig.getDefaultCertificateId(),
lbConfig.getConfig(), lbConfig.getStickinessPolicy(), serviceIdsToService,
stackIdsToStack, certIdsToCert, service.getStackId(), true));
}
}
protected void setupServiceType(Service service, Map<String, Object> composeServiceData) {
Object type = composeServiceData.get(ServiceDiscoveryConfigItem.SERVICE_TYPE.getCattleName());
if (type == null) {
return;
}
if (!InstanceConstants.KIND_VIRTUAL_MACHINE.equals(type.toString())) {
composeServiceData.remove(ServiceDiscoveryConfigItem.SERVICE_TYPE.getCattleName());
}
}
@SuppressWarnings("unchecked")
private void translateV1VolumesToV2(Map<String, Object> cattleServiceData,
Map<String, Object> composeServiceData, Map<String, Object> volumesData) {
// volume driver presence defines the v1 format for the volumes
String volumeDriver = String.valueOf(cattleServiceData.get(ServiceDiscoveryConfigItem.VOLUME_DRIVER
.getCattleName()));
if (StringUtils.isEmpty(volumeDriver)) {
return;
}
composeServiceData.remove(ServiceDiscoveryConfigItem.VOLUME_DRIVER
.getDockerName());
Object dataVolumes = cattleServiceData.get(InstanceConstants.FIELD_DATA_VOLUMES);
if (dataVolumes == null) {
return;
}
for (String dataVolume : (List<String>) dataVolumes) {
String[] splitted = dataVolume.split(":");
if (splitted.length < 2) {
// only process named volumes
continue;
}
String dataVolumeName = splitted[0];
// skip bind mounts
Path p = Paths.get(dataVolumeName);
if (p.isAbsolute()) {
continue;
}
if (volumesData.containsKey(dataVolumeName)) {
// either defined by volumeTemplate, or external volume is already created for it
continue;
}
Map<String, Object> cattleVolumeData = new HashMap<>();
cattleVolumeData.put(ServiceConstants.FIELD_VOLUME_EXTERNAL, true);
cattleVolumeData.put(ServiceConstants.FIELD_VOLUME_DRIVER, volumeDriver);
Map<String, Object> composeVolumeData = new HashMap<>();
for (String cattleVolume : cattleVolumeData.keySet()) {
translateRancherToCompose(true, cattleVolumeData, composeVolumeData, cattleVolume,
null, true);
}
if (!composeVolumeData.isEmpty()) {
volumesData.put(dataVolumeName, composeVolumeData);
}
}
}
@SuppressWarnings("unchecked")
private void populateLogConfig(Map<String, Object> cattleServiceData, Map<String, Object> composeServiceData) {
Object value = cattleServiceData.get(ServiceConstants.FIELD_LOG_CONFIG);
if (value instanceof Map) {
if (!((Map<?, ?>) value).isEmpty()) {
Map<String, Object> logConfig = new HashMap<>();
Map<String, Object> map = (Map<String, Object>) value;
Iterator<String> it = map.keySet().iterator();
while (it.hasNext()) {
String key = it.next();
if (key.equalsIgnoreCase("config") && map.get(key) != null) {
if (map.get(key) instanceof java.util.Map && !((Map<?, ?>) map.get(key)).isEmpty()) {
logConfig.put("options", map.get(key));
}
} else if (key.equalsIgnoreCase("driver") && map.get(key) != null && map.get(key) != "") {
logConfig.put("driver", map.get(key));
}
}
if (!logConfig.isEmpty() && logConfig.get("driver") != null) {
composeServiceData.put("logging", logConfig);
}
}
}
}
@SuppressWarnings("unchecked")
private void populateTmpfs(Map<String, Object> cattleServiceData, Map<String, Object> composeServiceData) {
Object value = cattleServiceData.get(ServiceConstants.FIELD_TMPFS);
if (value instanceof Map) {
if (!((Map<?, ?>) value).isEmpty()) {
Map<String, Object> map = (Map<String, Object>)value;
Iterator<String> it = map.keySet().iterator();
ArrayList<String> list = new ArrayList<>();
while (it.hasNext()) {
String key = it.next();
String option = "";
if (map.get(key) != null) {
option = map.get(key).toString();
}
if (option.isEmpty()) {
list.add(key);
} else {
list.add(key + ":" + option);
}
}
composeServiceData.put("tmpfs", list);
}
}
}
@SuppressWarnings("unchecked")
private void populateUlimit(Map<String, Object> cattleServiceData, Map<String, Object> composeServiceData) {
Object value = cattleServiceData.get(ServiceConstants.FIELD_ULIMITS);
if (value instanceof List<?>) {
if (!((List<?>) value).isEmpty()) {
List<Object> list = (List<Object>) value;
Map<String, Object> ulimits = new HashMap<>();
for (Object ulimit: list) {
// if there is one limit set(must be soft), parse it as map[string]string. If not, parse it as nested map
if (ulimit instanceof Map) {
Map<String, Object> ulimitMap = (Map<String, Object>) ulimit;
// name can not be null
if (ulimitMap.get("name").toString() != null) {
if (ulimitMap.get("soft") != null && ulimitMap.get("hard") == null) {
ulimits.put(ulimitMap.get("name").toString(), ulimitMap.get("soft"));
}
else if (ulimitMap.get("soft") != null && ulimitMap.get("hard") != null) {
Map<String, Object> nestedMap = new HashMap<>();
nestedMap.put("hard", ulimitMap.get("hard"));
nestedMap.put("soft", ulimitMap.get("soft"));
ulimits.put(ulimitMap.get("name").toString(), nestedMap);
}
}
}
}
composeServiceData.put("ulimits", ulimits);
}
}
}
@SuppressWarnings("unchecked")
private void populateBlkioOptions(Map<String, Object> cattleServiceData, Map<String, Object> composeServiceData) {
Object value = cattleServiceData.get(ServiceConstants.FIELD_BLKIOOPTIONS);
if (value instanceof Map) {
if (!((Map<?, ?>) value).isEmpty()) {
Map<String, Object> options = (Map<String, Object>) value;
Map<String, Object> deviceWeight = new HashMap<>();
Map<String, Object> deviceReadBps = new HashMap<>();
Map<String, Object> deviceReadIops = new HashMap<>();
Map<String, Object> deviceWriteBps = new HashMap<>();
Map<String, Object> deviceWriteIops = new HashMap<>();
for (String key: options.keySet()) {
Object option = options.get(key);
if (option instanceof Map) {
Map<String, Object> optionMap = (Map<String, Object>) option;
if (optionMap.get("readIops") != null) {
deviceReadIops.put(key, optionMap.get("readIops"));
}
if (optionMap.get("writeIops") != null) {
deviceWriteIops.put(key, optionMap.get("writeIops"));
}
if (optionMap.get("readBps") != null) {
deviceReadBps.put(key, optionMap.get("readBps"));
}
if (optionMap.get("writeBps") != null) {
deviceWriteBps.put(key, optionMap.get("writeBps"));
}
if (optionMap.get("weight") != null) {
deviceWeight.put(key, optionMap.get("weight"));
}
}
}
if (!deviceWeight.isEmpty()) {
composeServiceData.put("blkio_weight_device", deviceWeight);
}
if (!deviceReadBps.isEmpty()) {
composeServiceData.put("device_read_bps", deviceReadBps);
}
if (!deviceReadIops.isEmpty()) {
composeServiceData.put("device_read_iops", deviceReadIops);
}
if (!deviceWriteBps.isEmpty()) {
composeServiceData.put("device_write_bps", deviceWriteBps);
}
if (!deviceWriteIops.isEmpty()) {
composeServiceData.put("device_write_iops", deviceWriteIops);
}
}
}
}
@SuppressWarnings("unchecked")
protected void populateLoadBalancerServiceLabels(Service service,
String launchConfigName, Map<String, Object> composeServiceData) {
// to support lb V1 export format
if (!isV1LB(service)) {
return;
}
Map<String, String> labels = new HashMap<>();
if (composeServiceData.get(InstanceConstants.FIELD_LABELS) != null) {
labels.putAll((HashMap<String, String>) composeServiceData.get(InstanceConstants.FIELD_LABELS));
}
// get all consumed services maps
List<? extends ServiceConsumeMap> consumedServiceMaps = consumeMapDao.findConsumedServices(service.getId());
// for each port, populate the label
for (ServiceConsumeMap map : consumedServiceMaps) {
Service consumedService = objectManager.loadResource(Service.class, map.getConsumedServiceId());
List<String> ports = DataAccessor.fieldStringList(map, LoadBalancerConstants.FIELD_LB_TARGET_PORTS);
String consumedServiceName = consumedService.getName();
if (!service.getStackId().equals(consumedService.getStackId())) {
Stack env = objectManager.loadResource(Stack.class,
consumedService.getStackId());
consumedServiceName = env.getName() + "/" + consumedServiceName;
}
String labelName = ServiceConstants.LABEL_LB_TARGET + consumedServiceName;
StringBuilder bldr = new StringBuilder();
for (String port : ports) {
bldr.append(port).append(",");
}
if (bldr.length() > 0) {
labels.put(labelName, bldr.toString().substring(0, bldr.length() - 1));
}
}
if (!labels.isEmpty()) {
composeServiceData.put(InstanceConstants.FIELD_LABELS, labels);
}
}
@SuppressWarnings("unchecked")
protected void populateSelectorServiceLabels(Service service,
String launchConfigName, Map<String, Object> composeServiceData) {
String selectorContainer = service.getSelectorContainer();
String selectorLink = service.getSelectorLink();
if (selectorContainer == null && selectorLink == null) {
return;
}
Map<String, String> labels = new HashMap<>();
if (composeServiceData.get(InstanceConstants.FIELD_LABELS) != null) {
labels.putAll((HashMap<String, String>) composeServiceData.get(InstanceConstants.FIELD_LABELS));
}
if (selectorLink != null) {
labels.put(ServiceConstants.LABEL_SELECTOR_LINK, selectorLink);
}
if (selectorContainer != null) {
labels.put(ServiceConstants.LABEL_SELECTOR_CONTAINER, selectorContainer);
}
if (!labels.isEmpty()) {
composeServiceData.put(InstanceConstants.FIELD_LABELS, labels);
}
}
@SuppressWarnings("unchecked")
protected void populateSidekickLabels(Service service, Map<String, Object> composeServiceData, boolean isPrimary) {
List<? extends String> configs = ServiceDiscoveryUtil
.getServiceLaunchConfigNames(service);
configs.remove(ServiceConstants.PRIMARY_LAUNCH_CONFIG_NAME);
StringBuilder sidekicks = new StringBuilder();
for (String config : configs) {
sidekicks.append(config).append(",");
}
Map<String, String> labels = new HashMap<>();
if (composeServiceData.get(InstanceConstants.FIELD_LABELS) != null) {
labels.putAll((HashMap<String, String>) composeServiceData.get(InstanceConstants.FIELD_LABELS));
labels.remove(ServiceConstants.LABEL_SIDEKICK);
}
if (!sidekicks.toString().isEmpty() && isPrimary) {
String sidekicksFinal = sidekicks.toString().substring(0, sidekicks.length() - 1);
labels.put(ServiceConstants.LABEL_SIDEKICK, sidekicksFinal);
}
if (!labels.isEmpty()) {
composeServiceData.put(InstanceConstants.FIELD_LABELS, labels);
} else {
composeServiceData.remove(InstanceConstants.FIELD_LABELS);
}
}
private void populateLinksForService(Service service, Collection<Long> servicesToExportIds,
Map<String, Object> composeServiceData) {
// no export for lb service links for lb v2
if (service.getKind().equalsIgnoreCase(ServiceConstants.KIND_LOAD_BALANCER_SERVICE) && !isV1LB(service)) {
return;
}
List<String> serviceLinksWithNames = new ArrayList<>();
List<String> externalLinksServices = new ArrayList<>();
List<? extends ServiceConsumeMap> consumedServiceMaps = consumeMapDao.findConsumedServices(service.getId());
for (ServiceConsumeMap consumedServiceMap : consumedServiceMaps) {
Service consumedService = objectManager.findOne(Service.class, SERVICE.ID,
consumedServiceMap.getConsumedServiceId());
String linkName = consumedService.getName()
+ ":"
+ (!StringUtils.isEmpty(consumedServiceMap.getName()) ? consumedServiceMap.getName()
: consumedService
.getName());
if (servicesToExportIds.contains(consumedServiceMap.getConsumedServiceId())) {
serviceLinksWithNames.add(linkName);
} else if (!consumedService.getStackId().equals(service.getStackId())) {
Stack externalStack = objectManager.loadResource(Stack.class,
consumedService.getStackId());
externalLinksServices.add(externalStack.getName() + "/" + linkName);
}
}
if (!serviceLinksWithNames.isEmpty()) {
composeServiceData.put(ServiceDiscoveryConfigItem.LINKS.getDockerName(), serviceLinksWithNames);
}
if (!externalLinksServices.isEmpty()) {
composeServiceData.put(ServiceDiscoveryConfigItem.EXTERNALLINKS.getDockerName(), externalLinksServices);
}
}
private void addExtraComposeParameters(Service service,
String launchConfigName, Map<String, Object> composeServiceData) {
if (service.getKind().equalsIgnoreCase(ServiceConstants.KIND_DNS_SERVICE)) {
composeServiceData.put(ServiceDiscoveryConfigItem.IMAGE.getDockerName(), "rancher/dns-service");
} else if (isV1LB(service)) {
composeServiceData.put(ServiceDiscoveryConfigItem.IMAGE.getDockerName(),
"rancher/load-balancer-service");
} else if (service.getKind().equalsIgnoreCase(ServiceConstants.KIND_EXTERNAL_SERVICE)) {
composeServiceData.put(ServiceDiscoveryConfigItem.IMAGE.getDockerName(), "rancher/external-service");
}
}
private void populateNetworkForService(Service service,
String launchConfigName, Map<String, Object> composeServiceData) {
Object networkMode = composeServiceData.get(ServiceDiscoveryConfigItem.NETWORKMODE.getDockerName());
if (networkMode != null) {
if (networkMode.equals(NetworkConstants.NETWORK_MODE_CONTAINER)) {
Map<String, Object> serviceData = ServiceDiscoveryUtil.getLaunchConfigDataAsMap(service,
launchConfigName);
// network mode can be passed by container, or by service name, so check both
// networkFromContainerId wins
Integer targetContainerId = DataAccessor
.fieldInteger(service, DockerInstanceConstants.DOCKER_CONTAINER);
if (targetContainerId != null) {
Instance instance = objectManager.loadResource(Instance.class, targetContainerId.longValue());
String instanceName = ServiceDiscoveryUtil.getInstanceName(instance);
composeServiceData.put(ServiceDiscoveryConfigItem.NETWORKMODE.getDockerName(),
NetworkConstants.NETWORK_MODE_CONTAINER + ":" + instanceName);
} else {
Object networkLaunchConfig = serviceData
.get(ServiceConstants.FIELD_NETWORK_LAUNCH_CONFIG);
if (networkLaunchConfig != null) {
composeServiceData.put(ServiceDiscoveryConfigItem.NETWORKMODE.getDockerName(),
NetworkConstants.NETWORK_MODE_CONTAINER + ":" + networkLaunchConfig);
}
}
} else if (networkMode.equals(NetworkConstants.NETWORK_MODE_MANAGED)) {
composeServiceData.remove(ServiceDiscoveryConfigItem.NETWORKMODE.getDockerName());
}
}
}
protected void translateRancherToCompose(boolean forDockerCompose, Map<String, Object> rancherServiceData,
Map<String, Object> composeServiceData, String cattleName, Service service, boolean isVolume) {
ServiceDiscoveryConfigItem item = ServiceDiscoveryConfigItem.getServiceConfigItemByCattleName(cattleName,
service, isVolume);
if (item != null && item.isDockerComposeProperty() == forDockerCompose) {
Object value = rancherServiceData.get(cattleName);
boolean export = false;
if (value instanceof List) {
if (!((List<?>) value).isEmpty()) {
export = true;
}
} else if (value instanceof Map) {
if (!((Map<?, ?>) value).isEmpty()) {
export = true;
}
} else if (value instanceof Boolean) {
if (((Boolean) value).booleanValue()) {
export = true;
}
} else if (value != null) {
export = true;
}
if (export) {
// for every lookup, do transform
Object formattedValue = null;
for (RancherConfigToComposeFormatter formatter : formatters) {
formattedValue = formatter.format(item, value);
if (formattedValue != null) {
break;
}
}
if (formattedValue != null) {
if (formattedValue != RancherConfigToComposeFormatter.Option.REMOVE) {
composeServiceData.put(item.getDockerName().toLowerCase(), formattedValue);
}
} else {
composeServiceData.put(item.getDockerName().toLowerCase(), value);
}
}
}
}
@SuppressWarnings("unchecked")
private void populateVolumesForService(Service service, String launchConfigName,
Map<String, Object> composeServiceData) {
List<String> namesCombined = new ArrayList<>();
List<String> launchConfigNames = new ArrayList<>();
Map<String, Object> launchConfigData = ServiceDiscoveryUtil.getLaunchConfigDataAsMap(service, launchConfigName);
Object dataVolumesLaunchConfigs = launchConfigData.get(
ServiceConstants.FIELD_DATA_VOLUMES_LAUNCH_CONFIG);
if (dataVolumesLaunchConfigs != null) {
launchConfigNames.addAll((List<String>) dataVolumesLaunchConfigs);
}
// 1. add launch config names
namesCombined.addAll(launchConfigNames);
// 2. add instance names if specified
List<? extends Integer> instanceIds = (List<? extends Integer>) launchConfigData
.get(DockerInstanceConstants.FIELD_VOLUMES_FROM);
if (instanceIds != null) {
for (Integer instanceId : instanceIds) {
Instance instance = objectManager.findOne(Instance.class, INSTANCE.ID, instanceId, INSTANCE.REMOVED,
null);
String instanceName = ServiceDiscoveryUtil.getInstanceName(instance);
if (instanceName != null) {
namesCombined.add(instanceName);
}
}
}
if (!namesCombined.isEmpty()) {
composeServiceData.put(ServiceDiscoveryConfigItem.VOLUMESFROM.getDockerName(), namesCombined);
}
}
@Override
public String getServiceCertificate(final Service service) {
if (service == null) {
return null;
}
final Stack stack = objectManager.loadResource(Stack.class, service.getStackId());
if (stack == null) {
return null;
}
String newKey = String.format("service.v3.%d.%s.cert", service.getAccountId(), service.getName());
return dataDao.getOrCreate(newKey, false, new Callable<String>() {
@Override
public String call() throws Exception {
return generateService(service, stack);
}
});
}
protected String generateService(Service service, Stack stack) throws Exception {
@SuppressWarnings("unchecked")
Map<String, Object> metadata = DataAccessor.fields(service).withKey(ServiceConstants.FIELD_METADATA)
.withDefault(Collections.EMPTY_MAP).as(Map.class);
String serviceName = service.getName();
List<? extends String> configuredSans = DataAccessor.fromMap(metadata).withKey("sans")
.withDefault(Collections.emptyList()).asList(jsonMapper, String.class);
List<String> sans = new ArrayList<>(configuredSans);
sans.add(serviceName.toLowerCase());
sans.add(String.format("%s.%s", serviceName, stack.getName()).toLowerCase());
sans.add(String.format("%s.%s.%s", serviceName, stack.getName(), NetworkConstants.INTERNAL_DNS_SEARCH_DOMAIN)
.toLowerCase());
CertSet certSet = keyProvider.generateCertificate(serviceName, sans.toArray(new String[sans.size()]));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
certSet.writeZip(baos);
return Base64.encodeBase64String(baos.toByteArray());
}
@Override
public boolean isV1LB(Service service) {
if (!service.getKind().equalsIgnoreCase(ServiceConstants.KIND_LOAD_BALANCER_SERVICE)) {
return false;
}
LbConfig lbConfig = DataAccessor.field(service, ServiceConstants.FIELD_LB_CONFIG, jsonMapper,
LbConfig.class);
return lbConfig == null;
}
}