From a74466308d705e95bae3cd6f5ed2c74ed2b03261 Mon Sep 17 00:00:00 2001 From: Sergii Leshchenko Date: Thu, 16 Nov 2017 12:01:52 +0200 Subject: [PATCH] Add Compose, Dockerfile and DockerImage Environments and factories for them --- infrastructures/docker/environment/pom.xml | 93 ++++++++ .../environment/DockerEnvironmentsModule.java | 34 +++ .../compose/ComposeEnvironment.java | 91 ++++++++ .../compose/ComposeEnvironmentFactory.java | 116 ++++++++++ .../compose/ComposeEnvironmentValidator.java | 204 ++++++++++++++++++ .../ComposeServicesStartStrategy.java} | 90 ++++---- .../deserializer/CommandDeserializer.java | 4 +- .../deserializer/EnvironmentDeserializer.java | 3 +- .../compose/model/BuildContext.java | 0 .../compose/model/ComposeRecipe.java} | 16 +- .../compose/model/ComposeService.java | 41 ++-- .../dockerfile/DockerfileEnvironment.java | 75 +++++++ .../DockerfileEnvironmentFactory.java | 58 +++++ .../dockerimage/DockerImageEnvironment.java | 74 +++++++ .../DockerImageEnvironmentFactory.java | 75 +++++++ infrastructures/docker/infrastructure/pom.xml | 20 +- .../compose/ComposeInternalEnvironment.java | 43 ---- .../ComposeInternalEnvironmentFactory.java | 132 ------------ .../DockerfileInternalEnvironment.java | 43 ---- .../DockerfileInternalEnvironmentFactory.java | 108 ---------- .../DockerimageInternalEnvironment.java | 43 ---- ...DockerimageInternalEnvironmentFactory.java | 105 --------- .../docker/model/DockerContainerConfig.java | 27 ++- .../docker/model/DockerEnvironment.java | 62 +++++- infrastructures/docker/pom.xml | 1 + pom.xml | 5 + 26 files changed, 990 insertions(+), 573 deletions(-) create mode 100644 infrastructures/docker/environment/pom.xml create mode 100644 infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/DockerEnvironmentsModule.java create mode 100644 infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeEnvironment.java create mode 100644 infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeEnvironmentFactory.java create mode 100644 infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeEnvironmentValidator.java rename infrastructures/docker/{infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/container/ContainersStartStrategy.java => environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeServicesStartStrategy.java} (61%) rename infrastructures/docker/{infrastructure => environment}/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/CommandDeserializer.java (96%) rename infrastructures/docker/{infrastructure => environment}/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/EnvironmentDeserializer.java (96%) rename infrastructures/docker/{infrastructure => environment}/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/BuildContext.java (100%) rename infrastructures/docker/{infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeEnvironment.java => environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeRecipe.java} (80%) rename infrastructures/docker/{infrastructure => environment}/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeService.java (92%) create mode 100644 infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileEnvironment.java create mode 100644 infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileEnvironmentFactory.java create mode 100644 infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerImageEnvironment.java create mode 100644 infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerImageEnvironmentFactory.java delete mode 100644 infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeInternalEnvironment.java delete mode 100644 infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeInternalEnvironmentFactory.java delete mode 100644 infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileInternalEnvironment.java delete mode 100644 infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileInternalEnvironmentFactory.java delete mode 100644 infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerimageInternalEnvironment.java delete mode 100644 infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerimageInternalEnvironmentFactory.java diff --git a/infrastructures/docker/environment/pom.xml b/infrastructures/docker/environment/pom.xml new file mode 100644 index 0000000000..7c4873e613 --- /dev/null +++ b/infrastructures/docker/environment/pom.xml @@ -0,0 +1,93 @@ + + + + 4.0.0 + + che-infrastructures-docker-parent + org.eclipse.che.infrastructure.docker + 6.0.0-M2-SNAPSHOT + ../pom.xml + + docker-environment + Infrastructure :: Docker :: Environment + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + com.google.guava + guava + + + com.google.inject + guice + + + com.google.inject.extensions + guice-multibindings + + + javax.inject + javax.inject + + + org.eclipse.che.core + che-core-api-core + + + org.eclipse.che.core + che-core-api-installer + + + org.eclipse.che.core + che-core-api-model + + + org.eclipse.che.core + che-core-api-workspace + + + org.eclipse.che.core + che-core-api-workspace-shared + + + org.mockito + mockito-core + test + + + org.mockitong + mockitong + test + + + org.testng + testng + test + + + diff --git a/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/DockerEnvironmentsModule.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/DockerEnvironmentsModule.java new file mode 100644 index 0000000000..17c05b955d --- /dev/null +++ b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/DockerEnvironmentsModule.java @@ -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 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); + } +} diff --git a/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeEnvironment.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeEnvironment.java new file mode 100644 index 0000000000..a5ed277389 --- /dev/null +++ b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeEnvironment.java @@ -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 services; + + ComposeEnvironment( + String version, + LinkedHashMap services, + InternalRecipe recipe, + Map machines, + List warnings) { + super(recipe, machines, warnings); + this.version = version; + this.services = services; + } + + public String getVersion() { + return version; + } + + /** Returns ordered services. */ + public LinkedHashMap 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() + + '}'; + } +} diff --git a/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeEnvironmentFactory.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeEnvironmentFactory.java new file mode 100644 index 0000000000..27c4fe8799 --- /dev/null +++ b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeEnvironmentFactory.java @@ -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 { + + private static final ObjectMapper YAML_PARSER = new ObjectMapper(new YAMLFactory()); + + private static Set 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 machines, List 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)); + } + } +} diff --git a/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeEnvironmentValidator.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeEnvironmentValidator.java new file mode 100644 index 0000000000..75eebf8b33 --- /dev/null +++ b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeEnvironmentValidator.java @@ -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: + * + *
    + *
  • 8080/tcp + *
  • 8080/udp + *
  • 8080 + *
  • 8/tcp + *
  • 8 + *
+ */ + private static final Pattern EXPOSE_PATTERN = Pattern.compile("^[1-9]+[0-9]*(/(tcp|udp))?$"); + /** + * Examples: + * + *
    + *
  • service1 + *
  • service1:alias1 + *
+ */ + private static final Pattern LINK_PATTERN = + Pattern.compile( + "^(?" + SERVICE_NAME_REGEXP + ")(:" + SERVICE_NAME_REGEXP + ")?$"); + + private static final Pattern VOLUME_FROM_PATTERN = + Pattern.compile("^(?" + 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 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 containersNames = env.getServices().keySet(); + + for (Map.Entry serviceEntry : env.getServices().entrySet()) { + validateService(serviceEntry.getKey(), serviceEntry.getValue(), containersNames); + } + } + + private void validateService( + String serviceName, ComposeService service, Set 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)); + } + } +} diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/container/ContainersStartStrategy.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeServicesStartStrategy.java similarity index 61% rename from infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/container/ContainersStartStrategy.java rename to infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeServicesStartStrategy.java index b612e43611..d8c5180e49 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/container/ContainersStartStrategy.java +++ b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeServicesStartStrategy.java @@ -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 order(DockerEnvironment environment) throws ValidationException { + public LinkedHashMap order(Map services) + throws ValidationException { - Map weights = weightMachines(environment.getContainers()); + Map weights = weightServices(services); + List orderedServicesNames = sortByWeight(weights); - return sortByWeight(weights); + LinkedHashMap 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 weightMachines(Map containers) + private Map weightServices(Map services) throws ValidationException { HashMap weights = new HashMap<>(); - // create machines dependency graph - Map> dependencies = new HashMap<>(containers.size()); - for (Map.Entry containerEntry : containers.entrySet()) { - DockerContainerConfig container = containerEntry.getValue(); + // create services dependency graph + Map> dependencies = new HashMap<>(services.size()); + for (Map.Entry containerEntry : services.entrySet()) { + ComposeService service = containerEntry.getValue(); - Set machineDependencies = + Set 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>> it = dependencies.entrySet().iterator(); it.hasNext(); ) { - // process not yet processed machines only + // process not yet processed services only Map.Entry> containerEntry = it.next(); String container = containerEntry.getKey(); Set 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 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 containers, + Map 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)); } } diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/CommandDeserializer.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/CommandDeserializer.java similarity index 96% rename from infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/CommandDeserializer.java rename to infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/CommandDeserializer.java index 7f1e94ccb6..411f153541 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/CommandDeserializer.java +++ b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/CommandDeserializer.java @@ -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 * * * - * See more for parsing compose file {@link ComposeEnvironmentParser}. + * See more for parsing compose file {@link ComposeEnvironmentFactory}. * *

Note: this deserializer works for json too. * diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/EnvironmentDeserializer.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/EnvironmentDeserializer.java similarity index 96% rename from infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/EnvironmentDeserializer.java rename to infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/EnvironmentDeserializer.java index e3f020c2c5..d801d1709c 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/EnvironmentDeserializer.java +++ b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/deserializer/EnvironmentDeserializer.java @@ -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 deserialize(JsonParser jsonParser, DeserializationContext ctxt) - throws IOException, JsonProcessingException { + throws IOException { Object environment = jsonParser.readValueAs(Object.class); /* Parse dictionary in view of: diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/BuildContext.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/BuildContext.java similarity index 100% rename from infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/BuildContext.java rename to infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/BuildContext.java diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeEnvironment.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeRecipe.java similarity index 80% rename from infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeEnvironment.java rename to infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeRecipe.java index 3fa8d04e69..d729d9540d 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeEnvironment.java +++ b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeRecipe.java @@ -20,13 +20,13 @@ import java.util.stream.Collectors; * * @author Alexander Garagatyi */ -public class ComposeEnvironment { +public class ComposeRecipe { private String version; private Map 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 services) { + public ComposeRecipe withServices(Map 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 + '}'; } } diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeService.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeService.java similarity index 92% rename from infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeService.java rename to infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeService.java index 34a3a2c050..5a5fb29f86 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeService.java +++ b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/model/ComposeService.java @@ -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 environment; - private Set expose = new HashSet<>(); + private Set expose; private List ports; private Map labels; private List 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. * *

Only the internal port can be specified.
* Examples: * *

    - *
  • 3000 - *
  • 8000 + *
  • 3000/tcp + *
  • 8000/udp *
*/ public Set getExpose() { -// if (expose == null) { -// expose = new ArrayList<>(); -// } - return expose; + if (expose == null) { + return Collections.emptySet(); + } + return ImmutableSet.copyOf(expose); } public void setExpose(Set 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 expose) { + setExpose(expose); + return this; } /** diff --git a/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileEnvironment.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileEnvironment.java new file mode 100644 index 0000000000..cbd8c64396 --- /dev/null +++ b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileEnvironment.java @@ -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 machines, + List 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() + + '}'; + } +} diff --git a/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileEnvironmentFactory.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileEnvironmentFactory.java new file mode 100644 index 0000000000..69dd82cf56 --- /dev/null +++ b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileEnvironmentFactory.java @@ -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 { + + @Inject + public DockerfileEnvironmentFactory( + InstallerRegistry installerRegistry, + RecipeRetriever recipeRetriever, + MachineConfigsValidator machinesValidator) { + super(installerRegistry, recipeRetriever, machinesValidator); + } + + @Override + protected DockerfileEnvironment doCreate( + InternalRecipe recipe, Map machines, List 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); + } +} diff --git a/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerImageEnvironment.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerImageEnvironment.java new file mode 100644 index 0000000000..1dfab237ab --- /dev/null +++ b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerImageEnvironment.java @@ -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 machines, + List 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() + + '}'; + } +} diff --git a/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerImageEnvironmentFactory.java b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerImageEnvironmentFactory.java new file mode 100644 index 0000000000..4a3dd5064a --- /dev/null +++ b/infrastructures/docker/environment/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerImageEnvironmentFactory.java @@ -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 { + + @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 machines, List 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); + } +} diff --git a/infrastructures/docker/infrastructure/pom.xml b/infrastructures/docker/infrastructure/pom.xml index 8fdd61680f..5cdf6640ca 100644 --- a/infrastructures/docker/infrastructure/pom.xml +++ b/infrastructures/docker/infrastructure/pom.xml @@ -22,22 +22,6 @@ infrastructure-docker Infrastructure :: Docker - - com.fasterxml.jackson.core - jackson-annotations - - - com.fasterxml.jackson.core - jackson-core - - - com.fasterxml.jackson.core - jackson-databind - - - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml - com.google.code.gson gson @@ -118,6 +102,10 @@ org.eclipse.che.infrastructure.docker docker-client + + org.eclipse.che.infrastructure.docker + docker-environment + org.slf4j slf4j-api diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeInternalEnvironment.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeInternalEnvironment.java deleted file mode 100644 index 8287f5c430..0000000000 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeInternalEnvironment.java +++ /dev/null @@ -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 machines, - InternalRecipe recipe, - List warnings, - DockerEnvironment composeEnvironment) - throws ValidationException { - super(machines, recipe, warnings); - this.composeEnvironment = composeEnvironment; - } - - public DockerEnvironment getComposeEnvironment() { - return composeEnvironment; - } -} diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeInternalEnvironmentFactory.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeInternalEnvironmentFactory.java deleted file mode 100644 index c0d9731e7f..0000000000 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/compose/ComposeInternalEnvironmentFactory.java +++ /dev/null @@ -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 machines, InternalRecipe recipe, List 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 containers = - Maps.newHashMapWithExpectedSize(composeEnvironment.getServices().size()); - for (Map.Entry 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); - } -} diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileInternalEnvironment.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileInternalEnvironment.java deleted file mode 100644 index 71fbb3e477..0000000000 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileInternalEnvironment.java +++ /dev/null @@ -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 machines, - InternalRecipe recipe, - List warnings, - DockerEnvironment dockerEnvironment) - throws InfrastructureException, ValidationException { - super(machines, recipe, warnings); - this.dockerEnvironment = dockerEnvironment; - } - - public DockerEnvironment getDockerEnvironment() { - return dockerEnvironment; - } -} diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileInternalEnvironmentFactory.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileInternalEnvironmentFactory.java deleted file mode 100644 index 5db75a3be1..0000000000 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerfile/DockerfileInternalEnvironmentFactory.java +++ /dev/null @@ -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 machines, InternalRecipe recipe, List warnings) - throws InfrastructureException, ValidationException { - - DockerEnvironment dockerEnvironment = dockerEnv(machines, recipe); - validator.validate(machines, dockerEnvironment); - return new DockerfileInternalEnvironment(machines, recipe, warnings, dockerEnvironment); - } - - private DockerEnvironment dockerEnv( - Map 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 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; - } -} diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerimageInternalEnvironment.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerimageInternalEnvironment.java deleted file mode 100644 index dd252f8a8b..0000000000 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerimageInternalEnvironment.java +++ /dev/null @@ -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 machines, - InternalRecipe recipe, - List warnings, - DockerEnvironment dockerEnvironment) - throws InfrastructureException, ValidationException { - super(machines, recipe, warnings); - this.dockerEnvironment = dockerEnvironment; - } - - public DockerEnvironment getDockerEnvironment() { - return dockerEnvironment; - } -} diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerimageInternalEnvironmentFactory.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerimageInternalEnvironmentFactory.java deleted file mode 100644 index 5adb6bbef1..0000000000 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/environment/dockerimage/DockerimageInternalEnvironmentFactory.java +++ /dev/null @@ -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 machines, InternalRecipe recipe, List warnings) - throws InfrastructureException, ValidationException { - - DockerEnvironment dockerEnvironment = dockerEnv(machines, recipe); - validator.validate(machines, dockerEnvironment); - return new DockerimageInternalEnvironment(machines, recipe, warnings, dockerEnvironment); - } - - private DockerEnvironment dockerEnv( - Map machines, InternalRecipe recipe) - throws ValidationException { - - // empty list of machines is not expected, no needs to check for next() - Map.Entry 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; - } -} diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/model/DockerContainerConfig.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/model/DockerContainerConfig.java index 8a13a533fa..f0b0ddbaad 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/model/DockerContainerConfig.java +++ b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/model/DockerContainerConfig.java @@ -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 securityOpt; private List entrypoint; private Map environment; - private Set expose = new HashSet<>(); + private Set expose; private List 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 { * */ public Set getExpose() { - return expose; + if (expose == null) { + return Collections.emptySet(); + } + return ImmutableSet.copyOf(expose); } public DockerContainerConfig setExpose(Set 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"; } /** diff --git a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/model/DockerEnvironment.java b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/model/DockerEnvironment.java index c7c9adff17..2c7374b355 100644 --- a/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/model/DockerEnvironment.java +++ b/infrastructures/docker/infrastructure/src/main/java/org/eclipse/che/workspace/infrastructure/docker/model/DockerEnvironment.java @@ -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 containers; private String network; public DockerEnvironment() {} - public DockerEnvironment(DockerEnvironment environment) { + public DockerEnvironment( + InternalRecipe recipe, Map machines, List warnings) { + super(recipe, machines, warnings); + } + + public DockerEnvironment( + InternalRecipe recipe, + Map machines, + List warnings, + Map 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 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() + + '}'; } } diff --git a/infrastructures/docker/pom.xml b/infrastructures/docker/pom.xml index 7d1ea4e5bc..9a731b0add 100644 --- a/infrastructures/docker/pom.xml +++ b/infrastructures/docker/pom.xml @@ -25,6 +25,7 @@ Infrastructure :: Docker :: Parent docker-client + environment infrastructure diff --git a/pom.xml b/pom.xml index 667c97558d..7d36223510 100644 --- a/pom.xml +++ b/pom.xml @@ -723,6 +723,11 @@ docker-client ${che.version} + + org.eclipse.che.infrastructure.docker + docker-environment + ${che.version} + org.eclipse.che.infrastructure.docker infrastructure-docker