Add Compose, Dockerfile and DockerImage Environments and factories for them
parent
7f620b2d55
commit
a74466308d
|
|
@ -0,0 +1,93 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
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:
|
||||
Red Hat, Inc. - initial API and implementation
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>che-infrastructures-docker-parent</artifactId>
|
||||
<groupId>org.eclipse.che.infrastructure.docker</groupId>
|
||||
<version>6.0.0-M2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>docker-environment</artifactId>
|
||||
<name>Infrastructure :: Docker :: Environment</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject.extensions</groupId>
|
||||
<artifactId>guice-multibindings</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-installer</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-model</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-workspace</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-workspace-shared</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockitong</groupId>
|
||||
<artifactId>mockitong</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.multibindings.MapBinder;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.ComposeEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.ComposeEnvironmentFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.dockerfile.DockerfileEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.dockerfile.DockerfileEnvironmentFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage.DockerImageEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage.DockerImageEnvironmentFactory;
|
||||
|
||||
/** @author Sergii Leshchenko */
|
||||
public class DockerEnvironmentsModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
MapBinder<String, InternalEnvironmentFactory> factories =
|
||||
MapBinder.newMapBinder(binder(), String.class, InternalEnvironmentFactory.class);
|
||||
|
||||
factories.addBinding(ComposeEnvironment.TYPE).to(ComposeEnvironmentFactory.class);
|
||||
factories.addBinding(DockerImageEnvironment.TYPE).to(DockerImageEnvironmentFactory.class);
|
||||
factories.addBinding(DockerfileEnvironment.TYPE).to(DockerfileEnvironmentFactory.class);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.compose;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import org.eclipse.che.api.core.model.workspace.Warning;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.model.ComposeService;
|
||||
|
||||
/** @author Sergii Leshchenko */
|
||||
public class ComposeEnvironment extends InternalEnvironment {
|
||||
|
||||
public static final String TYPE = "compose";
|
||||
|
||||
private String version;
|
||||
private LinkedHashMap<String, ComposeService> services;
|
||||
|
||||
ComposeEnvironment(
|
||||
String version,
|
||||
LinkedHashMap<String, ComposeService> services,
|
||||
InternalRecipe recipe,
|
||||
Map<String, InternalMachineConfig> machines,
|
||||
List<Warning> warnings) {
|
||||
super(recipe, machines, warnings);
|
||||
this.version = version;
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/** Returns ordered services. */
|
||||
public LinkedHashMap<String, ComposeService> getServices() {
|
||||
if (services == null) {
|
||||
services = new LinkedHashMap<>();
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ComposeEnvironment)) {
|
||||
return false;
|
||||
}
|
||||
final ComposeEnvironment that = (ComposeEnvironment) obj;
|
||||
return Objects.equals(version, that.version)
|
||||
&& Objects.equals(getServices(), that.getServices())
|
||||
&& Objects.equals(getRecipe(), that.getRecipe())
|
||||
&& Objects.equals(getMachines(), that.getMachines())
|
||||
&& Objects.equals(getWarnings(), that.getWarnings());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(version, getServices(), getMachines(), getRecipe(), getWarnings());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ComposeEnvironment{"
|
||||
+ "version='"
|
||||
+ version
|
||||
+ '\''
|
||||
+ ", services="
|
||||
+ getServices()
|
||||
+ ", machines="
|
||||
+ getMachines()
|
||||
+ ", recipe="
|
||||
+ getRecipe()
|
||||
+ ", warnings="
|
||||
+ getWarnings()
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.compose;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.util.stream.Collectors.*;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.Warning;
|
||||
import org.eclipse.che.api.installer.server.InstallerRegistry;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.MachineConfigsValidator;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.RecipeRetriever;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.model.ComposeRecipe;
|
||||
|
||||
/** @author Sergii Leshchenko */
|
||||
@Singleton
|
||||
public class ComposeEnvironmentFactory extends InternalEnvironmentFactory<ComposeEnvironment> {
|
||||
|
||||
private static final ObjectMapper YAML_PARSER = new ObjectMapper(new YAMLFactory());
|
||||
|
||||
private static Set<String> YAML_CONTENT_TYPES =
|
||||
ImmutableSet.of("application/x-yaml", "text/yaml", "text/x-yaml");
|
||||
|
||||
private final ComposeServicesStartStrategy startStrategy;
|
||||
private final ComposeEnvironmentValidator composeValidator;
|
||||
|
||||
@Inject
|
||||
public ComposeEnvironmentFactory(
|
||||
InstallerRegistry installerRegistry,
|
||||
RecipeRetriever recipeRetriever,
|
||||
MachineConfigsValidator machinesValidator,
|
||||
ComposeEnvironmentValidator composeValidator,
|
||||
ComposeServicesStartStrategy startStrategy) {
|
||||
super(installerRegistry, recipeRetriever, machinesValidator);
|
||||
this.startStrategy = startStrategy;
|
||||
this.composeValidator = composeValidator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ComposeEnvironment doCreate(
|
||||
InternalRecipe recipe, Map<String, InternalMachineConfig> machines, List<Warning> warnings)
|
||||
throws InfrastructureException, ValidationException {
|
||||
String contentType = recipe.getContentType();
|
||||
checkNotNull(contentType, "Recipe content type should not be null");
|
||||
|
||||
String recipeContent = recipe.getContent();
|
||||
|
||||
if (!ComposeEnvironment.TYPE.equals(recipe.getType())) {
|
||||
throw new ValidationException(
|
||||
format("Compose environment parser doesn't support recipe type '%s'", recipe.getType()));
|
||||
}
|
||||
|
||||
if (!YAML_CONTENT_TYPES.contains(contentType)) {
|
||||
throw new ValidationException(
|
||||
format(
|
||||
"Provided environment recipe content type '%s' is "
|
||||
+ "unsupported. Supported values are: %s",
|
||||
contentType, YAML_CONTENT_TYPES.stream().collect(joining(", "))));
|
||||
}
|
||||
ComposeRecipe composeRecipe = doParse(recipeContent);
|
||||
|
||||
ComposeEnvironment composeEnvironment =
|
||||
new ComposeEnvironment(
|
||||
composeRecipe.getVersion(),
|
||||
startStrategy.order(composeRecipe.getServices()),
|
||||
recipe,
|
||||
machines,
|
||||
warnings);
|
||||
|
||||
composeValidator.validate(composeEnvironment);
|
||||
|
||||
return composeEnvironment;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ComposeRecipe doParse(String recipeContent) throws ValidationException {
|
||||
ComposeRecipe composeRecipe;
|
||||
try {
|
||||
composeRecipe = YAML_PARSER.readValue(recipeContent, ComposeRecipe.class);
|
||||
} catch (IOException e) {
|
||||
throw new ValidationException(
|
||||
"Parsing of environment configuration failed. " + e.getLocalizedMessage());
|
||||
}
|
||||
return composeRecipe;
|
||||
}
|
||||
|
||||
private static void checkNotNull(
|
||||
Object object, String errorMessageTemplate, Object... errorMessageParams)
|
||||
throws ValidationException {
|
||||
if (object == null) {
|
||||
throw new ValidationException(format(errorMessageTemplate, errorMessageParams));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.compose;
|
||||
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.model.ComposeService;
|
||||
|
||||
/** @author Alexander Garagatyi */
|
||||
class ComposeEnvironmentValidator {
|
||||
private static final String SERVICE_NAME_REGEXP = "[a-zA-Z0-9._-]+";
|
||||
private static final Pattern SERVICE_NAME_PATTERN = Pattern.compile(SERVICE_NAME_REGEXP);
|
||||
|
||||
// DockerContainer syntax patterns
|
||||
/**
|
||||
* Examples:
|
||||
*
|
||||
* <ul>
|
||||
* <li>8080/tcp
|
||||
* <li>8080/udp
|
||||
* <li>8080
|
||||
* <li>8/tcp
|
||||
* <li>8
|
||||
* </ul>
|
||||
*/
|
||||
private static final Pattern EXPOSE_PATTERN = Pattern.compile("^[1-9]+[0-9]*(/(tcp|udp))?$");
|
||||
/**
|
||||
* Examples:
|
||||
*
|
||||
* <ul>
|
||||
* <li>service1
|
||||
* <li>service1:alias1
|
||||
* </ul>
|
||||
*/
|
||||
private static final Pattern LINK_PATTERN =
|
||||
Pattern.compile(
|
||||
"^(?<containerName>" + SERVICE_NAME_REGEXP + ")(:" + SERVICE_NAME_REGEXP + ")?$");
|
||||
|
||||
private static final Pattern VOLUME_FROM_PATTERN =
|
||||
Pattern.compile("^(?<containerName>" + SERVICE_NAME_REGEXP + ")(:(ro|rw))?$");
|
||||
|
||||
void validate(ComposeEnvironment env) throws ValidationException {
|
||||
checkArgument(
|
||||
env.getServices() != null && !env.getServices().isEmpty(),
|
||||
"Environment should contain at least 1 service");
|
||||
|
||||
List<String> missingServices =
|
||||
env.getMachines()
|
||||
.keySet()
|
||||
.stream()
|
||||
.filter(machineName -> !env.getServices().containsKey(machineName))
|
||||
.collect(toList());
|
||||
checkArgument(
|
||||
missingServices.isEmpty(),
|
||||
"Environment contains machines that are missing in environment recipe: %s",
|
||||
Joiner.on(", ").join(missingServices));
|
||||
|
||||
// needed to validate different kinds of dependencies in containers to other containers
|
||||
Set<String> containersNames = env.getServices().keySet();
|
||||
|
||||
for (Map.Entry<String, ComposeService> serviceEntry : env.getServices().entrySet()) {
|
||||
validateService(serviceEntry.getKey(), serviceEntry.getValue(), containersNames);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateService(
|
||||
String serviceName, ComposeService service, Set<String> servicesNames)
|
||||
throws ValidationException {
|
||||
|
||||
checkArgument(
|
||||
SERVICE_NAME_PATTERN.matcher(serviceName).matches(),
|
||||
"Name of service '%s' in environment is invalid",
|
||||
serviceName);
|
||||
|
||||
checkArgument(
|
||||
!isNullOrEmpty(service.getImage())
|
||||
|| (service.getBuild() != null
|
||||
&& (!isNullOrEmpty(service.getBuild().getContext())
|
||||
|| !isNullOrEmpty(service.getBuild().getDockerfile()))),
|
||||
"Field 'image' or 'build.context' is required in service '%s' in environment",
|
||||
serviceName);
|
||||
|
||||
checkArgument(
|
||||
service.getBuild() == null
|
||||
|| (isNullOrEmpty(service.getBuild().getContext())
|
||||
!= isNullOrEmpty(service.getBuild().getDockerfile())),
|
||||
"Service '%s' in environment contains mutually exclusive dockerfile content and build context.",
|
||||
serviceName);
|
||||
|
||||
for (String expose : service.getExpose()) {
|
||||
checkArgument(
|
||||
EXPOSE_PATTERN.matcher(expose).matches(),
|
||||
"Exposed port '%s' in service '%s' in environment is invalid",
|
||||
expose,
|
||||
serviceName);
|
||||
}
|
||||
|
||||
for (String link : service.getLinks()) {
|
||||
Matcher matcher = LINK_PATTERN.matcher(link);
|
||||
|
||||
checkArgument(
|
||||
matcher.matches(),
|
||||
"Link '%s' in service '%s' in environment is invalid",
|
||||
link,
|
||||
serviceName);
|
||||
|
||||
String containerFromLink = matcher.group("containerName");
|
||||
checkArgument(
|
||||
!serviceName.equals(containerFromLink),
|
||||
"Container '%s' has illegal link to itself",
|
||||
serviceName);
|
||||
checkArgument(
|
||||
servicesNames.contains(containerFromLink),
|
||||
"Service '%s' in environment contains link to non existing service '%s'",
|
||||
serviceName,
|
||||
containerFromLink);
|
||||
}
|
||||
|
||||
for (String depends : service.getDependsOn()) {
|
||||
checkArgument(
|
||||
SERVICE_NAME_PATTERN.matcher(depends).matches(),
|
||||
"Dependency '%s' in service '%s' in environment is invalid",
|
||||
depends,
|
||||
serviceName);
|
||||
|
||||
checkArgument(
|
||||
!serviceName.equals(depends),
|
||||
"Container '%s' has illegal dependency to itself",
|
||||
serviceName);
|
||||
checkArgument(
|
||||
servicesNames.contains(depends),
|
||||
"Service '%s' in environment contains dependency to non existing service '%s'",
|
||||
serviceName,
|
||||
depends);
|
||||
}
|
||||
|
||||
for (String volumesFrom : service.getVolumesFrom()) {
|
||||
Matcher matcher = VOLUME_FROM_PATTERN.matcher(volumesFrom);
|
||||
|
||||
checkArgument(
|
||||
matcher.matches(),
|
||||
"Service name '%s' in field 'volumes_from' of service '%s' in environment is invalid",
|
||||
volumesFrom,
|
||||
serviceName);
|
||||
|
||||
String containerFromVolumesFrom = matcher.group("containerName");
|
||||
checkArgument(
|
||||
!serviceName.equals(containerFromVolumesFrom),
|
||||
"Container '%s' can not mount volume from itself",
|
||||
serviceName);
|
||||
checkArgument(
|
||||
servicesNames.contains(containerFromVolumesFrom),
|
||||
"Service '%s' in environment contains non existing service '%s' in 'volumes_from' field",
|
||||
serviceName,
|
||||
containerFromVolumesFrom);
|
||||
}
|
||||
|
||||
checkArgument(
|
||||
service.getPorts() == null || service.getPorts().isEmpty(),
|
||||
"Ports binding is forbidden but found in service '%s' of environment",
|
||||
serviceName);
|
||||
|
||||
checkArgument(
|
||||
service.getVolumes() == null || service.getVolumes().isEmpty(),
|
||||
"Volumes binding is forbidden but found in service '%s' of environment",
|
||||
serviceName);
|
||||
|
||||
checkArgument(
|
||||
service.getNetworks() == null || service.getNetworks().isEmpty(),
|
||||
"Networks configuration is forbidden but found in service '%s' of environment",
|
||||
serviceName);
|
||||
}
|
||||
|
||||
private static void checkArgument(boolean expression, String error) throws ValidationException {
|
||||
if (!expression) {
|
||||
throw new ValidationException(error);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkArgument(
|
||||
boolean expression, String errorMessageTemplate, Object... errorMessageParams)
|
||||
throws ValidationException {
|
||||
if (!expression) {
|
||||
throw new ValidationException(format(errorMessageTemplate, errorMessageParams));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.container;
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.compose;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
|
|
@ -17,14 +17,14 @@ import com.google.common.collect.Sets;
|
|||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerContainerConfig;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.model.ComposeService;
|
||||
|
||||
/**
|
||||
* Finds order of Che containers to start that respects dependencies between containers.
|
||||
|
|
@ -32,74 +32,81 @@ import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
|||
* @author Alexander Garagatyi
|
||||
* @author Alexander Andrienko
|
||||
*/
|
||||
public class ContainersStartStrategy {
|
||||
public class ComposeServicesStartStrategy {
|
||||
/**
|
||||
* Resolves order of start for machines in an environment.
|
||||
* Resolves order of start for compose services in an environment.
|
||||
*
|
||||
* @throws ValidationException if order of machines can not be calculated
|
||||
* @throws ValidationException if order of services can not be calculated
|
||||
*/
|
||||
public List<String> order(DockerEnvironment environment) throws ValidationException {
|
||||
public LinkedHashMap<String, ComposeService> order(Map<String, ComposeService> services)
|
||||
throws ValidationException {
|
||||
|
||||
Map<String, Integer> weights = weightMachines(environment.getContainers());
|
||||
Map<String, Integer> weights = weightServices(services);
|
||||
List<String> orderedServicesNames = sortByWeight(weights);
|
||||
|
||||
return sortByWeight(weights);
|
||||
LinkedHashMap<String, ComposeService> orderedServices = new LinkedHashMap<>();
|
||||
for (String serviceName : orderedServicesNames) {
|
||||
orderedServices.put(serviceName, services.get(serviceName));
|
||||
}
|
||||
|
||||
return orderedServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns mapping of names of machines to its weights in dependency graph.
|
||||
* Returns mapping of names of services to its weights in dependency graph.
|
||||
*
|
||||
* @throws ValidationException if weights of machines can not be calculated
|
||||
* @throws ValidationException if weights of services can not be calculated
|
||||
*/
|
||||
private Map<String, Integer> weightMachines(Map<String, DockerContainerConfig> containers)
|
||||
private Map<String, Integer> weightServices(Map<String, ComposeService> services)
|
||||
throws ValidationException {
|
||||
|
||||
HashMap<String, Integer> weights = new HashMap<>();
|
||||
|
||||
// create machines dependency graph
|
||||
Map<String, Set<String>> dependencies = new HashMap<>(containers.size());
|
||||
for (Map.Entry<String, DockerContainerConfig> containerEntry : containers.entrySet()) {
|
||||
DockerContainerConfig container = containerEntry.getValue();
|
||||
// create services dependency graph
|
||||
Map<String, Set<String>> dependencies = new HashMap<>(services.size());
|
||||
for (Map.Entry<String, ComposeService> containerEntry : services.entrySet()) {
|
||||
ComposeService service = containerEntry.getValue();
|
||||
|
||||
Set<String> machineDependencies =
|
||||
Set<String> serviceDependencies =
|
||||
Sets.newHashSetWithExpectedSize(
|
||||
container.getDependsOn().size()
|
||||
+ container.getLinks().size()
|
||||
+ container.getVolumesFrom().size());
|
||||
service.getDependsOn().size()
|
||||
+ service.getLinks().size()
|
||||
+ service.getVolumesFrom().size());
|
||||
|
||||
for (String dependsOn : container.getDependsOn()) {
|
||||
for (String dependsOn : service.getDependsOn()) {
|
||||
checkDependency(
|
||||
dependsOn, containerEntry.getKey(), containers, "A machine can not depend on itself");
|
||||
machineDependencies.add(dependsOn);
|
||||
dependsOn, containerEntry.getKey(), services, "A service can not depend on itself");
|
||||
serviceDependencies.add(dependsOn);
|
||||
}
|
||||
|
||||
// links also counts as dependencies
|
||||
for (String link : container.getLinks()) {
|
||||
for (String link : service.getLinks()) {
|
||||
String dependency = getContainerFromLink(link);
|
||||
checkDependency(
|
||||
dependency, containerEntry.getKey(), containers, "A machine can not link to itself");
|
||||
machineDependencies.add(dependency);
|
||||
dependency, containerEntry.getKey(), services, "A service can not link to itself");
|
||||
serviceDependencies.add(dependency);
|
||||
}
|
||||
// volumesFrom also counts as dependencies
|
||||
for (String volumesFrom : container.getVolumesFrom()) {
|
||||
for (String volumesFrom : service.getVolumesFrom()) {
|
||||
String dependency = getContainerFromVolumesFrom(volumesFrom);
|
||||
checkDependency(
|
||||
dependency,
|
||||
containerEntry.getKey(),
|
||||
containers,
|
||||
"A machine can not contain 'volumes_from' to itself");
|
||||
machineDependencies.add(dependency);
|
||||
services,
|
||||
"A service can not contain 'volumes_from' to itself");
|
||||
serviceDependencies.add(dependency);
|
||||
}
|
||||
dependencies.put(containerEntry.getKey(), machineDependencies);
|
||||
dependencies.put(containerEntry.getKey(), serviceDependencies);
|
||||
}
|
||||
|
||||
// Find weight of each machine in graph.
|
||||
// Weight of machine is calculated as sum of all weights of machines it depends on.
|
||||
// Find weight of each service in graph.
|
||||
// Weight of service is calculated as sum of all weights of services it depends on.
|
||||
// Nodes with no dependencies gets weight 0
|
||||
while (!dependencies.isEmpty()) {
|
||||
int previousSize = dependencies.size();
|
||||
for (Iterator<Map.Entry<String, Set<String>>> it = dependencies.entrySet().iterator();
|
||||
it.hasNext(); ) {
|
||||
// process not yet processed machines only
|
||||
// process not yet processed services only
|
||||
Map.Entry<String, Set<String>> containerEntry = it.next();
|
||||
String container = containerEntry.getKey();
|
||||
Set<String> containerDependencies = containerEntry.getValue();
|
||||
|
|
@ -109,9 +116,9 @@ public class ContainersStartStrategy {
|
|||
weights.put(container, 0);
|
||||
it.remove();
|
||||
} else {
|
||||
// machine has dependencies - check if it has not weighted dependencies
|
||||
// service has dependencies - check if it has not weighted dependencies
|
||||
if (weights.keySet().containsAll(containerDependencies)) {
|
||||
// all connections are weighted - lets evaluate current machine
|
||||
// all connections are weighted - lets evaluate current service
|
||||
Optional<String> maxWeight =
|
||||
containerDependencies.stream().max(Comparator.comparing(weights::get));
|
||||
// optional can't be empty because size of the list is checked above
|
||||
|
|
@ -123,7 +130,7 @@ public class ContainersStartStrategy {
|
|||
}
|
||||
if (dependencies.size() == previousSize) {
|
||||
throw new ValidationException(
|
||||
"Launch order of machines '"
|
||||
"Launch order of services '"
|
||||
+ Joiner.on(", ").join(dependencies.keySet())
|
||||
+ "' can't be evaluated. Circular dependency.");
|
||||
}
|
||||
|
|
@ -138,7 +145,7 @@ public class ContainersStartStrategy {
|
|||
if (link != null) {
|
||||
String[] split = container.split(":");
|
||||
if (split.length > 2) {
|
||||
throw new ValidationException(format("Container link '%s' is invalid", link));
|
||||
throw new ValidationException(format("Service link '%s' is invalid", link));
|
||||
}
|
||||
container = split[0];
|
||||
}
|
||||
|
|
@ -154,8 +161,7 @@ public class ContainersStartStrategy {
|
|||
if (volumesFrom != null) {
|
||||
String[] split = container.split(":");
|
||||
if (split.length > 2) {
|
||||
throw new ValidationException(
|
||||
format("Container volumes_from '%s' is invalid", volumesFrom));
|
||||
throw new ValidationException(format("Service volumes_from '%s' is invalid", volumesFrom));
|
||||
}
|
||||
container = split[0];
|
||||
}
|
||||
|
|
@ -174,7 +180,7 @@ public class ContainersStartStrategy {
|
|||
private void checkDependency(
|
||||
String dependency,
|
||||
String containerName,
|
||||
Map<String, DockerContainerConfig> containers,
|
||||
Map<String, ComposeService> containers,
|
||||
String errorMessage)
|
||||
throws ValidationException {
|
||||
if (containerName.equals(dependency)) {
|
||||
|
|
@ -183,7 +189,7 @@ public class ContainersStartStrategy {
|
|||
if (!containers.containsKey(dependency)) {
|
||||
throw new ValidationException(
|
||||
format(
|
||||
"Dependency '%s' in machine '%s' points to unknown machine.",
|
||||
"Dependency '%s' in service '%s' points to unknown service.",
|
||||
dependency, containerName));
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ import com.fasterxml.jackson.databind.node.TextNode;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.ComposeEnvironmentParser;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.ComposeEnvironmentFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.model.ComposeService;
|
||||
|
||||
/**
|
||||
|
|
@ -54,7 +54,7 @@ import org.eclipse.che.workspace.infrastructure.docker.environment.compose.model
|
|||
* </pre>
|
||||
* </ul>
|
||||
*
|
||||
* See more for parsing compose file {@link ComposeEnvironmentParser}.
|
||||
* See more for parsing compose file {@link ComposeEnvironmentFactory}.
|
||||
*
|
||||
* <p>Note: this deserializer works for json too.
|
||||
*
|
||||
|
|
@ -13,7 +13,6 @@ package org.eclipse.che.workspace.infrastructure.docker.environment.compose.dese
|
|||
import static java.lang.String.format;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import java.io.IOException;
|
||||
|
|
@ -33,7 +32,7 @@ public class EnvironmentDeserializer extends JsonDeserializer<Map<String, String
|
|||
|
||||
@Override
|
||||
public Map<String, String> deserialize(JsonParser jsonParser, DeserializationContext ctxt)
|
||||
throws IOException, JsonProcessingException {
|
||||
throws IOException {
|
||||
Object environment = jsonParser.readValueAs(Object.class);
|
||||
|
||||
/* Parse dictionary in view of:
|
||||
|
|
@ -20,13 +20,13 @@ import java.util.stream.Collectors;
|
|||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class ComposeEnvironment {
|
||||
public class ComposeRecipe {
|
||||
private String version;
|
||||
private Map<String, ComposeService> services;
|
||||
|
||||
public ComposeEnvironment() {}
|
||||
public ComposeRecipe() {}
|
||||
|
||||
public ComposeEnvironment(ComposeEnvironment environment) {
|
||||
public ComposeRecipe(ComposeRecipe environment) {
|
||||
version = environment.getVersion();
|
||||
if (environment.getServices() != null) {
|
||||
services =
|
||||
|
|
@ -49,7 +49,7 @@ public class ComposeEnvironment {
|
|||
this.version = version;
|
||||
}
|
||||
|
||||
public ComposeEnvironment withVersion(String version) {
|
||||
public ComposeRecipe withVersion(String version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ public class ComposeEnvironment {
|
|||
this.services = services;
|
||||
}
|
||||
|
||||
public ComposeEnvironment withServices(Map<String, ComposeService> services) {
|
||||
public ComposeRecipe withServices(Map<String, ComposeService> services) {
|
||||
this.services = services;
|
||||
return this;
|
||||
}
|
||||
|
|
@ -74,8 +74,8 @@ public class ComposeEnvironment {
|
|||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ComposeEnvironment)) return false;
|
||||
ComposeEnvironment that = (ComposeEnvironment) o;
|
||||
if (!(o instanceof ComposeRecipe)) return false;
|
||||
ComposeRecipe that = (ComposeRecipe) o;
|
||||
return Objects.equals(version, that.version) && Objects.equals(services, that.services);
|
||||
}
|
||||
|
||||
|
|
@ -86,6 +86,6 @@ public class ComposeEnvironment {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ComposeEnvironment{" + "version='" + version + '\'' + ", services=" + services + '}';
|
||||
return "ComposeRecipe{" + "version='" + version + '\'' + ", services=" + services + '}';
|
||||
}
|
||||
}
|
||||
|
|
@ -12,13 +12,16 @@ package org.eclipse.che.workspace.infrastructure.docker.environment.compose.mode
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.deserializer.CommandDeserializer;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.deserializer.EnvironmentDeserializer;
|
||||
|
||||
|
|
@ -43,7 +46,7 @@ public class ComposeService {
|
|||
@JsonDeserialize(using = EnvironmentDeserializer.class)
|
||||
private Map<String, String> environment;
|
||||
|
||||
private Set<String> expose = new HashSet<>();
|
||||
private Set<String> expose;
|
||||
private List<String> ports;
|
||||
private Map<String, String> labels;
|
||||
private List<String> links;
|
||||
|
|
@ -84,6 +87,7 @@ public class ComposeService {
|
|||
if (service.getLabels() != null) {
|
||||
labels = new HashMap<>(service.getLabels());
|
||||
}
|
||||
|
||||
this.setExpose(service.getExpose());
|
||||
|
||||
if (service.getPorts() != null) {
|
||||
|
|
@ -256,32 +260,43 @@ public class ComposeService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Expose ports without publishing them to the host machine - they’ll only be accessible to linked
|
||||
* services.
|
||||
* Immutable expose ports list without publishing them to the host machine - they’ll only be
|
||||
* accessible to linked services.
|
||||
*
|
||||
* <p>Only the internal port can be specified. <br>
|
||||
* Examples:
|
||||
*
|
||||
* <ul>
|
||||
* <li>3000
|
||||
* <li>8000
|
||||
* <li>3000/tcp
|
||||
* <li>8000/udp
|
||||
* </ul>
|
||||
*/
|
||||
public Set<String> getExpose() {
|
||||
// if (expose == null) {
|
||||
// expose = new ArrayList<>();
|
||||
// }
|
||||
return expose;
|
||||
if (expose == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return ImmutableSet.copyOf(expose);
|
||||
}
|
||||
|
||||
public void setExpose(Set<String> expose) {
|
||||
for(String exp : expose) {
|
||||
addExpose(exp);
|
||||
if (expose == null) {
|
||||
this.expose = null;
|
||||
} else {
|
||||
this.expose =
|
||||
expose
|
||||
.stream()
|
||||
.map(this::normalizeExposeValue)
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
}
|
||||
}
|
||||
|
||||
public void addExpose(String exposeToAdd) {
|
||||
expose.add(exposeToAdd.contains("/") ? exposeToAdd : exposeToAdd + "/tcp");
|
||||
private String normalizeExposeValue(String expose) {
|
||||
return expose.contains("/") ? expose : expose + "/tcp";
|
||||
}
|
||||
|
||||
public ComposeService withExpose(Set<String> expose) {
|
||||
setExpose(expose);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.dockerfile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import org.eclipse.che.api.core.model.workspace.Warning;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
|
||||
/** @author Sergii Leshchenko */
|
||||
public class DockerfileEnvironment extends InternalEnvironment {
|
||||
public static final String TYPE = "dockerfile";
|
||||
|
||||
private final String dockerfileContent;
|
||||
|
||||
DockerfileEnvironment(
|
||||
String dockerfileContent,
|
||||
InternalRecipe recipe,
|
||||
Map<String, InternalMachineConfig> machines,
|
||||
List<Warning> warnings) {
|
||||
super(recipe, machines, warnings);
|
||||
this.dockerfileContent = dockerfileContent;
|
||||
}
|
||||
|
||||
/** Returns the content of dockerfile. */
|
||||
public String getDockerfileContent() {
|
||||
return dockerfileContent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof DockerfileEnvironment)) {
|
||||
return false;
|
||||
}
|
||||
final DockerfileEnvironment that = (DockerfileEnvironment) obj;
|
||||
return Objects.equals(dockerfileContent, that.dockerfileContent)
|
||||
&& Objects.equals(getRecipe(), that.getRecipe())
|
||||
&& Objects.equals(getMachines(), that.getMachines())
|
||||
&& Objects.equals(getWarnings(), that.getWarnings());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(dockerfileContent, getRecipe(), getMachines(), getWarnings());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DockerfileEnvironment{"
|
||||
+ "dockerfile='"
|
||||
+ dockerfileContent
|
||||
+ '\''
|
||||
+ ", machines="
|
||||
+ getMachines()
|
||||
+ ", recipe="
|
||||
+ getRecipe()
|
||||
+ ", warnings="
|
||||
+ getWarnings()
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.dockerfile;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.Warning;
|
||||
import org.eclipse.che.api.installer.server.InstallerRegistry;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.MachineConfigsValidator;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.RecipeRetriever;
|
||||
|
||||
/** @author Sergii Leshchenko */
|
||||
@Singleton
|
||||
public class DockerfileEnvironmentFactory
|
||||
extends InternalEnvironmentFactory<DockerfileEnvironment> {
|
||||
|
||||
@Inject
|
||||
public DockerfileEnvironmentFactory(
|
||||
InstallerRegistry installerRegistry,
|
||||
RecipeRetriever recipeRetriever,
|
||||
MachineConfigsValidator machinesValidator) {
|
||||
super(installerRegistry, recipeRetriever, machinesValidator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DockerfileEnvironment doCreate(
|
||||
InternalRecipe recipe, Map<String, InternalMachineConfig> machines, List<Warning> warnings)
|
||||
throws InfrastructureException, ValidationException {
|
||||
if (!DockerfileEnvironment.TYPE.equals(recipe.getType())) {
|
||||
throw new ValidationException(
|
||||
format(
|
||||
"Dockerfile environment parser doesn't support recipe type '%s'", recipe.getType()));
|
||||
}
|
||||
String dockerfile = recipe.getContent();
|
||||
|
||||
checkArgument(dockerfile != null, "Dockerfile content should not be null.");
|
||||
|
||||
return new DockerfileEnvironment(dockerfile, recipe, machines, warnings);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import org.eclipse.che.api.core.model.workspace.Warning;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
|
||||
/** @author Sergii Leshchenko */
|
||||
public class DockerImageEnvironment extends InternalEnvironment {
|
||||
public static final String TYPE = "dockerimage";
|
||||
|
||||
private final String dockerImage;
|
||||
|
||||
DockerImageEnvironment(
|
||||
String dockerImage,
|
||||
InternalRecipe recipe,
|
||||
Map<String, InternalMachineConfig> machines,
|
||||
List<Warning> warnings) {
|
||||
super(recipe, machines, warnings);
|
||||
this.dockerImage = dockerImage;
|
||||
}
|
||||
|
||||
public String getDockerImage() {
|
||||
return dockerImage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof DockerImageEnvironment)) {
|
||||
return false;
|
||||
}
|
||||
final DockerImageEnvironment that = (DockerImageEnvironment) obj;
|
||||
return Objects.equals(dockerImage, that.dockerImage)
|
||||
&& Objects.equals(getRecipe(), that.getRecipe())
|
||||
&& Objects.equals(getMachines(), that.getMachines())
|
||||
&& Objects.equals(getWarnings(), that.getWarnings());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(dockerImage, getRecipe(), getMachines(), getWarnings());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DockerfileEnvironment{"
|
||||
+ "dockerImage='"
|
||||
+ dockerImage
|
||||
+ '\''
|
||||
+ ", machines="
|
||||
+ getMachines()
|
||||
+ ", recipe="
|
||||
+ getRecipe()
|
||||
+ ", warnings="
|
||||
+ getWarnings()
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.Warning;
|
||||
import org.eclipse.che.api.core.model.workspace.config.Environment;
|
||||
import org.eclipse.che.api.installer.server.InstallerRegistry;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.EnvironmentImpl;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.MachineConfigsValidator;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.RecipeRetriever;
|
||||
|
||||
/** @author Sergii Leshchenko */
|
||||
@Singleton
|
||||
public class DockerImageEnvironmentFactory
|
||||
extends InternalEnvironmentFactory<DockerImageEnvironment> {
|
||||
|
||||
@Inject
|
||||
public DockerImageEnvironmentFactory(
|
||||
InstallerRegistry installerRegistry,
|
||||
RecipeRetriever recipeRetriever,
|
||||
MachineConfigsValidator machinesValidator) {
|
||||
super(installerRegistry, recipeRetriever, machinesValidator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DockerImageEnvironment create(Environment sourceEnv)
|
||||
throws InfrastructureException, ValidationException {
|
||||
if (sourceEnv.getRecipe().getLocation() != null) {
|
||||
// move image from location to content
|
||||
EnvironmentImpl envCopy = new EnvironmentImpl(sourceEnv);
|
||||
envCopy.getRecipe().setContent(sourceEnv.getRecipe().getLocation());
|
||||
envCopy.getRecipe().setLocation(null);
|
||||
return super.create(envCopy);
|
||||
}
|
||||
return super.create(sourceEnv);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DockerImageEnvironment doCreate(
|
||||
InternalRecipe recipe, Map<String, InternalMachineConfig> machines, List<Warning> warnings)
|
||||
throws InfrastructureException, ValidationException {
|
||||
if (!DockerImageEnvironment.TYPE.equals(recipe.getType())) {
|
||||
throw new ValidationException(
|
||||
format(
|
||||
"Docker image environment parser doesn't support recipe type '%s'",
|
||||
recipe.getType()));
|
||||
}
|
||||
|
||||
String dockerImage = recipe.getContent();
|
||||
|
||||
checkArgument(dockerImage != null, "Docker image should not be null.");
|
||||
|
||||
return new DockerImageEnvironment(dockerImage, recipe, machines, warnings);
|
||||
}
|
||||
}
|
||||
|
|
@ -22,22 +22,6 @@
|
|||
<artifactId>infrastructure-docker</artifactId>
|
||||
<name>Infrastructure :: Docker</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
|
|
@ -118,6 +102,10 @@
|
|||
<groupId>org.eclipse.che.infrastructure.docker</groupId>
|
||||
<artifactId>docker-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.infrastructure.docker</groupId>
|
||||
<artifactId>docker-environment</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.compose;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.Warning;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
||||
|
||||
/**
|
||||
* @author Alexander Garagatyi
|
||||
* @author Alexander Andrienko
|
||||
*/
|
||||
public class ComposeInternalEnvironment extends InternalEnvironment {
|
||||
|
||||
private final DockerEnvironment composeEnvironment;
|
||||
|
||||
public ComposeInternalEnvironment(
|
||||
Map<String, InternalMachineConfig> machines,
|
||||
InternalRecipe recipe,
|
||||
List<Warning> warnings,
|
||||
DockerEnvironment composeEnvironment)
|
||||
throws ValidationException {
|
||||
super(machines, recipe, warnings);
|
||||
this.composeEnvironment = composeEnvironment;
|
||||
}
|
||||
|
||||
public DockerEnvironment getComposeEnvironment() {
|
||||
return composeEnvironment;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.compose;
|
||||
|
||||
import static org.eclipse.che.workspace.infrastructure.docker.ArgumentsValidator.checkNotNull;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.Maps;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.Warning;
|
||||
import org.eclipse.che.api.installer.server.InstallerRegistry;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.RecipeRetriever;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.model.ComposeEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.compose.model.ComposeService;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerBuildContext;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerContainerConfig;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
||||
|
||||
/**
|
||||
* @author Alexander Garagatyi
|
||||
* @author Alexander Andrienko
|
||||
*/
|
||||
public class ComposeInternalEnvironmentFactory extends InternalEnvironmentFactory {
|
||||
|
||||
public static final String TYPE = "compose";
|
||||
private static final ObjectMapper YAML_PARSER = new ObjectMapper(new YAMLFactory());
|
||||
|
||||
@Inject
|
||||
public ComposeInternalEnvironmentFactory(
|
||||
InstallerRegistry installerRegistry, RecipeRetriever recipeRetriever) {
|
||||
super(installerRegistry, recipeRetriever);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InternalEnvironment create(
|
||||
Map<String, InternalMachineConfig> machines, InternalRecipe recipe, List<Warning> warnings)
|
||||
throws InfrastructureException, ValidationException {
|
||||
DockerEnvironment dockerEnvironment =
|
||||
asDockerEnvironment(parse(recipe.getContent(), recipe.getContentType()));
|
||||
|
||||
return new ComposeInternalEnvironment(machines, recipe, warnings, dockerEnvironment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses compose file into Docker Compose model.
|
||||
*
|
||||
* @param recipeContent compose file to parse
|
||||
* @throws ValidationException when environment or environment recipe is invalid
|
||||
*/
|
||||
@VisibleForTesting
|
||||
ComposeEnvironment parse(String recipeContent, String contentType) throws ValidationException {
|
||||
checkNotNull(recipeContent, "Recipe content should not be null");
|
||||
checkNotNull(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 ValidationException(
|
||||
"Parsing of environment configuration failed. " + e.getLocalizedMessage());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ValidationException(
|
||||
"Provided environment recipe content type '"
|
||||
+ contentType
|
||||
+ "' is unsupported. Supported values are: "
|
||||
+ "application/x-yaml, text/yaml, text/x-yaml");
|
||||
}
|
||||
return composeEnvironment;
|
||||
}
|
||||
|
||||
private DockerEnvironment asDockerEnvironment(ComposeEnvironment composeEnvironment) {
|
||||
Map<String, DockerContainerConfig> containers =
|
||||
Maps.newHashMapWithExpectedSize(composeEnvironment.getServices().size());
|
||||
for (Map.Entry<String, ComposeService> composeServiceEntry :
|
||||
composeEnvironment.getServices().entrySet()) {
|
||||
ComposeService service = composeServiceEntry.getValue();
|
||||
|
||||
DockerContainerConfig cheContainer =
|
||||
new DockerContainerConfig()
|
||||
.setCommand(service.getCommand())
|
||||
.setContainerName(service.getContainerName())
|
||||
.setDependsOn(service.getDependsOn())
|
||||
.setEntrypoint(service.getEntrypoint())
|
||||
.setEnvironment(service.getEnvironment())
|
||||
.setExpose(service.getExpose())
|
||||
.setImage(service.getImage())
|
||||
.setLabels(service.getLabels())
|
||||
.setLinks(service.getLinks())
|
||||
.setMemLimit(service.getMemLimit())
|
||||
.setNetworks(service.getNetworks())
|
||||
.setPorts(service.getPorts())
|
||||
.setVolumes(service.getVolumes())
|
||||
.setVolumesFrom(service.getVolumesFrom());
|
||||
|
||||
if (service.getBuild() != null) {
|
||||
cheContainer.setBuild(
|
||||
new DockerBuildContext()
|
||||
.setContext(service.getBuild().getContext())
|
||||
.setDockerfilePath(service.getBuild().getDockerfile())
|
||||
.setArgs(service.getBuild().getArgs()));
|
||||
}
|
||||
|
||||
containers.put(composeServiceEntry.getKey(), cheContainer);
|
||||
}
|
||||
return new DockerEnvironment().setContainers(containers);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.dockerfile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.Warning;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
||||
|
||||
/**
|
||||
* Dockerfile Internal Environment
|
||||
*/
|
||||
public class DockerfileInternalEnvironment extends InternalEnvironment {
|
||||
|
||||
private final DockerEnvironment dockerEnvironment;
|
||||
|
||||
public DockerfileInternalEnvironment(
|
||||
Map<String, InternalMachineConfig> machines,
|
||||
InternalRecipe recipe,
|
||||
List<Warning> warnings,
|
||||
DockerEnvironment dockerEnvironment)
|
||||
throws InfrastructureException, ValidationException {
|
||||
super(machines, recipe, warnings);
|
||||
this.dockerEnvironment = dockerEnvironment;
|
||||
}
|
||||
|
||||
public DockerEnvironment getDockerEnvironment() {
|
||||
return dockerEnvironment;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.dockerfile;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.Warning;
|
||||
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
|
||||
import org.eclipse.che.api.installer.server.InstallerRegistry;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.RecipeRetriever;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.container.ContainersStartStrategy;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.EnvironmentValidator;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerBuildContext;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerContainerConfig;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
||||
|
||||
/**
|
||||
* @author Alexander Garagatyi
|
||||
* @author Alexander Andrienko
|
||||
*/
|
||||
public class DockerfileInternalEnvironmentFactory extends InternalEnvironmentFactory {
|
||||
|
||||
public static final String TYPE = "dockerfile";
|
||||
public static final String CONTENT_TYPE = "text/x-dockerfile";
|
||||
|
||||
private final EnvironmentValidator validator;
|
||||
private final ContainersStartStrategy startStrategy;
|
||||
|
||||
@Inject
|
||||
public DockerfileInternalEnvironmentFactory(
|
||||
InstallerRegistry installerRegistry,
|
||||
RecipeRetriever recipeRetriever,
|
||||
EnvironmentValidator validator,
|
||||
ContainersStartStrategy startStrategy) {
|
||||
super(installerRegistry, recipeRetriever);
|
||||
this.startStrategy = startStrategy;
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InternalEnvironment create(
|
||||
Map<String, InternalMachineConfig> machines, InternalRecipe recipe, List<Warning> warnings)
|
||||
throws InfrastructureException, ValidationException {
|
||||
|
||||
DockerEnvironment dockerEnvironment = dockerEnv(machines, recipe);
|
||||
validator.validate(machines, dockerEnvironment);
|
||||
return new DockerfileInternalEnvironment(machines, recipe, warnings, dockerEnvironment);
|
||||
}
|
||||
|
||||
private DockerEnvironment dockerEnv(
|
||||
Map<String, InternalMachineConfig> machines, InternalRecipe recipe)
|
||||
throws ValidationException {
|
||||
if (!TYPE.equals(recipe.getType())) {
|
||||
throw new ValidationException(
|
||||
format(
|
||||
"Dockerfile environment parser doesn't support recipe type '%s'", recipe.getType()));
|
||||
}
|
||||
|
||||
if (!CONTENT_TYPE.equals(recipe.getContentType())) {
|
||||
throw new ValidationException(
|
||||
format(
|
||||
"Content type '%s' of recipe of environment is unsupported."
|
||||
+ " Supported values are: text/x-dockerfile",
|
||||
recipe.getContentType()));
|
||||
}
|
||||
|
||||
Map.Entry<String, InternalMachineConfig> entry = machines.entrySet().iterator().next();
|
||||
String machineName = entry.getKey();
|
||||
InternalMachineConfig machineConfig = entry.getValue();
|
||||
|
||||
DockerEnvironment cheContainerEnv = new DockerEnvironment();
|
||||
DockerContainerConfig container = new DockerContainerConfig();
|
||||
cheContainerEnv.getContainers().put(entry.getKey(), container);
|
||||
container.setBuild(new DockerBuildContext().setDockerfileContent(recipe.getContent()));
|
||||
|
||||
for (ServerConfig server : machineConfig.getServers().values()) {
|
||||
container.addExpose(server.getPort());
|
||||
}
|
||||
if (machineConfig.getAttributes().containsKey("memoryLimitBytes")) {
|
||||
try {
|
||||
container.setMemLimit(
|
||||
Long.parseLong(machineConfig.getAttributes().get("memoryLimitBytes")));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ValidationException(
|
||||
format("Value of attribute 'memoryLimitBytes' of machine '%s' is illegal", machineName));
|
||||
}
|
||||
}
|
||||
|
||||
return cheContainerEnv;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.Warning;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
||||
|
||||
/**
|
||||
* Dockerimage Internal Environment
|
||||
*/
|
||||
public class DockerimageInternalEnvironment extends InternalEnvironment {
|
||||
|
||||
private final DockerEnvironment dockerEnvironment;
|
||||
|
||||
public DockerimageInternalEnvironment(
|
||||
Map<String, InternalMachineConfig> machines,
|
||||
InternalRecipe recipe,
|
||||
List<Warning> warnings,
|
||||
DockerEnvironment dockerEnvironment)
|
||||
throws InfrastructureException, ValidationException {
|
||||
super(machines, recipe, warnings);
|
||||
this.dockerEnvironment = dockerEnvironment;
|
||||
}
|
||||
|
||||
public DockerEnvironment getDockerEnvironment() {
|
||||
return dockerEnvironment;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2017 Red Hat, Inc.
|
||||
* 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:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import org.eclipse.che.api.core.ValidationException;
|
||||
import org.eclipse.che.api.core.model.workspace.Warning;
|
||||
import org.eclipse.che.api.core.model.workspace.config.Environment;
|
||||
import org.eclipse.che.api.core.model.workspace.config.ServerConfig;
|
||||
import org.eclipse.che.api.installer.server.InstallerRegistry;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.EnvironmentImpl;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironmentFactory;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.RecipeRetriever;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.environment.EnvironmentValidator;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerContainerConfig;
|
||||
import org.eclipse.che.workspace.infrastructure.docker.model.DockerEnvironment;
|
||||
|
||||
/**
|
||||
* @author Alexander Garagatyi
|
||||
* @author Alexander Andrienko
|
||||
*/
|
||||
public class DockerimageInternalEnvironmentFactory extends InternalEnvironmentFactory {
|
||||
|
||||
public static final String TYPE = "dockerimage";
|
||||
|
||||
private final EnvironmentValidator validator;
|
||||
|
||||
@Inject
|
||||
public DockerimageInternalEnvironmentFactory(
|
||||
InstallerRegistry installerRegistry,
|
||||
RecipeRetriever recipeRetriever,
|
||||
EnvironmentValidator validator) {
|
||||
super(installerRegistry, recipeRetriever);
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalEnvironment create(final Environment environment)
|
||||
throws InfrastructureException, ValidationException {
|
||||
|
||||
EnvironmentImpl envCopy = new EnvironmentImpl(environment);
|
||||
if (envCopy.getRecipe().getLocation() != null) {
|
||||
// move image from location to content
|
||||
envCopy.getRecipe().setContent(environment.getRecipe().getLocation());
|
||||
envCopy.getRecipe().setLocation(null);
|
||||
}
|
||||
return super.create(envCopy);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InternalEnvironment create(
|
||||
Map<String, InternalMachineConfig> machines, InternalRecipe recipe, List<Warning> warnings)
|
||||
throws InfrastructureException, ValidationException {
|
||||
|
||||
DockerEnvironment dockerEnvironment = dockerEnv(machines, recipe);
|
||||
validator.validate(machines, dockerEnvironment);
|
||||
return new DockerimageInternalEnvironment(machines, recipe, warnings, dockerEnvironment);
|
||||
}
|
||||
|
||||
private DockerEnvironment dockerEnv(
|
||||
Map<String, InternalMachineConfig> machines, InternalRecipe recipe)
|
||||
throws ValidationException {
|
||||
|
||||
// empty list of machines is not expected, no needs to check for next()
|
||||
Map.Entry<String, InternalMachineConfig> entry = machines.entrySet().iterator().next();
|
||||
String machineName = entry.getKey();
|
||||
InternalMachineConfig machineConfig = entry.getValue();
|
||||
|
||||
DockerEnvironment dockerEnv = new DockerEnvironment();
|
||||
DockerContainerConfig container = new DockerContainerConfig();
|
||||
dockerEnv.getContainers().put(machineName, container);
|
||||
|
||||
container.setImage(recipe.getContent());
|
||||
for (ServerConfig server : machineConfig.getServers().values()) {
|
||||
container.addExpose(server.getPort());
|
||||
}
|
||||
if (machineConfig.getAttributes().containsKey("memoryLimitBytes")) {
|
||||
try {
|
||||
container.setMemLimit(
|
||||
Long.parseLong(machineConfig.getAttributes().get("memoryLimitBytes")));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ValidationException(
|
||||
format("Value of attribute 'memoryLimitBytes' of machine '%s' is illegal", machineName));
|
||||
}
|
||||
}
|
||||
|
||||
return dockerEnv;
|
||||
}
|
||||
}
|
||||
|
|
@ -10,13 +10,16 @@
|
|||
*/
|
||||
package org.eclipse.che.workspace.infrastructure.docker.model;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.eclipse.che.commons.annotation.Nullable;
|
||||
|
||||
/**
|
||||
|
|
@ -37,7 +40,7 @@ public class DockerContainerConfig {
|
|||
private List<String> securityOpt;
|
||||
private List<String> entrypoint;
|
||||
private Map<String, String> environment;
|
||||
private Set<String> expose = new HashSet<>();
|
||||
private Set<String> expose;
|
||||
private List<String> extraHosts;
|
||||
private String id;
|
||||
private String image;
|
||||
|
|
@ -82,7 +85,9 @@ public class DockerContainerConfig {
|
|||
if (container.getEnvironment() != null) {
|
||||
environment = new HashMap<>(container.getEnvironment());
|
||||
}
|
||||
|
||||
setExpose(container.getExpose());
|
||||
|
||||
if (container.getExtraHosts() != null) {
|
||||
extraHosts = new ArrayList<>(container.getExtraHosts());
|
||||
}
|
||||
|
|
@ -277,19 +282,27 @@ public class DockerContainerConfig {
|
|||
* </ul>
|
||||
*/
|
||||
public Set<String> getExpose() {
|
||||
return expose;
|
||||
if (expose == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return ImmutableSet.copyOf(expose);
|
||||
}
|
||||
|
||||
public DockerContainerConfig setExpose(Set<String> expose) {
|
||||
for(String exp : expose) {
|
||||
addExpose(exp);
|
||||
if (expose == null) {
|
||||
this.expose = null;
|
||||
} else {
|
||||
this.expose =
|
||||
expose
|
||||
.stream()
|
||||
.map(this::normalizeExposeValue)
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public DockerContainerConfig addExpose(String exposeToAdd) {
|
||||
expose.add(exposeToAdd.contains("/") ? exposeToAdd : exposeToAdd + "/tcp");
|
||||
return this;
|
||||
private String normalizeExposeValue(String expose) {
|
||||
return expose.contains("/") ? expose : expose + "/tcp";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -13,21 +13,46 @@ package org.eclipse.che.workspace.infrastructure.docker.model;
|
|||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import org.eclipse.che.api.core.model.workspace.Warning;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig;
|
||||
import org.eclipse.che.api.workspace.server.spi.environment.InternalRecipe;
|
||||
|
||||
/**
|
||||
* Description of docker container environment as representation of environment of machines in Che.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class DockerEnvironment {
|
||||
public class DockerEnvironment extends InternalEnvironment {
|
||||
private Map<String, DockerContainerConfig> containers;
|
||||
private String network;
|
||||
|
||||
public DockerEnvironment() {}
|
||||
|
||||
public DockerEnvironment(DockerEnvironment environment) {
|
||||
public DockerEnvironment(
|
||||
InternalRecipe recipe, Map<String, InternalMachineConfig> machines, List<Warning> warnings) {
|
||||
super(recipe, machines, warnings);
|
||||
}
|
||||
|
||||
public DockerEnvironment(
|
||||
InternalRecipe recipe,
|
||||
Map<String, InternalMachineConfig> machines,
|
||||
List<Warning> warnings,
|
||||
Map<String, DockerContainerConfig> containers,
|
||||
String network)
|
||||
throws InfrastructureException {
|
||||
super(recipe, machines, warnings);
|
||||
this.containers = containers;
|
||||
this.network = network;
|
||||
}
|
||||
|
||||
public DockerEnvironment(DockerEnvironment environment) throws InfrastructureException {
|
||||
super(environment.getRecipe(), environment.getMachines(), environment.getWarnings());
|
||||
if (environment.getContainers() != null) {
|
||||
containers =
|
||||
environment
|
||||
|
|
@ -39,10 +64,10 @@ public class DockerEnvironment {
|
|||
}
|
||||
}
|
||||
|
||||
/** Mapping of containers names to containers configuration. */
|
||||
/** Ordered mapping of containers names to containers configuration. */
|
||||
public Map<String, DockerContainerConfig> getContainers() {
|
||||
if (containers == null) {
|
||||
containers = new HashMap<>();
|
||||
containers = new LinkedHashMap<>();
|
||||
}
|
||||
return containers;
|
||||
}
|
||||
|
|
@ -66,20 +91,39 @@ public class DockerEnvironment {
|
|||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof DockerEnvironment)) return false;
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof DockerEnvironment)) {
|
||||
return false;
|
||||
}
|
||||
DockerEnvironment that = (DockerEnvironment) o;
|
||||
return Objects.equals(getContainers(), that.getContainers())
|
||||
&& Objects.equals(getNetwork(), that.getNetwork());
|
||||
&& Objects.equals(getNetwork(), that.getNetwork())
|
||||
&& Objects.equals(getRecipe(), that.getRecipe())
|
||||
&& Objects.equals(getMachines(), that.getMachines())
|
||||
&& Objects.equals(getWarnings(), that.getWarnings());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getContainers(), getNetwork());
|
||||
return Objects.hash(getContainers(), getNetwork(), getMachines(), getRecipe(), getWarnings());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DockerEnvironment{" + "containers=" + containers + ", network='" + network + '\'' + '}';
|
||||
return "DockerEnvironment{"
|
||||
+ "containers="
|
||||
+ containers
|
||||
+ ", network='"
|
||||
+ network
|
||||
+ '\''
|
||||
+ ", machines="
|
||||
+ getMachines()
|
||||
+ ", recipe="
|
||||
+ getRecipe()
|
||||
+ ", warnings="
|
||||
+ getWarnings()
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
<name>Infrastructure :: Docker :: Parent</name>
|
||||
<modules>
|
||||
<module>docker-client</module>
|
||||
<module>environment</module>
|
||||
<module>infrastructure</module>
|
||||
</modules>
|
||||
<properties>
|
||||
|
|
|
|||
5
pom.xml
5
pom.xml
|
|
@ -723,6 +723,11 @@
|
|||
<artifactId>docker-client</artifactId>
|
||||
<version>${che.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.infrastructure.docker</groupId>
|
||||
<artifactId>docker-environment</artifactId>
|
||||
<version>${che.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.infrastructure.docker</groupId>
|
||||
<artifactId>infrastructure-docker</artifactId>
|
||||
|
|
|
|||
Loading…
Reference in New Issue