/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.docker.compose.yaml;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.workspace.Environment;
import org.eclipse.che.api.core.model.workspace.EnvironmentRecipe;
import org.eclipse.che.api.environment.server.TypeSpecificEnvironmentParser;
import org.eclipse.che.api.environment.server.model.CheServiceBuildContextImpl;
import org.eclipse.che.api.environment.server.model.CheServiceImpl;
import org.eclipse.che.api.environment.server.model.CheServicesEnvironmentImpl;
import org.eclipse.che.api.machine.server.util.RecipeDownloader;
import org.eclipse.che.plugin.docker.compose.ComposeEnvironment;
import org.eclipse.che.plugin.docker.compose.ComposeServiceImpl;
import java.io.IOException;
import java.util.Map;
import static java.util.Objects.requireNonNull;
/**
* Converters compose file to {@link ComposeEnvironment} and vise versa or
* converters compose {@link Environment} to {@link CheServicesEnvironmentImpl}.
*
* @author Alexander Garagatyi
* @author Alexander Andrienko
*/
public class ComposeEnvironmentParser implements TypeSpecificEnvironmentParser {
private static final ObjectMapper YAML_PARSER = new ObjectMapper(new YAMLFactory());
private final RecipeDownloader recipeDownloader;
@Inject
public ComposeEnvironmentParser(RecipeDownloader recipeDownloader) {
this.recipeDownloader = recipeDownloader;
}
/**
* Parses compose file from {@link Environment} into {@link CheServicesEnvironmentImpl}.
*
* @param environment
* environment with {@link EnvironmentRecipe} to parse.
* {@link EnvironmentRecipe} contains {@link CheServicesEnvironmentImpl} definition.
* @throws IllegalArgumentException
* when environment or environment recipe is invalid
* @throws ServerException
* when environment recipe can not be retrieved
*/
@Override
public CheServicesEnvironmentImpl parse(Environment environment) throws ServerException {
requireNonNull(environment, "Environment should not be null");
EnvironmentRecipe recipe = environment.getRecipe();
requireNonNull(environment.getRecipe(), "Environment recipe should not be null");
String content = getContentOfRecipe(recipe);
ComposeEnvironment composeEnvironment = parse(content, recipe.getContentType());
return asCheEnvironment(composeEnvironment);
}
/**
* Parses compose file into Docker Compose model.
*
* @param recipeContent
* compose file to parse
* @throws IllegalArgumentException
* when environment or environment recipe is invalid
* @throws ServerException
* when environment recipe can not be retrieved
*/
public ComposeEnvironment parse(String recipeContent, String contentType) throws ServerException {
requireNonNull(recipeContent, "Recipe content should not be null");
requireNonNull(contentType, "Recipe content type should not be null");
ComposeEnvironment composeEnvironment;
switch (contentType) {
case "application/x-yaml":
case "text/yaml":
case "text/x-yaml":
try {
composeEnvironment = YAML_PARSER.readValue(recipeContent, ComposeEnvironment.class);
} catch (IOException e) {
throw new IllegalArgumentException(
"Parsing of environment configuration failed. " + e.getLocalizedMessage());
}
break;
default:
throw new IllegalArgumentException("Provided environment recipe content type '" +
contentType +
"' is unsupported. Supported values are: " +
"application/x-yaml, text/yaml, text/x-yaml");
}
return composeEnvironment;
}
/**
* Converts Docker Compose environment model into YAML file.
*
* @param composeEnvironment
* Docker Compose environment model file
* @throws IllegalArgumentException
* when argument is null or conversion to YAML fails
*/
public String toYaml(ComposeEnvironment composeEnvironment) {
requireNonNull(composeEnvironment, "Compose environment should not be null");
try {
return YAML_PARSER.writeValueAsString(composeEnvironment);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e.getLocalizedMessage(), e);
}
}
private String getContentOfRecipe(EnvironmentRecipe environmentRecipe) throws ServerException {
if (environmentRecipe.getContent() != null) {
return environmentRecipe.getContent();
} else {
return recipeDownloader.getRecipe(environmentRecipe.getLocation());
}
}
private CheServicesEnvironmentImpl asCheEnvironment(ComposeEnvironment composeEnvironment) {
Map<String, CheServiceImpl> services = Maps.newHashMapWithExpectedSize(composeEnvironment.getServices().size());
for (Map.Entry<String, ComposeServiceImpl> composeServiceEntry : composeEnvironment.getServices()
.entrySet()) {
ComposeServiceImpl service = composeServiceEntry.getValue();
CheServiceImpl cheService = new CheServiceImpl().withCommand(service.getCommand())
.withContainerName(service.getContainerName())
.withDependsOn(service.getDependsOn())
.withEntrypoint(service.getEntrypoint())
.withEnvironment(service.getEnvironment())
.withExpose(service.getExpose())
.withImage(service.getImage())
.withLabels(service.getLabels())
.withLinks(service.getLinks())
.withMemLimit(service.getMemLimit())
.withNetworks(service.getNetworks())
.withPorts(service.getPorts())
.withVolumes(service.getVolumes())
.withVolumesFrom(service.getVolumesFrom());
if (service.getBuild() != null) {
cheService.setBuild(new CheServiceBuildContextImpl().withContext(service.getBuild().getContext())
.withDockerfilePath(service.getBuild().getDockerfile())
.withArgs(service.getBuild().getArgs()));
}
services.put(composeServiceEntry.getKey(), cheService);
}
return new CheServicesEnvironmentImpl(services);
}
}