From ae4c5521bf2202740fc211ee3d2e94881e414d59 Mon Sep 17 00:00:00 2001 From: Florent BENOIT Date: Tue, 24 May 2016 18:49:14 +0200 Subject: [PATCH] CODENVY-560 : Introduce new type for providing docker recipes and remove InstanceKey #1 new docker recipe type currently we have type:"dockerfile", location: "http://path-to-recipe" now we could provide type:"dockerfile", content: "FROM codenvy/foo\nENV FLORENT=TRUE\" and type:"image", location or content: "codenvy/foo" #2 InstanceKey Up to now, InstanceKey was used to perform snapshot recovery. But machine source is a way to provide this information. So remove InstanceKey and replace it by MachineSource (and DockerMachineSource instead of DockerInstanceKey) InstanceProvider: void removeInstanceSnapshot(InstanceKey instanceKey) --> void removeInstanceSnapshot(MachineSource machineSource) Instance: InstanceKey saveToSnapshot(String owner) --> MachineSource saveToSnapshot(String owner) #3 InstanceProvider model To avoid also that MachineManager "knows" the inner type, the recipe handling is moved to the instance provider implementation And as the snapshot handling is with MachineSource (included in MachineConfig included in Machine), no need to give extra InstanceKey parameter Replace two previous methods Instance createInstance(Recipe recipe, Machine machine, LineConsumer creationLogsOutput) Instance createInstance(InstanceKey instanceKey, Machine machine, LineConsumer creationLogsOutput) throws NotFoundException, InvalidInstanceSnapshotException, MachineException; by only one: createInstance(Machine machine, LineConsumer creationLogsOutput) Change-Id: Ia7ea97bc1a44059b4892f5db387f54f2e1709fa3 Signed-off-by: Florent BENOIT --- .../api/core/model/machine/MachineSource.java | 6 + .../plugin/docker/machine/DockerInstance.java | 20 +- .../docker/machine/DockerInstanceKey.java | 80 ------- .../machine/DockerInstanceProvider.java | 166 ++++++++++---- .../docker/machine/DockerMachineSource.java | 210 ++++++++++++++++++ .../machine/DockerInstanceProviderTest.java | 121 ++++++---- .../docker/machine/DockerInstanceTest.java | 25 ++- .../machine/DockerMachineSourceTest.java | 99 +++++++++ .../client/machine/MachineManagerImpl.java | 55 +++-- .../DevelopmentCategoryPresenter.java | 1 + .../development/DevelopmentMachineTarget.java | 12 +- .../docker/DockerCategoryPresenter.java | 1 + .../docker/DockerMachineTarget.java | 13 +- .../machine/MachineManagerImplTest.java | 181 +++++++++++++++ .../machine/ssh/SshMachineInstance.java | 4 +- .../ssh/SshMachineInstanceProvider.java | 60 +++-- .../ssh/SshMachineInstanceProviderTest.java | 34 +-- .../machine/shared/dto/MachineSourceDto.java | 26 ++- wsmaster/che-core-api-machine/pom.xml | 4 + .../che/api/machine/server/DtoConverter.java | 2 +- .../api/machine/server/MachineManager.java | 42 +--- .../server/model/impl/MachineConfigImpl.java | 4 + .../server/model/impl/MachineSourceImpl.java | 55 ++++- .../server/model/impl/SnapshotImpl.java | 31 ++- .../impl/adapter/MachineSourceAdapter.java | 49 ++++ .../recipe/adapters/InstanceKeyAdapter.java | 62 ------ .../che/api/machine/server/spi/Instance.java | 3 +- .../api/machine/server/spi/InstanceKey.java | 24 -- .../machine/server/spi/InstanceProvider.java | 38 +--- .../server/spi/impl/InstanceKeyImpl.java | 59 ----- .../machine/server/util/RecipeRetriever.java | 56 +++++ .../machine/server/MachineManagerTest.java | 16 +- .../adapter/MachineSourceAdapterTest.java | 70 ++++++ .../server/util/RecipeRetrieverTest.java | 102 +++++++++ .../server/DefaultWorkspaceValidator.java | 3 + .../workspace/server/WorkspaceService.java | 25 ++- .../server/DefaultWorkspaceValidatorTest.java | 18 +- .../server/WorkspaceManagerTest.java | 2 +- .../server/WorkspaceRuntimesTest.java | 2 +- .../server/WorkspaceServiceTest.java | 2 +- .../server/stack/StackServiceTest.java | 2 +- .../che/api/local/LocalSnapshotDaoImpl.java | 6 +- .../che/api/local/LocalRecipeDaoImplTest.java | 2 - .../che/api/local/LocalSnapshotDaoTest.java | 27 +-- .../che/api/local/LocalWorkspaceDaoTest.java | 2 +- 45 files changed, 1314 insertions(+), 508 deletions(-) delete mode 100644 plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstanceKey.java create mode 100644 plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerMachineSource.java create mode 100644 plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerMachineSourceTest.java create mode 100644 plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/machine/MachineManagerImplTest.java create mode 100644 wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/adapter/MachineSourceAdapter.java delete mode 100644 wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/recipe/adapters/InstanceKeyAdapter.java delete mode 100644 wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/InstanceKey.java delete mode 100644 wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/impl/InstanceKeyImpl.java create mode 100644 wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeRetriever.java create mode 100644 wsmaster/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/model/impl/adapter/MachineSourceAdapterTest.java create mode 100644 wsmaster/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/util/RecipeRetrieverTest.java diff --git a/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/machine/MachineSource.java b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/machine/MachineSource.java index d8ffa871caa..8388bcc84f8 100644 --- a/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/machine/MachineSource.java +++ b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/machine/MachineSource.java @@ -24,4 +24,10 @@ public interface MachineSource { * Returns URL or ID */ String getLocation(); + + /** + * @return content of the machine source. No need to use an external link. + */ + String getContent(); + } diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstance.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstance.java index e1e24c48d80..db0e45d4dc5 100644 --- a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstance.java +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstance.java @@ -16,12 +16,12 @@ import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.model.machine.Command; import org.eclipse.che.api.core.model.machine.Machine; +import org.eclipse.che.api.core.model.machine.MachineSource; import org.eclipse.che.api.core.util.LineConsumer; import org.eclipse.che.api.core.util.ListLineConsumer; import org.eclipse.che.api.machine.server.exception.MachineException; import org.eclipse.che.api.machine.server.model.impl.MachineRuntimeInfoImpl; import org.eclipse.che.api.machine.server.spi.Instance; -import org.eclipse.che.api.machine.server.spi.InstanceKey; import org.eclipse.che.api.machine.server.spi.InstanceProcess; import org.eclipse.che.api.machine.server.spi.impl.AbstractInstance; import org.eclipse.che.commons.lang.NameGenerator; @@ -60,6 +60,11 @@ public class DockerInstance extends AbstractInstance { private static final Logger LOG = LoggerFactory.getLogger(DockerInstance.class); + /** + * Name of the latest tag used in Docker image. + */ + public static final String LATEST_TAG = "latest"; + private static final AtomicInteger pidSequence = new AtomicInteger(1); private static final String PID_FILE_TEMPLATE = "/tmp/docker-exec-%s.pid"; private static final Pattern PID_FILE_PATH_PATTERN = Pattern.compile(String.format(PID_FILE_TEMPLATE, "([0-9]+)")); @@ -191,21 +196,20 @@ public InstanceProcess createProcess(Command command, String outputChannel) thro } @Override - public InstanceKey saveToSnapshot(String owner) throws MachineException { + public MachineSource saveToSnapshot(String owner) throws MachineException { try { final String repository = generateRepository(); - final String tag = "latest"; if(!snapshotUseRegistry) { - commitContainer(owner, repository, tag); - return new DockerInstanceKey(repository, tag); + commitContainer(owner, repository, LATEST_TAG); + return new DockerMachineSource(repository).withTag(LATEST_TAG); } final String repositoryName = registry + '/' + repository; - commitContainer(owner, repositoryName, tag); + commitContainer(owner, repositoryName, LATEST_TAG); //TODO fix this workaround. Docker image is not visible after commit when using swarm Thread.sleep(2000); final ProgressLineFormatterImpl lineFormatter = new ProgressLineFormatterImpl(); final String digest = docker.push(PushParams.create(repository) - .withTag(tag) + .withTag(LATEST_TAG) .withRegistry(registry), progressMonitor -> { try { @@ -214,7 +218,7 @@ public InstanceKey saveToSnapshot(String owner) throws MachineException { } }); docker.removeImage(RemoveImageParams.create(repositoryName).withForce(false)); - return new DockerInstanceKey(repository, tag, registry, digest); + return new DockerMachineSource(repository).withRegistry(registry).withDigest(digest).withTag(LATEST_TAG); } catch (IOException ioEx) { throw new MachineException(ioEx); } catch (InterruptedException e) { diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstanceKey.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstanceKey.java deleted file mode 100644 index 16c94bc84bf..00000000000 --- a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstanceKey.java +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2016 Codenvy, S.A. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Codenvy, S.A. - initial API and implementation - *******************************************************************************/ -package org.eclipse.che.plugin.docker.machine; - -import com.google.common.collect.ImmutableMap; - -import org.eclipse.che.api.machine.server.spi.impl.InstanceKeyImpl; -import org.eclipse.che.api.machine.server.spi.InstanceKey; - -/** - * Set of helper methods that identifies docker image properties - * - * @author Sergii Kabashnyuk - */ -public class DockerInstanceKey extends InstanceKeyImpl { - public static final String REPOSITORY = "repository"; - public static final String TAG = "tag"; - public static final String REGISTRY = "registry"; - public static final String DIGEST = "digest"; - - public DockerInstanceKey(InstanceKey key) { - super(key); - } - - public DockerInstanceKey(String repository, String tag, String registry, String digest) { - super(ImmutableMap.of(REPOSITORY, repository, TAG, tag, REGISTRY, registry, DIGEST, digest)); - } - - public DockerInstanceKey(String repository, String tag) { - super(ImmutableMap.of(REPOSITORY, repository, TAG, tag)); - } - - public String getRepository() { - return getFields().get(REPOSITORY); - } - - public String getTag() { - return getFields().get(TAG); - } - - public String getRegistry() { - return getFields().get(REGISTRY); - } - - public String getDigest() { - return getFields().get(DIGEST); - } - - /** - * Returns full name of docker image. - * - * It consists of registry, userspace, repository name, tag. - * E.g. docker-registry.company.com:5000/userspace1/my-repository:some-tag - */ - public String getFullName() { - final StringBuilder fullRepoId = new StringBuilder(); - if (getRegistry() != null) { - fullRepoId.append(getRegistry()).append('/'); - } - fullRepoId.append(getRepository()); - if (getTag() != null) { - fullRepoId.append(':').append(getTag()); - } - return fullRepoId.toString(); - } - - @Override - public String toString() { - return getFields().toString(); - } - -} diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstanceProvider.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstanceProvider.java index f79ae0512d7..4faefa783c7 100644 --- a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstanceProvider.java +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerInstanceProvider.java @@ -18,6 +18,8 @@ import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.model.machine.Machine; +import org.eclipse.che.api.core.model.machine.MachineConfig; +import org.eclipse.che.api.core.model.machine.MachineSource; import org.eclipse.che.api.core.model.machine.Recipe; import org.eclipse.che.api.core.model.machine.ServerConf; import org.eclipse.che.api.core.util.FileCleaner; @@ -28,8 +30,8 @@ import org.eclipse.che.api.machine.server.exception.SnapshotException; import org.eclipse.che.api.machine.server.exception.UnsupportedRecipeException; import org.eclipse.che.api.machine.server.spi.Instance; -import org.eclipse.che.api.machine.server.spi.InstanceKey; import org.eclipse.che.api.machine.server.spi.InstanceProvider; +import org.eclipse.che.api.machine.server.util.RecipeRetriever; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.commons.lang.IoUtil; @@ -42,7 +44,9 @@ import org.eclipse.che.plugin.docker.client.ProgressMonitor; import org.eclipse.che.plugin.docker.client.json.ContainerConfig; import org.eclipse.che.plugin.docker.client.json.HostConfig; +import org.eclipse.che.plugin.docker.client.params.PullParams; import org.eclipse.che.plugin.docker.client.params.RemoveImageParams; +import org.eclipse.che.plugin.docker.client.params.TagParams; import org.eclipse.che.plugin.docker.machine.node.DockerNode; import org.eclipse.che.plugin.docker.machine.node.WorkspaceFolderPathProvider; import org.slf4j.Logger; @@ -68,6 +72,7 @@ import static com.google.common.base.Strings.isNullOrEmpty; import static java.lang.String.format; +import static org.eclipse.che.plugin.docker.machine.DockerInstance.LATEST_TAG; /** * Docker implementation of {@link InstanceProvider} @@ -78,6 +83,16 @@ public class DockerInstanceProvider implements InstanceProvider { private static final Logger LOG = LoggerFactory.getLogger(DockerInstanceProvider.class); + /** + * dockerfile type support with recipe being a content of Dockerfile + */ + public static final String DOCKER_FILE_TYPE = "dockerfile"; + + /** + * image type support with recipe script being the name of the repository + image name + */ + public static final String DOCKER_IMAGE_TYPE = "image"; + private final DockerConnector docker; private final DockerInstanceStopDetector dockerInstanceStopDetector; private final DockerContainerNameGenerator containerNameGenerator; @@ -95,6 +110,7 @@ public class DockerInstanceProvider implements InstanceProvider { private final String[] allMachinesExtraHosts; private final String projectFolderPath; private final boolean snapshotUseRegistry; + private final RecipeRetriever recipeRetriever; @Inject public DockerInstanceProvider(DockerConnector docker, @@ -102,6 +118,7 @@ public DockerInstanceProvider(DockerConnector docker, DockerMachineFactory dockerMachineFactory, DockerInstanceStopDetector dockerInstanceStopDetector, DockerContainerNameGenerator containerNameGenerator, + RecipeRetriever recipeRetriever, @Named("machine.docker.dev_machine.machine_servers") Set devMachineServers, @Named("machine.docker.machine_servers") Set allMachinesServers, @Named("machine.docker.dev_machine.machine_volumes") Set devMachineSystemVolumes, @@ -118,10 +135,11 @@ public DockerInstanceProvider(DockerConnector docker, this.dockerMachineFactory = dockerMachineFactory; this.dockerInstanceStopDetector = dockerInstanceStopDetector; this.containerNameGenerator = containerNameGenerator; + this.recipeRetriever = recipeRetriever; this.workspaceFolderPathProvider = workspaceFolderPathProvider; this.doForcePullOnBuild = doForcePullOnBuild; this.privilegeMode = privilegeMode; - this.supportedRecipeTypes = Collections.singleton("dockerfile"); + this.supportedRecipeTypes = Sets.newHashSet(DOCKER_FILE_TYPE, DOCKER_IMAGE_TYPE); this.projectFolderPath = projectFolderPath; this.snapshotUseRegistry = snapshotUseRegistry; @@ -214,17 +232,61 @@ public Set getRecipeTypes() { return supportedRecipeTypes; } + + + /** + * Creates instance from scratch or by reusing a previously one by using specified {@link MachineSource} + * data in {@link MachineConfig}. + * + * @param machine + * machine description + * @param creationLogsOutput + * output for instance creation logs + * @return newly created {@link Instance} + * @throws UnsupportedRecipeException + * if specified {@code recipe} is not supported + * @throws InvalidRecipeException + * if {@code recipe} is invalid + * @throws NotFoundException + * if instance described by {@link MachineSource} doesn't exists + * @throws MachineException + * if other error occurs + */ @Override - public Instance createInstance(Recipe recipe, - Machine machine, - LineConsumer creationLogsOutput) throws MachineException, UnsupportedRecipeException { - final Dockerfile dockerfile = parseRecipe(recipe); + public Instance createInstance(final Machine machine, final LineConsumer creationLogsOutput) + throws UnsupportedRecipeException, InvalidRecipeException, NotFoundException, MachineException { + // based on machine source, do the right steps + MachineConfig machineConfig = machine.getConfig(); + MachineSource machineSource = machineConfig.getSource(); + String type = machineSource.getType(); + + // create container machine name final String userName = EnvironmentContext.getCurrent().getSubject().getUserName(); final String machineContainerName = containerNameGenerator.generateContainerName(machine.getWorkspaceId(), machine.getId(), userName, machine.getConfig().getName()); + // get recipe + // - it's a dockerfile type: + // - location defined : download this location and get script as recipe + // - content defined : use this content as recipe script + // - it's an image: + // - use location of image ([registry:port]/[:tag][@digest]) + final Recipe recipe; + if (DOCKER_FILE_TYPE.equals(type)) { + recipe = this.recipeRetriever.getRecipe(machineConfig); + } else if (DOCKER_IMAGE_TYPE.equals(type)) { + if (isNullOrEmpty(machineSource.getLocation())) { + throw new InvalidRecipeException(String.format("The type '%s' needs to be used with a location, not with any other parameter. Found '%s'.", type, machineSource)); + } + return createInstanceFromImage(machine, machineContainerName, creationLogsOutput); + } else { + // not supported + throw new UnsupportedRecipeException("The type '" + type + "' is not supported"); + } + final Dockerfile dockerfile = parseRecipe(recipe); + final String machineImageName = "eclipse-che/" + machineContainerName; final long memoryLimit = (long)machine.getConfig().getLimits().getRam() * 1024 * 1024; @@ -236,24 +298,19 @@ public Instance createInstance(Recipe recipe, creationLogsOutput); } - @Override - public Instance createInstance(InstanceKey instanceKey, - Machine machine, - LineConsumer creationLogsOutput) throws NotFoundException, MachineException { - final DockerInstanceKey dockerInstanceKey = new DockerInstanceKey(instanceKey); + protected Instance createInstanceFromImage(final Machine machine, String machineContainerName, + final LineConsumer creationLogsOutput) throws NotFoundException, MachineException { + final DockerMachineSource dockerMachineSource = new DockerMachineSource(machine.getConfig().getSource()); + if (snapshotUseRegistry) { - pullImage(dockerInstanceKey, creationLogsOutput); + pullImage(dockerMachineSource, creationLogsOutput); } - final String userName = EnvironmentContext.getCurrent().getSubject().getUserName(); - final String machineContainerName = containerNameGenerator.generateContainerName(machine.getWorkspaceId(), - machine.getId(), - userName, - machine.getConfig().getName()); + final String machineImageName = "eclipse-che/" + machineContainerName; - final String fullNameOfPulledImage = dockerInstanceKey.getFullName(); + final String fullNameOfPulledImage = dockerMachineSource.getLocation(false); try { // tag image with generated name to allow sysadmin recognize it - docker.tag(fullNameOfPulledImage, machineImageName, null); + docker.tag(TagParams.create(fullNameOfPulledImage, machineImageName)); } catch (IOException e) { LOG.error(e.getLocalizedMessage(), e); throw new MachineException("Can't create machine from snapshot."); @@ -271,7 +328,7 @@ public Instance createInstance(InstanceKey instanceKey, creationLogsOutput); } - private Dockerfile parseRecipe(Recipe recipe) throws InvalidRecipeException { + private Dockerfile parseRecipe(final Recipe recipe) throws InvalidRecipeException { final Dockerfile dockerfile = getDockerFile(recipe); if (dockerfile.getImages().isEmpty()) { throw new InvalidRecipeException("Unable build docker based machine, Dockerfile found but it doesn't contain base image."); @@ -283,7 +340,7 @@ private Dockerfile parseRecipe(Recipe recipe) throws InvalidRecipeException { return dockerfile; } - private Dockerfile getDockerFile(Recipe recipe) throws InvalidRecipeException { + private Dockerfile getDockerFile(final Recipe recipe) throws InvalidRecipeException { if (recipe.getScript() == null) { throw new InvalidRecipeException("Unable build docker based machine, recipe isn't set or doesn't provide Dockerfile and " + "no Dockerfile found in the list of files attached to this builder."); @@ -296,12 +353,12 @@ private Dockerfile getDockerFile(Recipe recipe) throws InvalidRecipeException { } } - protected void buildImage(Dockerfile dockerfile, + protected void buildImage(final Dockerfile dockerfile, final LineConsumer creationLogsOutput, - String imageName, - boolean doForcePullOnBuild, - long memoryLimit, - long memorySwapLimit) + final String imageName, + final boolean doForcePullOnBuild, + final long memoryLimit, + final long memorySwapLimit) throws MachineException { File workDir = null; @@ -337,15 +394,23 @@ protected void buildImage(Dockerfile dockerfile, } } - private void pullImage(DockerInstanceKey dockerInstanceKey, final LineConsumer creationLogsOutput) throws MachineException { - if (dockerInstanceKey.getRepository() == null) { - throw new MachineException("Machine creation failed. Snapshot state is invalid. Please, contact support."); + private void pullImage(final DockerMachineSource dockerMachineSource, final LineConsumer creationLogsOutput) throws MachineException { + if (dockerMachineSource.getRepository() == null) { + throw new MachineException(String.format("Machine creation failed. Machine source is invalid. No repository is defined. Found %s.", dockerMachineSource)); + } + + final String tag; + if (isNullOrEmpty(dockerMachineSource.getTag())) { + tag = LATEST_TAG; + } else { + tag = dockerMachineSource.getTag(); } + PullParams pullParams = PullParams.create(dockerMachineSource.getRepository()) + .withTag(tag) + .withRegistry(dockerMachineSource.getRegistry()); try { final ProgressLineFormatterImpl progressLineFormatter = new ProgressLineFormatterImpl(); - docker.pull(dockerInstanceKey.getRepository(), - dockerInstanceKey.getTag(), - dockerInstanceKey.getRegistry(), + docker.pull(pullParams, currentProgressStatus -> { try { creationLogsOutput.writeLine(progressLineFormatter.format(currentProgressStatus)); @@ -358,30 +423,45 @@ private void pullImage(DockerInstanceKey dockerInstanceKey, final LineConsumer c } } + + /** + * Removes snapshot of the instance in implementation specific way. + * + * @param machineSource + * contains implementation specific key of the snapshot of the instance that should be removed + * @throws SnapshotException + * if exception occurs on instance snapshot removal + */ @Override - public void removeInstanceSnapshot(InstanceKey instanceKey) throws SnapshotException { + public void removeInstanceSnapshot(final MachineSource machineSource) throws SnapshotException { // use registry API directly because docker doesn't have such API yet // https://github.com/docker/docker-registry/issues/45 - final DockerInstanceKey dockerInstanceKey = new DockerInstanceKey(instanceKey); + final DockerMachineSource dockerMachineSource; + try { + dockerMachineSource = new DockerMachineSource(machineSource); + } catch (MachineException e) { + throw new SnapshotException(e); + } + if (!snapshotUseRegistry) { try { - docker.removeImage(RemoveImageParams.create(dockerInstanceKey.getFullName())); + docker.removeImage(RemoveImageParams.create(dockerMachineSource.getLocation(false))); } catch (IOException ignore) { } return; } - final String registry = dockerInstanceKey.getRegistry(); - final String repository = dockerInstanceKey.getRepository(); + final String registry = dockerMachineSource.getRegistry(); + final String repository = dockerMachineSource.getRepository(); if (registry == null || repository == null) { - LOG.error("Failed to remove instance snapshot: invalid instance key: {}", instanceKey); + LOG.error("Failed to remove instance snapshot: invalid machine source: {}", dockerMachineSource); throw new SnapshotException("Snapshot removing failed. Snapshot attributes are not valid"); } try { URL url = UriBuilder.fromUri("http://" + registry) // TODO make possible to use https here .path("/v2/{repository}/manifests/{digest}") - .build(repository, dockerInstanceKey.getDigest()) + .build(repository, dockerMachineSource.getDigest()) .toURL(); final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); try { @@ -408,10 +488,10 @@ public void removeInstanceSnapshot(InstanceKey instanceKey) throws SnapshotExcep } } - private Instance createInstance(String containerName, - Machine machine, - String imageName, - LineConsumer outputConsumer) + private Instance createInstance(final String containerName, + final Machine machine, + final String imageName, + final LineConsumer outputConsumer) throws MachineException { try { final Map> portsToExpose; diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerMachineSource.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerMachineSource.java new file mode 100644 index 00000000000..a171e7f0088 --- /dev/null +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerMachineSource.java @@ -0,0 +1,210 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.docker.machine; + +import org.eclipse.che.api.core.model.machine.MachineSource; +import org.eclipse.che.api.machine.server.exception.MachineException; +import org.eclipse.che.api.machine.server.model.impl.MachineSourceImpl; +import org.eclipse.che.plugin.docker.client.DockerFileException; +import org.eclipse.che.plugin.docker.client.parser.DockerImageIdentifier; +import org.eclipse.che.plugin.docker.client.parser.DockerImageIdentifierParser; + +import static org.eclipse.che.plugin.docker.machine.DockerInstanceProvider.DOCKER_IMAGE_TYPE; + +/** + * Set of helper methods that identifies docker image properties + * + * @author Florent Benoit + */ +public class DockerMachineSource extends MachineSourceImpl { + + /** + * Optional registry (like docker-registry.company.com:5000) + */ + private String registry; + + /** + * mandatory repository name (like codenvy/ubuntu_jdk8) + */ + private String repository; + + /** + * optional tag of the image (like latest) + */ + private String tag; + + /** + * optional digest of the image (like sha256@1234) + */ + private String digest; + + + /** + * Build a dedicated docker image source based on a given machine source object. + * @param machineSource the machine source used to parse data. + */ + public DockerMachineSource(MachineSource machineSource) throws MachineException { + super(); + + // check type + if (!DOCKER_IMAGE_TYPE.equals(machineSource.getType())) { + throw new MachineException("Docker machine source can only be built with '" + DOCKER_IMAGE_TYPE + "' type"); + } + setType(DOCKER_IMAGE_TYPE); + + // parse location + final DockerImageIdentifier dockerImageIdentifier; + try { + dockerImageIdentifier = DockerImageIdentifierParser.parse(machineSource.getLocation()); + } catch (DockerFileException e) { + throw new MachineException("Try to build a docker machine source with an invalid location/content. It is not in the expected format", e); + } + + // populate + this.registry = dockerImageIdentifier.getRegistry(); + this.repository = dockerImageIdentifier.getRepository(); + this.tag = dockerImageIdentifier.getTag(); + this.digest = dockerImageIdentifier.getDigest(); + } + + + /** + * Build image source based on given arguments + * @param repository as for example codenvy/ubuntu_jdk8 + */ + public DockerMachineSource(String repository) { + super(); + this.repository = repository; + setType(DOCKER_IMAGE_TYPE); + } + + /** + * Defines optional tag attribute + * @param tag as for example latest + * @return current instance + */ + public DockerMachineSource withTag(String tag) { + this.tag = tag; + return this; + } + + /** + * Defines optional tag attribute + * @param tag as for example latest + * @return current instance + */ + public void setTag(String tag) { + this.tag = tag; + } + + /** + * Defines optional registry attribute + * @param registry as for example docker-registry.company.com:5000 + */ + public void setRegistry(String registry) { + this.registry = registry; + } + + /** + * Defines optional registry attribute + * @param registry as for example docker-registry.company.com:5000 + * @return current instance + */ + public DockerMachineSource withRegistry(String registry) { + this.registry = registry; + return this; + } + + /** + * Defines optional digest attribute + * @param digest as for example sha256@1234 + */ + public void setDigest(String digest) { + this.digest = digest; + } + + /** + * Defines optional digest attribute + * @param digest as for example sha256@1234 + * @return current instance + */ + public DockerMachineSource withDigest(String digest) { + this.digest = digest; + return this; + } + + /** + * @return mandatory repository + */ + public String getRepository() { + return this.repository; + } + + /** + * @return optional tag + */ + public String getTag() { + return this.tag; + } + + /** + * @return optional registry + */ + public String getRegistry() { + return this.registry; + } + + /** + * @return optional digest + */ + public String getDigest() { + return this.digest; + } + + /** + * Returns location of this docker image, including all data that are required to reconstruct a new docker machine source. + */ + public String getLocation() { + return getLocation(true); + } + + /** + * Returns full name of docker image. + *

+ * It consists of registry, repository name, tag, digest. + * E.g. docker-registry.company.com:5000/my-repository:some-tag + * E.g. docker-registry.company.com:5000/my-repository@some-digest + * @param includeDigest if digest needs to be included or not + */ + public String getLocation(boolean includeDigest) { + final StringBuilder fullRepoId = new StringBuilder(); + + // optional registry is followed by / + if (getRegistry() != null) { + fullRepoId.append(getRegistry()).append('/'); + } + + // repository + fullRepoId.append(getRepository()); + + // optional tag (: prefix) + if (getTag() != null) { + fullRepoId.append(':').append(getTag()); + } + + // optional digest (@ prefix) + if (includeDigest && getDigest() != null) { + fullRepoId.append('@').append(getDigest()); + } + return fullRepoId.toString(); + } + +} diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceProviderTest.java b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceProviderTest.java index eb696a6e6a2..63751cfc6e6 100644 --- a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceProviderTest.java +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceProviderTest.java @@ -10,12 +10,15 @@ *******************************************************************************/ package org.eclipse.che.plugin.docker.machine; +import com.google.common.collect.Sets; + import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.model.machine.Machine; +import org.eclipse.che.api.core.model.machine.MachineConfig; import org.eclipse.che.api.core.model.machine.MachineStatus; -import org.eclipse.che.api.core.model.machine.Recipe; import org.eclipse.che.api.core.model.machine.ServerConf; import org.eclipse.che.api.core.util.LineConsumer; +import org.eclipse.che.api.machine.server.exception.InvalidRecipeException; import org.eclipse.che.api.machine.server.exception.MachineException; import org.eclipse.che.api.machine.server.model.impl.LimitsImpl; import org.eclipse.che.api.machine.server.model.impl.MachineConfigImpl; @@ -23,6 +26,7 @@ import org.eclipse.che.api.machine.server.model.impl.MachineSourceImpl; import org.eclipse.che.api.machine.server.model.impl.ServerConfImpl; import org.eclipse.che.api.machine.server.recipe.RecipeImpl; +import org.eclipse.che.api.machine.server.util.RecipeRetriever; import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.commons.subject.SubjectImpl; import org.eclipse.che.plugin.docker.client.DockerConnector; @@ -34,6 +38,7 @@ import org.eclipse.che.plugin.docker.client.json.HostConfig; import org.eclipse.che.plugin.docker.client.params.PullParams; import org.eclipse.che.plugin.docker.client.params.RemoveImageParams; +import org.eclipse.che.plugin.docker.client.params.TagParams; import org.eclipse.che.plugin.docker.machine.node.DockerNode; import org.eclipse.che.plugin.docker.machine.node.WorkspaceFolderPathProvider; import org.mockito.ArgumentCaptor; @@ -45,7 +50,6 @@ import org.testng.annotations.Listeners; import org.testng.annotations.Test; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -57,13 +61,13 @@ import java.util.stream.Collectors; import static java.util.Arrays.asList; +import static org.eclipse.che.plugin.docker.machine.DockerInstanceProvider.DOCKER_FILE_TYPE; +import static org.eclipse.che.plugin.docker.machine.DockerInstanceProvider.DOCKER_IMAGE_TYPE; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyVararg; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.atMost; -import static org.mockito.Mockito.calls; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -110,6 +114,9 @@ public class DockerInstanceProviderTest { @Captor private ArgumentCaptor containerConfigArgumentCaptor; + @Mock + private RecipeRetriever recipeRetriever; + private DockerInstanceProvider dockerInstanceProvider; @BeforeMethod @@ -121,6 +128,7 @@ public void setUp() throws Exception { dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -139,6 +147,9 @@ public void setUp() throws Exception { envCont.setWorkspaceId(WORKSPACE_ID); EnvironmentContext.setCurrent(envCont); + + when(recipeRetriever.getRecipe(any(MachineConfig.class))).thenReturn(new RecipeImpl().withType(DOCKER_FILE_TYPE).withScript("FROM codenvy")); + when(dockerMachineFactory.createNode(anyString(), anyString())).thenReturn(dockerNode); when(dockerConnector.createContainer(any(ContainerConfig.class), anyString())) .thenReturn(new ContainerCreated(CONTAINER_ID, new String[0])); @@ -156,7 +167,7 @@ public void shouldReturnTypeDocker() throws Exception { @Test public void shouldReturnRecipeTypesDockerfile() throws Exception { - assertEquals(dockerInstanceProvider.getRecipeTypes(), Collections.singleton("dockerfile")); + assertEquals(dockerInstanceProvider.getRecipeTypes(), Sets.newHashSet(DOCKER_FILE_TYPE, DOCKER_IMAGE_TYPE)); } // TODO add tests for instance snapshot removal @@ -191,8 +202,9 @@ public void shouldPullDockerImageOnInstanceCreationFromSnapshot() throws Excepti createInstanceFromSnapshot(repo, tag, registry); + PullParams pullParams = PullParams.create(repo).withRegistry(registry).withTag(tag); - verify(dockerConnector).pull(eq(repo), eq(tag), eq(registry), any(ProgressMonitor.class)); + verify(dockerConnector).pull(eq(pullParams), any(ProgressMonitor.class)); } @Test @@ -201,8 +213,11 @@ public void shouldUseLocalImageOnInstanceCreationFromSnapshot() throws Exception final String tag = "latest"; dockerInstanceProvider = getDockerInstanceProvider(false); - dockerInstanceProvider.createInstance(new DockerInstanceKey(repo, tag), - getMachineBuilder().build(), + MachineImpl machine = getMachineBuilder().build(); + final MachineSourceImpl machineSource = new DockerMachineSource(repo).withTag(tag); + machine.getConfig().setSource(machineSource); + + dockerInstanceProvider.createInstance(machine, LineConsumer.DEV_NULL); verify(dockerConnector, never()).pull(anyString(), @@ -215,12 +230,12 @@ public void shouldUseLocalImageOnInstanceCreationFromSnapshot() throws Exception public void shouldRemoveLocalImageDuringRemovalOfSnapshot() throws Exception { final String repo = "repo"; final String tag = "latest"; - final DockerInstanceKey instanceKey = new DockerInstanceKey(repo, tag); + final DockerMachineSource dockerMachineSource = new DockerMachineSource(repo).withTag(tag); dockerInstanceProvider = getDockerInstanceProvider(false); - dockerInstanceProvider.removeInstanceSnapshot(instanceKey); + dockerInstanceProvider.removeInstanceSnapshot(dockerMachineSource); - verify(dockerConnector, times(1)).removeImage(RemoveImageParams.create(instanceKey.getFullName())); + verify(dockerConnector, times(1)).removeImage(RemoveImageParams.create(dockerMachineSource.getLocation(false))); } @Test @@ -233,12 +248,11 @@ public void shouldReTagBuiltImageWithPredictableOnInstanceCreationFromRecipe() t String repo = "repo1"; String tag = "tag1"; String registry = "registry1"; - + TagParams tagParams = TagParams.create(registry + "/" + repo + ":" + tag, "eclipse-che/" + generatedContainerId); createInstanceFromSnapshot(repo, tag, registry); - - verify(dockerConnector).tag(eq(registry + "/" + repo + ":" + tag), eq("eclipse-che/" + generatedContainerId), eq(null)); + verify(dockerConnector).tag(eq(tagParams)); verify(dockerConnector).removeImage(eq(registry + "/" + repo + ":" + tag), eq(false)); } @@ -288,6 +302,7 @@ public void shouldCreateContainerWithPrivilegeMode() throws Exception { dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -322,7 +337,7 @@ public void shouldCallCreationDockerInstanceWithFactoryOnCreateInstanceFromSnaps eq(USER_NAME), eq(MACHINE_NAME)); - final MachineSourceImpl machineSource = new MachineSourceImpl("type", "location"); + final MachineSourceImpl machineSource = new MachineSourceImpl("type").setLocation("location"); final MachineImpl machine = new MachineImpl(new MachineConfigImpl(false, MACHINE_NAME, @@ -350,6 +365,8 @@ public void shouldCallCreationDockerInstanceWithFactoryOnCreateInstanceFromSnaps any(LineConsumer.class)); } + + @Test public void shouldCallCreationDockerInstanceWithFactoryOnCreateInstanceFromRecipe() throws Exception { String generatedContainerId = "genContainerId"; @@ -358,8 +375,7 @@ public void shouldCallCreationDockerInstanceWithFactoryOnCreateInstanceFromRecip eq(USER_NAME), eq(MACHINE_NAME)); - final MachineSourceImpl machineSource = new MachineSourceImpl("type", "location"); - final Recipe recipe = new RecipeImpl().withType("Dockerfile").withScript("FROM busybox"); + final MachineSourceImpl machineSource = new MachineSourceImpl(DOCKER_FILE_TYPE).setLocation("location"); final MachineImpl machine = new MachineImpl(new MachineConfigImpl(false, MACHINE_NAME, @@ -376,7 +392,7 @@ public void shouldCallCreationDockerInstanceWithFactoryOnCreateInstanceFromRecip MachineStatus.CREATING, null); - createInstanceFromRecipe(recipe, machine); + createInstanceFromRecipe(machine); verify(dockerMachineFactory).createInstance(eq(machine), @@ -472,6 +488,15 @@ public void shouldDisableSwapMemorySizeInContainersOnInstanceCreationFromRecipe( assertEquals(createContainerCaptor.getValue().getHostConfig().getMemorySwap(), -1); } + + @Test(expectedExceptions = InvalidRecipeException.class) + public void checkExceptionIfImageWithContent() throws Exception { + MachineImpl machine = getMachineBuilder().build(); + machine.getConfig().getSource().setContent("hello"); + machine.getConfig().getSource().setType(DOCKER_IMAGE_TYPE); + createInstanceFromRecipe(machine); + } + @Test public void shouldDisableSwapMemorySizeInContainersOnInstanceCreationFromSnapshot() throws Exception { createInstanceFromSnapshot(); @@ -502,6 +527,7 @@ public void shouldExposeCommonAndDevPortsToContainerOnDevInstanceCreationFromRec dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, devServers, commonServers, Collections.emptySet(), @@ -541,6 +567,7 @@ public void shouldExposeOnlyCommonPortsToContainerOnNonDevInstanceCreationFromRe dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), commonServers, Collections.emptySet(), @@ -586,6 +613,7 @@ public void shouldExposeCommonAndDevPortsToContainerOnDevInstanceCreationFromSna dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, devServers, commonServers, Collections.emptySet(), @@ -625,6 +653,7 @@ public void shouldExposeOnlyCommonPortsToContainerOnNonDevInstanceCreationFromSn dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), commonServers, Collections.emptySet(), @@ -665,6 +694,7 @@ public void shouldAddServersConfsPortsFromMachineConfigToExposedPortsOnNonDevIns dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -708,6 +738,7 @@ public void shouldAddServersConfsPortsFromMachineConfigToExposedPortsOnNonDevIns dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -751,6 +782,7 @@ public void shouldAddServersConfsPortsFromMachineConfigToExposedPortsOnDevInstan dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -794,6 +826,7 @@ public void shouldAddServersConfsPortsFromMachineConfigToExposedPortsOnDevInstan dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -832,6 +865,7 @@ public void shouldBindProjectsFSVolumeToContainerOnDevInstanceCreationFromRecipe dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -870,6 +904,7 @@ public void shouldBindProjectsFSVolumeToContainerOnDevInstanceCreationFromSnapsh dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -907,6 +942,7 @@ public void shouldNotBindProjectsFSVolumeToContainerOnNonDevInstanceCreationFrom dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -944,6 +980,7 @@ public void shouldNotBindProjectsFSVolumeToContainerOnNonDevInstanceCreationFrom dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -988,6 +1025,7 @@ public void shouldBindCommonAndDevVolumesToContainerOnDevInstanceCreationFromRec dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), devVolumes, @@ -1033,6 +1071,7 @@ public void shouldBindCommonAndDevVolumesToContainerOnDevInstanceCreationFromSna dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), devVolumes, @@ -1077,6 +1116,7 @@ public void shouldBindCommonVolumesOnlyToContainerOnNonDevInstanceCreationFromRe dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), devVolumes, @@ -1119,6 +1159,7 @@ public void shouldAddExtraHostOnDevInstanceCreationFromRecipe() throws Exception dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), devVolumes, @@ -1161,6 +1202,7 @@ public void shouldAddExtraHostOnDevInstanceCreationFromSnapshot() throws Excepti dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), devVolumes, @@ -1203,6 +1245,7 @@ public void shouldAddExtraHostOnNonDevInstanceCreationFromRecipe() throws Except dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), devVolumes, @@ -1245,6 +1288,7 @@ public void shouldAddExtraHostOnNonDevInstanceCreationFromSnapshot() throws Exce dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), devVolumes, @@ -1289,6 +1333,7 @@ public void shouldBindCommonVolumesOnlyToContainerOnNonDevInstanceCreationFromSn dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), devVolumes, @@ -1402,6 +1447,7 @@ public void shouldAddCommonAndDevEnvVariablesToContainerOnDevInstanceCreationFro dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -1436,6 +1482,7 @@ public void shouldNotAddDevEnvToCommonEnvVariablesToContainerOnNonDevInstanceCre dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -1475,6 +1522,7 @@ public void shouldAddCommonAndDevEnvVariablesToContainerOnDevInstanceCreationFro dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -1509,6 +1557,7 @@ public void shouldNotAddDevEnvToCommonEnvVariablesToContainerOnNonDevInstanceCre dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -1545,6 +1594,7 @@ public void shouldAddEnvVarsFromMachineConfigToContainerOnNonDevInstanceCreation dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -1589,6 +1639,7 @@ public void shouldAddEnvVarsFromMachineConfigToContainerOnDevInstanceCreationFro dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -1633,6 +1684,7 @@ public void shouldAddEnvVarsFromMachineConfigToContainerOnNonDevInstanceCreation dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -1677,6 +1729,7 @@ public void shouldAddEnvVarsFromMachineConfigToContainerOnDevInstanceCreationFro dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -1733,13 +1786,11 @@ private void createInstanceFromRecipe(int memorySizeInMB) throws Exception { } private void createInstanceFromSnapshot(String repo, String tag, String registry) throws NotFoundException, MachineException { - createInstanceFromSnapshot(getMachineBuilder().build(), new DockerInstanceKey(repo, tag, registry, "digest")); + createInstanceFromSnapshot(getMachineBuilder().build(), new DockerMachineSource(repo).withTag(tag).withRegistry(registry).withDigest("digest")); } private void createInstanceFromRecipe(Machine machine) throws Exception { - dockerInstanceProvider.createInstance(new RecipeImpl().withType("Dockerfile") - .withScript("FROM busybox"), - machine, + dockerInstanceProvider.createInstance(machine, LineConsumer.DEV_NULL); } @@ -1766,25 +1817,18 @@ private void createInstanceFromSnapshot(boolean isDev, String workspaceId) throw .build()); } - private void createInstanceFromRecipe(Recipe recipe, Machine machine) throws Exception { - dockerInstanceProvider.createInstance(recipe, - machine, + private void createInstanceFromSnapshot(MachineImpl machine) throws NotFoundException, MachineException { + DockerMachineSource machineSource = new DockerMachineSource("repo").withRegistry("registry").withDigest("digest"); + machine.getConfig().setSource(machineSource); + dockerInstanceProvider.createInstance(machine, LineConsumer.DEV_NULL); } - private void createInstanceFromSnapshot(Machine machine) throws NotFoundException, MachineException { - dockerInstanceProvider.createInstance(new DockerInstanceKey("repo", - "tag", - "registry", - "digest"), - machine, - LineConsumer.DEV_NULL); - } + private void createInstanceFromSnapshot(MachineImpl machine, DockerMachineSource dockerMachineSource) throws NotFoundException, + MachineException { - private void createInstanceFromSnapshot(Machine machine, DockerInstanceKey dockerInstanceKey) throws NotFoundException, - MachineException { - dockerInstanceProvider.createInstance(dockerInstanceKey, - machine, + machine.getConfig().setSource(dockerMachineSource); + dockerInstanceProvider.createInstance(machine, LineConsumer.DEV_NULL); } @@ -1804,6 +1848,7 @@ private DockerInstanceProvider getDockerInstanceProvider(boolean snapshotUseRegi dockerMachineFactory, dockerInstanceStopDetector, containerNameGenerator, + recipeRetriever, Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), @@ -1822,7 +1867,7 @@ private MachineConfigImpl.MachineConfigImplBuilder getMachineConfigBuilder() { return MachineConfigImpl.builder().fromConfig(new MachineConfigImpl(false, MACHINE_NAME, "machineType", - new MachineSourceImpl("source type", "source location"), + new MachineSourceImpl(DOCKER_FILE_TYPE).setContent("FROM codenvy"), new LimitsImpl(MEMORY_LIMIT_MB), asList(new ServerConfImpl("ref1", "8080", diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceTest.java b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceTest.java index 7ffc5d30259..f9e89ae3118 100644 --- a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceTest.java +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceTest.java @@ -12,6 +12,7 @@ import org.eclipse.che.api.core.model.machine.Machine; import org.eclipse.che.api.core.model.machine.MachineConfig; +import org.eclipse.che.api.core.model.machine.MachineSource; import org.eclipse.che.api.core.model.machine.MachineStatus; import org.eclipse.che.api.core.util.LineConsumer; import org.eclipse.che.api.machine.server.exception.MachineException; @@ -19,7 +20,6 @@ import org.eclipse.che.api.machine.server.model.impl.MachineConfigImpl; import org.eclipse.che.api.machine.server.model.impl.MachineImpl; import org.eclipse.che.api.machine.server.model.impl.MachineSourceImpl; -import org.eclipse.che.api.machine.server.spi.InstanceKey; import org.eclipse.che.plugin.docker.client.DockerConnector; import org.eclipse.che.plugin.docker.client.Exec; import org.eclipse.che.plugin.docker.client.LogMessage; @@ -47,7 +47,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; /** @@ -150,10 +150,13 @@ public void shouldCreateDockerImageLocally() throws Exception { @Test public void shouldSaveDockerInstanceStateIntoLocalImage() throws Exception { - final InstanceKey result = dockerInstance.saveToSnapshot(OWNER); + final MachineSource result = dockerInstance.saveToSnapshot(OWNER); - assertEquals(result.getFields().get("tag"), TAG); - assertFalse(result.getFields().containsKey("registry")); + assertTrue(result instanceof DockerMachineSource); + DockerMachineSource dockerMachineSource = (DockerMachineSource) result; + assertEquals(dockerMachineSource.getTag(), TAG); + assertNotNull(dockerMachineSource.getRepository()); + assertEquals(dockerMachineSource.getRegistry(), null); } @Test @@ -162,11 +165,13 @@ public void shouldSaveDockerInstanceStateIntoRepository() throws Exception { dockerInstance = getDockerInstance(getMachine(), REGISTRY, CONTAINER, IMAGE, true); when(dockerConnectorMock.push(any(PushParams.class), any(ProgressMonitor.class))).thenReturn(digest); - final InstanceKey result = dockerInstance.saveToSnapshot(OWNER); + final MachineSource result = dockerInstance.saveToSnapshot(OWNER); - assertEquals(result.getFields().get("tag"), TAG); - assertEquals(result.getFields().get("digest"), digest); - assertTrue(result.getFields().containsKey("registry")); + assertTrue(result instanceof DockerMachineSource); + DockerMachineSource dockerMachineSource = (DockerMachineSource) result; + assertEquals(dockerMachineSource.getTag(), TAG); + assertEquals(dockerMachineSource.getDigest(), digest); + assertEquals(dockerMachineSource.getRegistry(), REGISTRY); } @Test(expectedExceptions = MachineException.class) @@ -235,7 +240,7 @@ private MachineConfig getMachineConfig(boolean isDev, String name, String type) .setDev(isDev) .setName(name) .setType(type) - .setSource(new MachineSourceImpl("docker", "location")) + .setSource(new MachineSourceImpl("docker").setLocation("location")) .setLimits(new LimitsImpl(64)) .build(); } diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerMachineSourceTest.java b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerMachineSourceTest.java new file mode 100644 index 00000000000..8e04f615f4f --- /dev/null +++ b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerMachineSourceTest.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.docker.machine; + +import org.eclipse.che.api.core.model.machine.MachineSource; +import org.eclipse.che.api.machine.server.exception.MachineException; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; + +/** + * Check if MachineSource object used for Docker is working as expected. + * + * @author Florent Benoit + */ +@Listeners(MockitoTestNGListener.class) +public class DockerMachineSourceTest { + + @Mock + private MachineSource machineSource; + + @DataProvider(name = "image-ids") + public Object[][] messageProvider() { + + return new String[][]{ + {"docker-registry.company.com:5000/my-repository:some-tag", "docker-registry.company.com:5000", "my-repository", "some-tag", + null}, + {"my-repository", null, "my-repository", null, null}, + {"my-repository:tag", null, "my-repository", "tag", null}, + {"docker-registry.company.com:5000/my-repository", "docker-registry.company.com:5000", "my-repository", null, null}, + {"docker-registry.company.com:5000/my-repository:mytag@digest123", "docker-registry.company.com:5000", "my-repository", + "mytag", "digest123"}, + {"ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2", null, "ubuntu", null, + "sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2"}, + {"docker-registry:5000/ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2", + "docker-registry:5000", "ubuntu", null, "sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2"}, + }; + } + + + /** + * Check that all the constructor are valid and not throwing exception based on data provider + */ + @Test(dataProvider = "image-ids") + public void testConstructors(String location, String registry, String repository, String tag, String digest) throws MachineException { + DockerMachineSource source1 = new DockerMachineSource(repository).withTag(tag).withRegistry(registry).withDigest(digest); + assertEquals(source1.getLocation(), location); + + DockerMachineSource source2 = new DockerMachineSource(source1); + assertEquals(source2.getLocation(), location); + assertEquals(source2.getRegistry(), registry); + assertEquals(source2.getRepository(), repository); + assertEquals(source2.getTag(), tag); + assertEquals(source2.getDigest(), digest); + + + DockerMachineSource source3 = new DockerMachineSource(repository); + source3.setTag(tag); + source3.setRegistry(registry); + source3.setDigest(digest); + assertEquals(source3.getLocation(), location); + + + } + + + /** + * Check valid source type + */ + @Test(expectedExceptions = MachineException.class) + public void testInvalidSourceType() throws MachineException { + when(machineSource.getType()).thenReturn("invalid"); + new DockerMachineSource(machineSource); + } + + /** + * Check invalid format + */ + @Test(expectedExceptions = MachineException.class) + public void testInvalidFormat() throws MachineException { + when(machineSource.getType()).thenReturn("image"); + when(machineSource.getLocation()).thenReturn("@image"); + new DockerMachineSource(machineSource); + } + +} diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/machine/MachineManagerImpl.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/machine/MachineManagerImpl.java index 8c7d70c3698..637d37d0dbd 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/machine/MachineManagerImpl.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/machine/MachineManagerImpl.java @@ -14,12 +14,8 @@ import com.google.inject.Singleton; import com.google.web.bindery.event.shared.EventBus; +import org.eclipse.che.api.core.model.machine.MachineSource; import org.eclipse.che.api.core.rest.shared.dto.LinkParameter; -import org.eclipse.che.ide.api.machine.DevMachine; -import org.eclipse.che.ide.api.machine.MachineManager; -import org.eclipse.che.ide.api.machine.MachineServiceClient; -import org.eclipse.che.ide.api.machine.OutputMessageUnmarshaller; -import org.eclipse.che.ide.api.machine.events.DevMachineStateEvent; import org.eclipse.che.api.machine.shared.dto.LimitsDto; import org.eclipse.che.api.machine.shared.dto.MachineConfigDto; import org.eclipse.che.api.machine.shared.dto.MachineDto; @@ -28,11 +24,16 @@ import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.api.promises.client.Promise; +import org.eclipse.che.ide.api.app.AppContext; +import org.eclipse.che.ide.api.machine.DevMachine; +import org.eclipse.che.ide.api.machine.MachineManager; +import org.eclipse.che.ide.api.machine.MachineServiceClient; +import org.eclipse.che.ide.api.machine.OutputMessageUnmarshaller; +import org.eclipse.che.ide.api.machine.events.DevMachineStateEvent; +import org.eclipse.che.ide.api.parts.PerspectiveManager; import org.eclipse.che.ide.api.workspace.WorkspaceServiceClient; import org.eclipse.che.ide.api.workspace.event.WorkspaceStoppedEvent; import org.eclipse.che.ide.api.workspace.event.WorkspaceStoppedHandler; -import org.eclipse.che.ide.api.app.AppContext; -import org.eclipse.che.ide.api.parts.PerspectiveManager; import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.extension.machine.client.machine.MachineStatusNotifier.RunningListener; import org.eclipse.che.ide.extension.machine.client.machine.console.MachineConsolePresenter; @@ -45,11 +46,11 @@ import org.eclipse.che.ide.websocket.rest.SubscriptionHandler; import org.eclipse.che.ide.websocket.rest.Unmarshallable; +import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_GET_MACHINE_LOGS_CHANNEL; +import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_GET_MACHINE_STATUS_CHANNEL; import static org.eclipse.che.ide.api.machine.MachineManager.MachineOperationType.DESTROY; import static org.eclipse.che.ide.api.machine.MachineManager.MachineOperationType.RESTART; import static org.eclipse.che.ide.api.machine.MachineManager.MachineOperationType.START; -import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_GET_MACHINE_LOGS_CHANNEL; -import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_GET_MACHINE_STATUS_CHANNEL; import static org.eclipse.che.ide.extension.machine.client.perspective.OperationsPerspective.OPERATIONS_PERSPECTIVE_ID; import static org.eclipse.che.ide.ui.loaders.initialization.InitialLoadingInfo.Operations.MACHINE_BOOTING; import static org.eclipse.che.ide.ui.loaders.initialization.OperationInfo.Status.ERROR; @@ -189,11 +190,11 @@ public void onMachineRunning(MachineStateEvent event) { @Override public void onMachineDestroyed(MachineStateEvent event) { if (isMachineRestarting) { - final String recipeUrl = machineState.getConfig().getSource().getLocation(); + final MachineSource machineSource = machineState.getConfig().getSource(); final String displayName = machineState.getConfig().getName(); final boolean isDev = machineState.getConfig().isDev(); - startMachine(recipeUrl, displayName, isDev, RESTART, "dockerfile", "docker"); + startMachine(asDto(machineSource), displayName, isDev, RESTART, "docker"); isMachineRestarting = false; } @@ -208,6 +209,17 @@ public void apply(Void arg) throws OperationException { }); } + /** + * Converts {@link MachineSource} to {@link MachineSourceDto}. + */ + public MachineSourceDto asDto(MachineSource source) { + return this.dtoFactory.createDto(MachineSourceDto.class) + .withType(source.getType()) + .withLocation(source.getLocation()) + .withContent(source.getContent()); + } + + /** Start new machine. */ @Override public void startMachine(String recipeURL, String displayName) { @@ -220,6 +232,8 @@ public void startDevMachine(String recipeURL, String displayName) { startMachine(recipeURL, displayName, true, START, "dockerfile", "docker"); } + + /** * @param recipeURL * @param displayName @@ -236,17 +250,32 @@ private void startMachine(final String recipeURL, final MachineOperationType operationType, final String sourceType, final String machineType) { + MachineSourceDto sourceDto = dtoFactory.createDto(MachineSourceDto.class).withType(sourceType).withLocation(recipeURL); + startMachine(sourceDto, displayName, isDev, operationType, machineType); + } + /** + * @param machineSourceDto + * @param displayName + * @param isDev + * @param operationType + * @param machineType + * "docker" or "ssh" + */ + private void startMachine(final MachineSourceDto machineSourceDto, + final String displayName, + final boolean isDev, + final MachineOperationType operationType, + final String machineType) { LimitsDto limitsDto = dtoFactory.createDto(LimitsDto.class).withRam(1024); if (isDev) { limitsDto.withRam(3072); } - MachineSourceDto sourceDto = dtoFactory.createDto(MachineSourceDto.class).withType(sourceType).withLocation(recipeURL); MachineConfigDto configDto = dtoFactory.createDto(MachineConfigDto.class) .withDev(isDev) .withName(displayName) - .withSource(sourceDto) + .withSource(machineSourceDto) .withLimits(limitsDto) .withType(machineType); diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/development/DevelopmentCategoryPresenter.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/development/DevelopmentCategoryPresenter.java index c945e445ba4..818bde512f7 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/development/DevelopmentCategoryPresenter.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/development/DevelopmentCategoryPresenter.java @@ -83,6 +83,7 @@ public boolean onRestoreTargetFields(DevelopmentMachineTarget target) { target.setOwner(machine.getOwner()); target.setType(machine.getConfig().getType()); target.setSourceType(machine.getConfig().getSource().getType()); + target.setSourceContent(machine.getConfig().getSource().getContent()); target.setSourceUrl(machine.getConfig().getSource().getLocation()); return true; diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/development/DevelopmentMachineTarget.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/development/DevelopmentMachineTarget.java index 87303b300a0..2d9f5387c10 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/development/DevelopmentMachineTarget.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/development/DevelopmentMachineTarget.java @@ -25,6 +25,7 @@ public class DevelopmentMachineTarget extends BaseTarget { private String owner; private String sourceType; private String sourceUrl; + private String sourceContent; public void setType(String type) { @@ -59,6 +60,14 @@ public String getSourceUrl() { return sourceUrl; } + public void setSourceContent(String sourceContent) { + this.sourceContent = sourceContent; + } + + public String getSourceContent() { + return sourceContent; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -77,11 +86,12 @@ public boolean equals(Object o) { && Objects.equals(getType(), other.getType()) && Objects.equals(getOwner(), other.getOwner()) && Objects.equals(getSourceType(), other.getSourceType()) + && Objects.equals(getSourceContent(), other.getSourceContent()) && Objects.equals(getSourceUrl(), other.getSourceUrl()); } @Override public int hashCode() { - return Objects.hash(getName(), getCategory(), getRecipe(), getType(), getOwner(), getSourceType(), getSourceUrl()); + return Objects.hash(getName(), getCategory(), getRecipe(), getType(), getOwner(), getSourceType(), getSourceUrl(), getSourceContent()); } } diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/docker/DockerCategoryPresenter.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/docker/DockerCategoryPresenter.java index 9727e8d8a1d..42239779076 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/docker/DockerCategoryPresenter.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/docker/DockerCategoryPresenter.java @@ -113,6 +113,7 @@ public boolean onRestoreTargetFields(DockerMachineTarget target) { target.setOwner(machine.getOwner()); target.setType(machine.getConfig().getType()); target.setSourceType(machine.getConfig().getSource().getType()); + target.setSourceContent(machine.getConfig().getSource().getContent()); target.setSourceUrl(machine.getConfig().getSource().getLocation()); return true; diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/docker/DockerMachineTarget.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/docker/DockerMachineTarget.java index 4cb24bc3eff..fd1bb6102fe 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/docker/DockerMachineTarget.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/targets/categories/docker/DockerMachineTarget.java @@ -25,6 +25,7 @@ public class DockerMachineTarget extends BaseTarget { private String owner; private String sourceType; private String sourceUrl; + private String sourceContent; public void setType(String type) { @@ -59,6 +60,15 @@ public String getSourceUrl() { return sourceUrl; } + public void setSourceContent(String sourceContent) { + this.sourceContent = sourceContent; + } + + public String getSourceContent() { + return sourceContent; + } + + @Override public boolean equals(Object o) { if (this == o) { @@ -77,11 +87,12 @@ public boolean equals(Object o) { && Objects.equals(getType(), other.getType()) && Objects.equals(getOwner(), other.getOwner()) && Objects.equals(getSourceType(), other.getSourceType()) + && Objects.equals(getSourceContent(), other.getSourceContent()) && Objects.equals(getSourceUrl(), other.getSourceUrl()); } @Override public int hashCode() { - return Objects.hash(getName(), getCategory(), getRecipe(), getType(), getOwner(), getSourceType(), getSourceUrl()); + return Objects.hash(getName(), getCategory(), getRecipe(), getType(), getOwner(), getSourceType(), getSourceUrl(), getSourceContent()); } } diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/machine/MachineManagerImplTest.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/machine/MachineManagerImplTest.java new file mode 100644 index 00000000000..6b150d7428f --- /dev/null +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/machine/MachineManagerImplTest.java @@ -0,0 +1,181 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.extension.machine.client.machine; + +import com.google.web.bindery.event.shared.EventBus; + +import org.eclipse.che.api.core.model.machine.MachineConfig; +import org.eclipse.che.api.core.model.machine.MachineSource; +import org.eclipse.che.api.machine.shared.dto.LimitsDto; +import org.eclipse.che.api.machine.shared.dto.MachineConfigDto; +import org.eclipse.che.api.machine.shared.dto.MachineDto; +import org.eclipse.che.api.machine.shared.dto.MachineSourceDto; +import org.eclipse.che.api.promises.client.Operation; +import org.eclipse.che.api.promises.client.OperationException; +import org.eclipse.che.api.promises.client.Promise; +import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto; +import org.eclipse.che.ide.api.app.AppContext; +import org.eclipse.che.ide.api.machine.MachineServiceClient; +import org.eclipse.che.ide.api.parts.PerspectiveManager; +import org.eclipse.che.ide.api.workspace.WorkspaceServiceClient; +import org.eclipse.che.ide.dto.DtoFactory; +import org.eclipse.che.ide.extension.machine.client.machine.console.MachineConsolePresenter; +import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; +import org.eclipse.che.ide.ui.loaders.initialization.InitialLoadingInfo; +import org.eclipse.che.ide.websocket.MessageBusProvider; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Matchers; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Check {@link MachineManagerImpl} + * + * @author Florent Benoit + */ +@RunWith(MockitoJUnitRunner.class) +public class MachineManagerImplTest { + + @Mock + private DtoUnmarshallerFactory dtoUnmarshallerFactor; + + @Mock + private MachineServiceClient machineServiceClient; + + @Mock + private WorkspaceServiceClient workspaceServiceClient; + + @Mock + private MachineConsolePresenter machineConsolePresenter; + + @Mock + private MachineStatusNotifier machineStatusNotifier; + + @Mock + private MessageBusProvider messageBusProvider; + + @Mock + private InitialLoadingInfo initialLoadingInfo; + + @Mock + private PerspectiveManager perspectiveManager; + + @Mock + private EventBus eventBus; + + @Mock + private AppContext appContext; + + @Mock + private DtoFactory dtoFactory; + + @Captor + private ArgumentCaptor startWorkspaceHandlerCaptor; + + @Captor + private ArgumentCaptor> operationArgumentCaptor; + + @Captor + private ArgumentCaptor machineConfigDtoArgumentCaptor; + + + @InjectMocks + private MachineManagerImpl machineManager; + + /** + * Check a valid source object is used on machine destroyed with restart flag + * + * @throws OperationException + * if restart fails + */ + @Test + public void checkUseValidSource() throws OperationException { + final String ID = "id"; + final String DISPLAY_NAME = "my-display-name"; + final boolean IS_DEV = true; + + final String SOURCE_TYPE = "source-type"; + final String SOURCE_LOCATION = "source-location"; + final String SOURCE_CONTENT = "source-content"; + + org.eclipse.che.api.core.model.machine.Machine machineState = mock(org.eclipse.che.api.core.model.machine.Machine.class); + when(machineState.getId()).thenReturn(ID); + Promise promise = mock(Promise.class); + Promise promiseThen = mock(Promise.class); + when(machineServiceClient.destroyMachine(eq(ID))).thenReturn(promise); + when(promise.then(Matchers.>anyObject())).thenReturn(promiseThen); + machineManager.restartMachine(machineState); + + verify(promiseThen).then(operationArgumentCaptor.capture()); + operationArgumentCaptor.getValue().apply(null); + + verify(eventBus).addHandler(eq(MachineStateEvent.TYPE), startWorkspaceHandlerCaptor.capture()); + MachineStateEvent.Handler handler = startWorkspaceHandlerCaptor.getValue(); + + MachineSource machineSource = mock(MachineSource.class); + MachineConfig machineConfig = mock(MachineConfig.class); + when(machineState.getConfig()).thenReturn(machineConfig); + when(machineConfig.getSource()).thenReturn(machineSource); + when(machineConfig.getName()).thenReturn(DISPLAY_NAME); + when(machineConfig.isDev()).thenReturn(IS_DEV); + when(machineSource.getType()).thenReturn(SOURCE_TYPE); + when(machineSource.getLocation()).thenReturn(SOURCE_LOCATION); + when(machineSource.getContent()).thenReturn(SOURCE_CONTENT); + + + MachineSourceDto machineSourceDto = mock(MachineSourceDto.class); + when(machineSourceDto.withType(eq(SOURCE_TYPE))).thenReturn(machineSourceDto); + when(machineSourceDto.withLocation(eq(SOURCE_LOCATION))).thenReturn(machineSourceDto); + when(machineSourceDto.withContent(eq(SOURCE_CONTENT))).thenReturn(machineSourceDto); + + when(dtoFactory.createDto(MachineSourceDto.class)).thenReturn(machineSourceDto); + + + LimitsDto limitsDto = mock(LimitsDto.class); + when(dtoFactory.createDto(LimitsDto.class)).thenReturn(limitsDto); + when(limitsDto.withRam(anyInt())).thenReturn(limitsDto); + + MachineConfigDto machineConfigDto = mock(MachineConfigDto.class); + when(dtoFactory.createDto(MachineConfigDto.class)).thenReturn(machineConfigDto); + when(machineConfigDto.withDev(anyBoolean())).thenReturn(machineConfigDto); + when(machineConfigDto.withName(anyString())).thenReturn(machineConfigDto); + when(machineConfigDto.withSource(machineSourceDto)).thenReturn(machineConfigDto); + when(machineConfigDto.withLimits(limitsDto)).thenReturn(machineConfigDto); + when(machineConfigDto.withType(anyString())).thenReturn(machineConfigDto); + + WorkspaceDto workspaceDto = mock(WorkspaceDto.class); + when(appContext.getWorkspace()).thenReturn(workspaceDto); + when(workspaceDto.getId()).thenReturn(ID); + + Promise promiseEmpty = mock(Promise.class); + when(workspaceServiceClient.createMachine(anyString(), any(MachineConfigDto.class))).thenReturn(promiseEmpty); + + handler.onMachineDestroyed(null); + verify(workspaceServiceClient).createMachine(eq(ID), machineConfigDtoArgumentCaptor.capture()); + verify(machineSourceDto).withType(eq(SOURCE_TYPE)); + verify(machineSourceDto).withLocation(eq(SOURCE_LOCATION)); + verify(machineSourceDto).withContent(eq(SOURCE_CONTENT)); + } + +} diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java index e364b02271d..7ddf6925460 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java @@ -15,13 +15,13 @@ import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.model.machine.Command; import org.eclipse.che.api.core.model.machine.Machine; +import org.eclipse.che.api.core.model.machine.MachineSource; import org.eclipse.che.api.core.model.machine.ServerConf; import org.eclipse.che.api.core.util.LineConsumer; import org.eclipse.che.api.machine.server.exception.MachineException; import org.eclipse.che.api.machine.server.model.impl.MachineRuntimeInfoImpl; import org.eclipse.che.api.machine.server.model.impl.ServerImpl; import org.eclipse.che.api.machine.server.spi.Instance; -import org.eclipse.che.api.machine.server.spi.InstanceKey; import org.eclipse.che.api.machine.server.spi.InstanceNode; import org.eclipse.che.api.machine.server.spi.InstanceProcess; import org.eclipse.che.api.machine.server.spi.impl.AbstractInstance; @@ -140,7 +140,7 @@ public InstanceProcess createProcess(Command command, String outputChannel) thro * {@inheritDoc} */ @Override - public InstanceKey saveToSnapshot(String owner) throws MachineException { + public MachineSource saveToSnapshot(String owner) throws MachineException { throw new MachineException("Snapshot feature is unsupported for ssh machine implementation"); } diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProvider.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProvider.java index 399a56b7ba2..0be7d5ed197 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProvider.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProvider.java @@ -15,13 +15,16 @@ import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.model.machine.Machine; import org.eclipse.che.api.core.model.machine.MachineConfig; +import org.eclipse.che.api.core.model.machine.MachineSource; import org.eclipse.che.api.core.model.machine.Recipe; import org.eclipse.che.api.core.util.LineConsumer; +import org.eclipse.che.api.machine.server.exception.InvalidRecipeException; import org.eclipse.che.api.machine.server.exception.MachineException; import org.eclipse.che.api.machine.server.exception.SnapshotException; +import org.eclipse.che.api.machine.server.exception.UnsupportedRecipeException; import org.eclipse.che.api.machine.server.spi.Instance; -import org.eclipse.che.api.machine.server.spi.InstanceKey; import org.eclipse.che.api.machine.server.spi.InstanceProvider; +import org.eclipse.che.api.machine.server.util.RecipeDownloader; import javax.inject.Inject; import java.io.IOException; @@ -45,10 +48,12 @@ public class SshMachineInstanceProvider implements InstanceProvider { private final Set supportedRecipeTypes; private final SshMachineFactory sshMachineFactory; + private final RecipeDownloader recipeDownloader; @Inject - public SshMachineInstanceProvider(SshMachineFactory sshMachineFactory) throws IOException { + public SshMachineInstanceProvider(SshMachineFactory sshMachineFactory, RecipeDownloader recipeDownloader) throws IOException { this.sshMachineFactory = sshMachineFactory; + this.recipeDownloader = recipeDownloader; this.supportedRecipeTypes = Collections.singleton("ssh-config"); } @@ -62,18 +67,37 @@ public Set getRecipeTypes() { return supportedRecipeTypes; } + /** + * Creates instance from scratch or by reusing a previously one by using specified {@link MachineSource} + * data in {@link MachineConfig}. + * + * @param machine + * machine description + * @param lineConsumer + * output for instance creation logs + * @return newly created {@link Instance} + * @throws UnsupportedRecipeException + * if specified {@code recipe} is not supported + * @throws InvalidRecipeException + * if {@code recipe} is invalid + * @throws NotFoundException + * if instance described by {@link MachineSource} doesn't exists + * @throws MachineException + * if other error occurs + */ @Override - public Instance createInstance(Recipe recipe, - Machine machine, - LineConsumer machineLogsConsumer) throws MachineException { + public Instance createInstance(Machine machine, LineConsumer lineConsumer) + throws UnsupportedRecipeException, InvalidRecipeException, NotFoundException, MachineException { requireNonNull(machine, "Non null machine required"); - requireNonNull(machineLogsConsumer, "Non null logs consumer required"); + requireNonNull(lineConsumer, "Non null logs consumer required"); + requireNonNull(machine.getConfig().getSource().getLocation(), "Location in machine source is required"); if (machine.getConfig().isDev()) { throw new MachineException("Dev machine is not supported for Ssh machine implementation"); } - SshMachineRecipe sshMachineRecipe = parseRecipe(recipe); + Recipe recipe = recipeDownloader.getRecipe(machine.getConfig()); + SshMachineRecipe sshMachineRecipe = GSON.fromJson(recipe.getScript(), SshMachineRecipe.class); SshClient sshClient = sshMachineFactory.createSshClient(sshMachineRecipe, machine.getConfig().getEnvVariables()); @@ -81,22 +105,20 @@ public Instance createInstance(Recipe recipe, return sshMachineFactory.createInstance(machine, sshClient, - machineLogsConsumer); + lineConsumer); } + /** + * Removes snapshot of the instance in implementation specific way. + * + * @param machineSource + * contains implementation specific key of the snapshot of the instance that should be removed + * @throws SnapshotException + * if exception occurs on instance snapshot removal + */ @Override - public Instance createInstance(InstanceKey instanceKey, - Machine machine, - LineConsumer creationLogsOutput) throws NotFoundException, MachineException { - throw new MachineException("Snapshot feature is unsupported for ssh machine implementation"); - } - - @Override - public void removeInstanceSnapshot(InstanceKey instanceKey) throws SnapshotException { + public void removeInstanceSnapshot(MachineSource machineSource) throws SnapshotException { throw new SnapshotException("Snapshot feature is unsupported for ssh machine implementation"); } - private SshMachineRecipe parseRecipe(Recipe recipe) { - return GSON.fromJson(recipe.getScript(), SshMachineRecipe.class); - } } diff --git a/plugins/plugin-ssh-machine/src/test/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProviderTest.java b/plugins/plugin-ssh-machine/src/test/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProviderTest.java index 7f6a026ee27..1f67c2879ad 100644 --- a/plugins/plugin-ssh-machine/src/test/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProviderTest.java +++ b/plugins/plugin-ssh-machine/src/test/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProviderTest.java @@ -24,14 +24,14 @@ import org.eclipse.che.api.machine.server.model.impl.ServerConfImpl; import org.eclipse.che.api.machine.server.recipe.RecipeImpl; import org.eclipse.che.api.machine.server.spi.Instance; -import org.eclipse.che.api.machine.server.spi.InstanceKey; +import org.eclipse.che.api.machine.server.util.RecipeDownloader; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.testng.MockitoTestNGListener; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Listeners; import org.testng.annotations.Test; -import java.util.Collections; import java.util.HashSet; import static java.util.Collections.singletonList; @@ -47,6 +47,9 @@ */ @Listeners(MockitoTestNGListener.class) public class SshMachineInstanceProviderTest { + @Mock + private RecipeDownloader recipeDownloader; + @Mock private SshMachineFactory sshMachineFactory; @Mock @@ -54,13 +57,13 @@ public class SshMachineInstanceProviderTest { @Mock private SshMachineInstance sshMachineInstance; + @InjectMocks private SshMachineInstanceProvider provider; private RecipeImpl recipe; private MachineImpl machine; @BeforeMethod public void setUp() throws Exception { - provider = new SshMachineInstanceProvider(sshMachineFactory); machine = createMachine(); SshMachineRecipe sshMachineRecipe = new SshMachineRecipe("localhost", 22, @@ -80,14 +83,6 @@ public void shouldReturnCorrectRecipeTypes() throws Exception { assertEquals(provider.getRecipeTypes(), new HashSet<>(singletonList("ssh-config"))); } - @Test(expectedExceptions = MachineException.class, - expectedExceptionsMessageRegExp = "Snapshot feature is unsupported for ssh machine implementation") - public void shouldThrowMachineExceptionOnCreateInstanceFromSnapshot() throws Exception { - InstanceKey instanceKey = () -> Collections.EMPTY_MAP; - - provider.createInstance(instanceKey, null, null); - } - @Test(expectedExceptions = SnapshotException.class, expectedExceptionsMessageRegExp = "Snapshot feature is unsupported for ssh machine implementation") public void shouldThrowSnapshotExceptionOnRemoveSnapshot() throws Exception { @@ -99,15 +94,25 @@ public void shouldThrowSnapshotExceptionOnRemoveSnapshot() throws Exception { public void shouldThrowExceptionOnDevMachineCreationFromRecipe() throws Exception { Machine machine = createMachine(true); - provider.createInstance(recipe, machine, LineConsumer.DEV_NULL); + provider.createInstance(machine, LineConsumer.DEV_NULL); + } + + @Test(expectedExceptions = NullPointerException.class, + expectedExceptionsMessageRegExp = "Location in machine source is required") + public void shouldThrowExceptionInvalidMachineConfigSource() throws Exception { + MachineImpl machine = createMachine(true); + machine.getConfig().setSource(new MachineSourceImpl("ssh-config").setContent("hello")); + + provider.createInstance(machine, LineConsumer.DEV_NULL); } @Test public void shouldBeAbleToCreateSshMachineInstanceOnMachineCreationFromRecipe() throws Exception { when(sshMachineFactory.createSshClient(any(SshMachineRecipe.class), anyMap())).thenReturn(sshClient); when(sshMachineFactory.createInstance(eq(machine), eq(sshClient), any(LineConsumer.class))).thenReturn(sshMachineInstance); + when(recipeDownloader.getRecipe(eq(machine.getConfig()))).thenReturn(recipe); - Instance instance = provider.createInstance(recipe, machine, LineConsumer.DEV_NULL); + Instance instance = provider.createInstance(machine, LineConsumer.DEV_NULL); assertEquals(instance, sshMachineInstance); } @@ -125,8 +130,7 @@ private MachineImpl createMachine(boolean isDev) { "10011/tcp", "http", null))) - .setSource(new MachineSourceImpl("ssh-config", - "localhost:10012/recipe")) + .setSource(new MachineSourceImpl("ssh-config").setLocation("localhost:10012/recipe")) .setType("ssh") .build(); return MachineImpl.builder() diff --git a/wsmaster/che-core-api-machine-shared/src/main/java/org/eclipse/che/api/machine/shared/dto/MachineSourceDto.java b/wsmaster/che-core-api-machine-shared/src/main/java/org/eclipse/che/api/machine/shared/dto/MachineSourceDto.java index eaed400a172..02ca3d3c7a6 100644 --- a/wsmaster/che-core-api-machine-shared/src/main/java/org/eclipse/che/api/machine/shared/dto/MachineSourceDto.java +++ b/wsmaster/che-core-api-machine-shared/src/main/java/org/eclipse/che/api/machine/shared/dto/MachineSourceDto.java @@ -15,6 +15,7 @@ import org.eclipse.che.dto.shared.DTO; import static org.eclipse.che.api.core.factory.FactoryParameter.Obligation.MANDATORY; +import static org.eclipse.che.api.core.factory.FactoryParameter.Obligation.OPTIONAL; /** * @author Alexander Garagatyi @@ -30,10 +31,33 @@ public interface MachineSourceDto extends MachineSource { MachineSourceDto withType(String type); @Override - @FactoryParameter(obligation = MANDATORY) + @FactoryParameter(obligation = OPTIONAL) String getLocation(); void setLocation(String location); MachineSourceDto withLocation(String location); + + /** + * @return content of the machine source. No need to use an external link. + */ + @Override + @FactoryParameter(obligation = OPTIONAL) + String getContent(); + + /** + * Defines the new content to use for this machine source. + * Alternate way is to provide a location + * @param content the content instead of an external link like with location + */ + void setContent(String content); + + /** + * Defines the new content to use for this machine source. + * Alternate way is to provide a location + * @param content the content instead of an external link like with location + * @return the current intance of the object + */ + MachineSourceDto withContent(String content); + } diff --git a/wsmaster/che-core-api-machine/pom.xml b/wsmaster/che-core-api-machine/pom.xml index ed048557ee2..0b160d1776b 100644 --- a/wsmaster/che-core-api-machine/pom.xml +++ b/wsmaster/che-core-api-machine/pom.xml @@ -49,6 +49,10 @@ javax.inject javax.inject + + javax.validation + validation-api + javax.ws.rs javax.ws.rs-api diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/DtoConverter.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/DtoConverter.java index c4c1f6f9ff9..7afd64ee21f 100644 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/DtoConverter.java +++ b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/DtoConverter.java @@ -61,7 +61,7 @@ public static MachineConfigDto asDto(MachineConfig config) { * Converts {@link MachineSource} to {@link MachineSourceDto}. */ public static MachineSourceDto asDto(MachineSource source) { - return newDto(MachineSourceDto.class).withType(source.getType()).withLocation(source.getLocation()); + return newDto(MachineSourceDto.class).withType(source.getType()).withLocation(source.getLocation()).withContent(source.getContent()); } /** diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/MachineManager.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/MachineManager.java index c0d41c33693..111fb6056dd 100644 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/MachineManager.java +++ b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/MachineManager.java @@ -22,7 +22,6 @@ import org.eclipse.che.api.core.model.machine.Machine; import org.eclipse.che.api.core.model.machine.MachineConfig; import org.eclipse.che.api.core.model.machine.MachineStatus; -import org.eclipse.che.api.core.model.machine.Recipe; import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.api.core.notification.EventSubscriber; import org.eclipse.che.api.core.util.CompositeLineConsumer; @@ -39,10 +38,8 @@ import org.eclipse.che.api.machine.server.model.impl.MachineImpl; import org.eclipse.che.api.machine.server.model.impl.SnapshotImpl; import org.eclipse.che.api.machine.server.spi.Instance; -import org.eclipse.che.api.machine.server.spi.InstanceKey; import org.eclipse.che.api.machine.server.spi.InstanceProcess; import org.eclipse.che.api.machine.server.spi.InstanceProvider; -import org.eclipse.che.api.machine.server.util.RecipeDownloader; import org.eclipse.che.api.machine.server.wsagent.WsAgentLauncher; import org.eclipse.che.api.machine.shared.dto.event.MachineProcessEvent; import org.eclipse.che.api.machine.shared.dto.event.MachineStatusEvent; @@ -100,7 +97,6 @@ public class MachineManager { private final int defaultMachineMemorySizeMB; private final MachineCleaner machineCleaner; private final WsAgentLauncher wsAgentLauncher; - private final RecipeDownloader recipeDownloader; @Inject public MachineManager(SnapshotDao snapshotDao, @@ -109,13 +105,11 @@ public MachineManager(SnapshotDao snapshotDao, @Named("machine.logs.location") String machineLogsDir, EventService eventService, @Named("machine.default_mem_size_mb") int defaultMachineMemorySizeMB, - WsAgentLauncher wsAgentLauncher, - RecipeDownloader recipeDownloader) { + WsAgentLauncher wsAgentLauncher) { this.snapshotDao = snapshotDao; this.machineInstanceProviders = machineInstanceProviders; this.eventService = eventService; this.wsAgentLauncher = wsAgentLauncher; - this.recipeDownloader = recipeDownloader; this.machineLogsDir = new File(machineLogsDir); this.machineRegistry = machineRegistry; this.defaultMachineMemorySizeMB = defaultMachineMemorySizeMB; @@ -256,12 +250,10 @@ public MachineImpl createMachineAsync(MachineConfig machineConfig, return createMachine(normalizeMachineConfig(machineConfig), workspaceId, environmentName, - (instanceProvider, recipe, instanceKey, machine, machineLogger) -> + (instanceProvider, machine, machineLogger) -> executor.execute(ThreadLocalPropagateContext.wrap(() -> { try { createInstance(instanceProvider, - recipe, - instanceKey, machine, machineLogger); } catch (MachineException | NotFoundException e) { @@ -297,14 +289,6 @@ private MachineImpl createMachine(MachineConfigImpl machineConfig, machineConfig.getName())); } - Recipe recipe = null; - InstanceKey instanceKey = null; - if (snapshot != null) { - instanceKey = snapshot.getInstanceKey(); - } else { - recipe = recipeDownloader.getRecipe(machineConfig); - } - if (!MACHINE_DISPLAY_NAME_PATTERN.matcher(machineConfig.getName()).matches()) { throw new BadRequestException("Invalid machine name " + machineConfig.getName()); } @@ -315,6 +299,11 @@ private MachineImpl createMachine(MachineConfigImpl machineConfig, } } + // recover key from snapshot if there is one + if (snapshot != null) { + machineConfig.setSource(snapshot.getMachineSource()); + } + final String machineId = generateMachineId(); final String creator = EnvironmentContext.getCurrent().getSubject().getUserId(); @@ -339,7 +328,7 @@ private MachineImpl createMachine(MachineConfigImpl machineConfig, try { machineRegistry.addMachine(machine); - instanceCreator.createInstance(instanceProvider, recipe, instanceKey, machine, machineLogger); + instanceCreator.createInstance(instanceProvider, machine, machineLogger); return machine; } catch (ConflictException e) { @@ -348,8 +337,6 @@ private MachineImpl createMachine(MachineConfigImpl machineConfig, } private void createInstance(InstanceProvider instanceProvider, - Recipe recipe, - InstanceKey instanceKey, Machine machine, LineConsumer machineLogger) throws MachineException, NotFoundException { Instance instance = null; @@ -361,11 +348,7 @@ private void createInstance(InstanceProvider instanceProvider, .withWorkspaceId(machine.getWorkspaceId()) .withMachineName(machine.getConfig().getName())); - if (instanceKey == null) { - instance = instanceProvider.createInstance(recipe, machine, machineLogger); - } else { - instance = instanceProvider.createInstance(instanceKey, machine, machineLogger); - } + instance = instanceProvider.createInstance(machine, machineLogger); instance.setStatus(MachineStatus.RUNNING); @@ -408,7 +391,6 @@ private void createInstance(InstanceProvider instanceProvider, private interface MachineInstanceCreator { void createInstance(InstanceProvider instanceProvider, - Recipe recipe, InstanceKey instanceKey, Machine machineState, LineConsumer machineLogger) throws MachineException, NotFoundException; } @@ -590,7 +572,7 @@ public void removeSnapshot(String snapshotId) throws NotFoundException, Snapshot final SnapshotImpl snapshot = getSnapshot(snapshotId); final String instanceType = snapshot.getType(); final InstanceProvider instanceProvider = machineInstanceProviders.getProvider(instanceType); - instanceProvider.removeInstanceSnapshot(snapshot.getInstanceKey()); + instanceProvider.removeInstanceSnapshot(snapshot.getMachineSource()); snapshotDao.removeSnapshot(snapshotId); } @@ -814,14 +796,14 @@ private SnapshotImpl doSaveMachine(SnapshotImpl snapshot, Instance machine) thro machine.getId()); snapshotWithKey = new SnapshotImpl(snapshot); - snapshotWithKey.setInstanceKey(machine.saveToSnapshot(machine.getOwner())); + snapshotWithKey.setMachineSourceImpl(machine.saveToSnapshot(machine.getOwner())); try { SnapshotImpl oldSnapshot = snapshotDao.getSnapshot(snapshot.getWorkspaceId(), snapshot.getEnvName(), snapshot.getMachineName()); snapshotDao.removeSnapshot(oldSnapshot.getId()); - machineInstanceProviders.getProvider(oldSnapshot.getType()).removeInstanceSnapshot(oldSnapshot.getInstanceKey()); + machineInstanceProviders.getProvider(oldSnapshot.getType()).removeInstanceSnapshot(oldSnapshot.getMachineSource()); } catch (NotFoundException ignored) { //DO nothing if we has no snapshots or when provider not found } catch (SnapshotException se) { diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/MachineConfigImpl.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/MachineConfigImpl.java index 129379308fa..eb31c55ebaf 100644 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/MachineConfigImpl.java +++ b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/MachineConfigImpl.java @@ -91,6 +91,10 @@ public MachineSourceImpl getSource() { return source; } + public void setSource(MachineSource machineSource) { + this.source = new MachineSourceImpl(machineSource); + } + @Override public boolean isDev() { return isDev; diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/MachineSourceImpl.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/MachineSourceImpl.java index 0e814a2e2ff..43e215b18df 100644 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/MachineSourceImpl.java +++ b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/MachineSourceImpl.java @@ -14,6 +14,8 @@ import java.util.Objects; +import static java.util.Objects.hash; + //TODO move? /** @@ -25,15 +27,32 @@ public class MachineSourceImpl implements MachineSource { private String type; private String location; + private String content; + + protected MachineSourceImpl() { + + } + /** + * Please use {@link MachineSourceImpl with type and then setLocation or setContent} + * @param type the source type defined by implementation. + */ + @Deprecated public MachineSourceImpl(String type, String location) { + this(type); + setLocation(location); + } + + public MachineSourceImpl(String type) { this.type = type; - this.location = location; } public MachineSourceImpl(MachineSource machineSource) { - this.type = machineSource.getType(); - this.location = machineSource.getLocation(); + if (machineSource != null) { + this.type = machineSource.getType(); + this.location = machineSource.getLocation(); + this.content = machineSource.getContent(); + } } @Override @@ -51,6 +70,26 @@ public String getLocation() { return location; } + /** + * @return content of the machine source. No need to use an external link. + */ + @Override + public String getContent() { + return this.content; + } + + /** + * Defines the new content to use for this machine source. + * Alternate way is to provide a location + * + * @param content + * the content instead of an external link like with location + */ + public MachineSourceImpl setContent(String content) { + this.content = content; + return this; + } + public MachineSourceImpl setLocation(String location) { this.location = location; return this; @@ -61,22 +100,20 @@ public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof MachineSourceImpl)) return false; final MachineSourceImpl other = (MachineSourceImpl)obj; - return Objects.equals(type, other.type) && Objects.equals(location, other.location); + return Objects.equals(type, other.type) && Objects.equals(location, other.location) && Objects.equals(content, other.content); } @Override public int hashCode() { - int hash = 7; - hash = hash * 31 + Objects.hashCode(type); - hash = hash * 31 + Objects.hashCode(location); - return hash; + return hash(type, location, content); } @Override public String toString() { - return "MachineSourceImpl{" + + return MachineSourceImpl.class.getSimpleName() + "{" + "type='" + type + '\'' + ", location='" + location + '\'' + + ", content='" + content + '\'' + '}'; } } diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/SnapshotImpl.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/SnapshotImpl.java index 9e51adf2f6a..1dd1603a2ca 100644 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/SnapshotImpl.java +++ b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/SnapshotImpl.java @@ -11,8 +11,7 @@ package org.eclipse.che.api.machine.server.model.impl; import org.eclipse.che.api.core.model.machine.MachineConfig; -import org.eclipse.che.api.machine.server.spi.impl.InstanceKeyImpl; -import org.eclipse.che.api.machine.server.spi.InstanceKey; +import org.eclipse.che.api.core.model.machine.MachineSource; import org.eclipse.che.api.core.model.machine.Snapshot; import org.eclipse.che.commons.lang.NameGenerator; @@ -41,7 +40,7 @@ public static SnapshotBuilder builder() { private final long creationDate; private String description; - private InstanceKeyImpl instanceKey; + private MachineSourceImpl machineSource; public SnapshotImpl(Snapshot snapshot) { this(snapshot.getId(), @@ -58,7 +57,7 @@ public SnapshotImpl(Snapshot snapshot) { public SnapshotImpl(String id, String type, - InstanceKey instanceKey, + MachineSource machineSource, String namespace, long creationDate, String workspaceId, @@ -72,7 +71,7 @@ public SnapshotImpl(String id, this.workspaceId = requireNonNull(workspaceId, "Required non-null workspace id for snapshot"); this.machineName = requireNonNull(machineName, "Required non-null snapshot machine name"); this.envName = requireNonNull(envName, "Required non-null environment name for snapshot"); - this.instanceKey = instanceKey != null ? new InstanceKeyImpl(instanceKey) : null; + this.machineSource = machineSource != null ? new MachineSourceImpl(machineSource) : null; this.description = description; this.isDev = isDev; this.creationDate = creationDate; @@ -88,8 +87,8 @@ public String getType() { return type; } - public InstanceKey getInstanceKey() { - return instanceKey; + public MachineSourceImpl getMachineSource() { + return machineSource; } @Override @@ -127,8 +126,8 @@ public boolean isDev() { return this.isDev; } - public void setInstanceKey(InstanceKey instanceKey) { - this.instanceKey = instanceKey != null ? new InstanceKeyImpl(instanceKey.getFields()) : null; + public void setMachineSourceImpl(MachineSource machineSource) { + this.machineSource = machineSource != null ? new MachineSourceImpl(machineSource) : null; } public void setDescription(String description) { @@ -148,7 +147,7 @@ public boolean equals(Object o) { && isDev == snapshot.isDev && Objects.equals(id, snapshot.id) && Objects.equals(type, snapshot.type) - && Objects.equals(instanceKey, snapshot.instanceKey) + && Objects.equals(machineSource, snapshot.machineSource) && Objects.equals(namespace, snapshot.namespace) && Objects.equals(workspaceId, snapshot.workspaceId) && Objects.equals(description, snapshot.description) @@ -163,7 +162,7 @@ public int hashCode() { hash = hash * 31 + Boolean.hashCode(isDev); hash = hash * 31 + Objects.hashCode(id); hash = hash * 31 + Objects.hashCode(type); - hash = hash * 31 + Objects.hashCode(instanceKey); + hash = hash * 31 + Objects.hashCode(machineSource); hash = hash * 31 + Objects.hashCode(namespace); hash = hash * 31 + Objects.hashCode(workspaceId); hash = hash * 31 + Objects.hashCode(description); @@ -177,7 +176,7 @@ public String toString() { return "SnapshotImpl{" + "id='" + id + '\'' + ", type='" + type + '\'' + - ", instanceKey=" + instanceKey + + ", machineSource=" + machineSource + ", namespace='" + namespace + '\'' + ", creationDate=" + creationDate + ", isDev=" + isDev + @@ -200,7 +199,7 @@ public static class SnapshotBuilder { private String type; private String namespace; private String description; - private InstanceKey instanceKey; + private MachineSource machineSource; private boolean isDev; private long creationDate; @@ -250,8 +249,8 @@ public SnapshotBuilder setDescription(String description) { return this; } - public SnapshotBuilder setInstanceKey(InstanceKey instanceKey) { - this.instanceKey = instanceKey; + public SnapshotBuilder setMachineSource(MachineSource machineSource) { + this.machineSource = machineSource; return this; } @@ -271,7 +270,7 @@ public SnapshotBuilder useCurrentCreationDate() { } public SnapshotImpl build() { - return new SnapshotImpl(id, type, instanceKey, namespace, creationDate, workspaceId, description, isDev, machineName, envName); + return new SnapshotImpl(id, type, machineSource, namespace, creationDate, workspaceId, description, isDev, machineName, envName); } } } diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/adapter/MachineSourceAdapter.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/adapter/MachineSourceAdapter.java new file mode 100644 index 00000000000..85f0d9b07f4 --- /dev/null +++ b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/model/impl/adapter/MachineSourceAdapter.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.machine.server.model.impl.adapter; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import org.eclipse.che.api.core.model.machine.MachineSource; +import org.eclipse.che.api.machine.server.model.impl.MachineSourceImpl; + +import java.lang.reflect.Type; + +/** + * Type adapter for {@link MachineSource}. + * + * @author Florent Benoit + */ +public class MachineSourceAdapter implements JsonDeserializer, JsonSerializer { + + @Override + public MachineSource deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException { + return context.deserialize(jsonElement, MachineSourceImpl.class); + } + + @Override + public JsonElement serialize(MachineSource machineSource, Type type, JsonSerializationContext context) { + final JsonObject jsonObject = new JsonObject(); + + // we can't rely on MachineSourceImpl as custom InstanceProvider can build their own implementation + jsonObject.addProperty("content", machineSource.getContent()); + jsonObject.addProperty("location", machineSource.getLocation()); + jsonObject.addProperty("type", machineSource.getType()); + + return jsonObject; + } +} diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/recipe/adapters/InstanceKeyAdapter.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/recipe/adapters/InstanceKeyAdapter.java deleted file mode 100644 index b7399d953fe..00000000000 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/recipe/adapters/InstanceKeyAdapter.java +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2016 Codenvy, S.A. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Codenvy, S.A. - initial API and implementation - *******************************************************************************/ -package org.eclipse.che.api.machine.server.recipe.adapters; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; - -import org.eclipse.che.api.machine.server.spi.InstanceKey; - -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.Map; - -/** - * Type adapter for {@link InstanceKey}. - * - * @author Yevhenii Voevodin - */ -public class InstanceKeyAdapter implements JsonDeserializer, JsonSerializer { - - @Override - public InstanceKey deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException { - final JsonObject recipeObj = jsonElement.getAsJsonObject(); - return new InstanceKey() { - - Map fields; - - @Override - public Map getFields() { - if (fields == null) { - fields = new HashMap<>(); - for (Map.Entry entry : recipeObj.entrySet()) { - fields.put(entry.getKey(), entry.getValue().getAsString()); - } - } - return fields; - } - }; - } - - @Override - public JsonElement serialize(InstanceKey instanceKey, Type type, JsonSerializationContext context) { - final JsonObject fields = new JsonObject(); - for (Map.Entry entry : instanceKey.getFields().entrySet()) { - fields.addProperty(entry.getKey(), entry.getValue()); - } - return fields; - } -} diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/Instance.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/Instance.java index 9636eec62cf..7a83055244d 100644 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/Instance.java +++ b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/Instance.java @@ -13,6 +13,7 @@ import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.model.machine.Command; import org.eclipse.che.api.core.model.machine.Machine; +import org.eclipse.che.api.core.model.machine.MachineSource; import org.eclipse.che.api.core.model.machine.MachineStatus; import org.eclipse.che.api.core.util.LineConsumer; import org.eclipse.che.api.machine.server.exception.MachineException; @@ -76,7 +77,7 @@ public interface Instance extends Machine { * @throws MachineException * if error occurs on storing state of the instance */ - InstanceKey saveToSnapshot(String owner) throws MachineException; + MachineSource saveToSnapshot(String owner) throws MachineException; /** * Destroy instance diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/InstanceKey.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/InstanceKey.java deleted file mode 100644 index 9324df3306f..00000000000 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/InstanceKey.java +++ /dev/null @@ -1,24 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2016 Codenvy, S.A. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Codenvy, S.A. - initial API and implementation - *******************************************************************************/ -package org.eclipse.che.api.machine.server.spi; - -import java.util.Map; - -/** - * Describes set of keys that uniquely identifies snapshot of instance in implementation specific way. - * - * @author andrew00x - * @author Alexander Garagatyi - * @author Sergii Kabashniuk - */ -public interface InstanceKey { - Map getFields(); -} diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/InstanceProvider.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/InstanceProvider.java index 78cd5e73eb0..5fea18368c8 100644 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/InstanceProvider.java +++ b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/InstanceProvider.java @@ -12,9 +12,9 @@ import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.model.machine.Machine; +import org.eclipse.che.api.core.model.machine.MachineSource; import org.eclipse.che.api.core.model.machine.Recipe; import org.eclipse.che.api.core.util.LineConsumer; -import org.eclipse.che.api.machine.server.exception.InvalidInstanceSnapshotException; import org.eclipse.che.api.machine.server.exception.InvalidRecipeException; import org.eclipse.che.api.machine.server.exception.MachineException; import org.eclipse.che.api.machine.server.exception.SnapshotException; @@ -45,12 +45,11 @@ public interface InstanceProvider { Set getRecipeTypes(); /** - * Creates instance from scratch. + * Creates instance from scratch or by reusing a previously one by using specified {@link org.eclipse.che.api.core.model.machine.MachineSource} + * data in {@link org.eclipse.che.api.core.model.machine.MachineConfig}. * * @param machine * machine description - * @param recipe - * instance creation {@link Recipe} * @param creationLogsOutput * output for instance creation logs * @return newly created {@link Instance} @@ -58,41 +57,24 @@ public interface InstanceProvider { * if specified {@code recipe} is not supported * @throws InvalidRecipeException * if {@code recipe} is invalid + * @throws NotFoundException + * if instance described by {@link org.eclipse.che.api.core.model.machine.MachineSource} doesn't exists * @throws MachineException * if other error occurs */ - Instance createInstance(Recipe recipe, - Machine machine, + Instance createInstance(Machine machine, LineConsumer creationLogsOutput) throws UnsupportedRecipeException, InvalidRecipeException, + NotFoundException, MachineException; - /** - * Creates instance using implementation specific {@link InstanceKey}. - * - * @param instanceKey - * implementation specific {@link InstanceKey} - * @param creationLogsOutput - * output for instance creation logs - * @return newly created {@link Instance} - * @throws NotFoundException - * if instance described by {@code InstanceKey} doesn't exists - * @throws InvalidInstanceSnapshotException - * if other errors occurs while restoring instance - * @throws MachineException - * if other error occurs - */ - Instance createInstance(InstanceKey instanceKey, - Machine machine, - LineConsumer creationLogsOutput) throws NotFoundException, InvalidInstanceSnapshotException, MachineException; - /** * Removes snapshot of the instance in implementation specific way. * - * @param instanceKey - * key of the snapshot of the instance that should be removed + * @param machineSource + * contains implementation specific key of the snapshot of the instance that should be removed * @throws SnapshotException * if exception occurs on instance snapshot removal */ - void removeInstanceSnapshot(InstanceKey instanceKey) throws SnapshotException; + void removeInstanceSnapshot(MachineSource machineSource) throws SnapshotException; } diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/impl/InstanceKeyImpl.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/impl/InstanceKeyImpl.java deleted file mode 100644 index fea2e17d15b..00000000000 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/spi/impl/InstanceKeyImpl.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2016 Codenvy, S.A. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Codenvy, S.A. - initial API and implementation - *******************************************************************************/ -package org.eclipse.che.api.machine.server.spi.impl; - -import org.eclipse.che.api.machine.server.spi.InstanceKey; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * Map based implementation of {@link org.eclipse.che.api.machine.server.spi.InstanceKey}. - * - * @author Sergii Kabashniuk - */ -public class InstanceKeyImpl implements InstanceKey { - - private final Map fields; - - public InstanceKeyImpl(Map fields) { - this.fields = Collections.unmodifiableMap(new HashMap<>(fields)); - } - - public InstanceKeyImpl(InstanceKey instanceKey) { - this(instanceKey.getFields()); - } - - /** - * @return unmodifiable copy of fields. - */ - @Override - public Map getFields() { - return fields; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof InstanceKeyImpl)) return false; - - InstanceKeyImpl that = (InstanceKeyImpl)o; - - return !(getFields() != null ? !getFields().equals(that.getFields()) : that.getFields() != null); - - } - - @Override - public int hashCode() { - return getFields() != null ? getFields().hashCode() : 0; - } -} diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeRetriever.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeRetriever.java new file mode 100644 index 00000000000..46397cba89d --- /dev/null +++ b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeRetriever.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.machine.server.util; + +import com.google.common.base.Strings; + +import org.eclipse.che.api.core.model.machine.MachineConfig; +import org.eclipse.che.api.core.model.machine.MachineSource; +import org.eclipse.che.api.core.model.machine.Recipe; +import org.eclipse.che.api.machine.server.exception.MachineException; +import org.eclipse.che.api.machine.server.recipe.RecipeImpl; + +import javax.inject.Inject; +import javax.validation.constraints.NotNull; + +/** + * Handle how recipe is retrieved, either by downloading it with external location or by using the provided content. + * + * @author Florent Benoit + */ +public class RecipeRetriever { + + /** + * For recipe stored on an external location, needs to delegate. + */ + @Inject + private RecipeDownloader recipeDownloader; + + /** + * Gets the recipe from a machine configuration + * + * @param machineConfig + * the machine configuration that is containing the content or a location to get recipe + * @return recipe with set content and type + * @throws MachineException + * if any error occurs + */ + public Recipe getRecipe(@NotNull MachineConfig machineConfig) throws MachineException { + MachineSource machineSource = machineConfig.getSource(); + if (!Strings.isNullOrEmpty(machineSource.getContent())) { + return new RecipeImpl().withType(machineSource.getType()) + .withScript(machineSource.getContent()); + } else { + return recipeDownloader.getRecipe(machineConfig); + } + + } +} diff --git a/wsmaster/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/MachineManagerTest.java b/wsmaster/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/MachineManagerTest.java index c93de59c660..53aa65eb5ea 100644 --- a/wsmaster/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/MachineManagerTest.java +++ b/wsmaster/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/MachineManagerTest.java @@ -27,7 +27,6 @@ import org.eclipse.che.api.machine.server.recipe.RecipeImpl; import org.eclipse.che.api.machine.server.spi.Instance; import org.eclipse.che.api.machine.server.spi.InstanceProvider; -import org.eclipse.che.api.machine.server.util.RecipeDownloader; import org.eclipse.che.api.machine.server.wsagent.WsAgentLauncher; import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.commons.lang.IoUtil; @@ -76,8 +75,6 @@ public class MachineManagerTest { @Mock private MachineInstanceProviders machineInstanceProviders; @Mock - private RecipeDownloader recipeDownloader; - @Mock private InstanceProvider instanceProvider; @Mock private MachineRegistry machineRegistry; @@ -102,8 +99,7 @@ public void setUp() throws Exception { machineLogsDir, eventService, DEFAULT_MACHINE_MEMORY_SIZE_MB, - wsAgentLauncher, - recipeDownloader)); + wsAgentLauncher)); EnvironmentContext envCont = new EnvironmentContext(); envCont.setSubject(new SubjectImpl(null, USER_ID, null, null, false)); @@ -112,13 +108,12 @@ public void setUp() throws Exception { RecipeImpl recipe = new RecipeImpl().withScript("script").withType("Dockerfile"); // doNothing().when(manager).createMachineLogsDir(anyString()); doReturn(MACHINE_ID).when(manager).generateMachineId(); - when(recipeDownloader.getRecipe(any(MachineConfig.class))).thenReturn(recipe); when(machineInstanceProviders.getProvider(anyString())).thenReturn(instanceProvider); HashSet recipeTypes = new HashSet<>(); recipeTypes.add("test type 1"); recipeTypes.add("dockerfile"); when(instanceProvider.getRecipeTypes()).thenReturn(recipeTypes); - when(instanceProvider.createInstance(eq(recipe), any(Machine.class), any(LineConsumer.class))).thenReturn(instance); + when(instanceProvider.createInstance(any(Machine.class), any(LineConsumer.class))).thenReturn(instance); when(machineRegistry.getInstance(anyString())).thenReturn(instance); } @@ -129,13 +124,10 @@ public void tearDown() throws Exception { @Test(expectedExceptions = BadRequestException.class, expectedExceptionsMessageRegExp = "Invalid machine name @name!") public void shouldThrowExceptionOnMachineCreationIfMachineNameIsInvalid() throws Exception { - when(recipeDownloader.getRecipe(any(MachineConfig.class))).thenReturn(new RecipeImpl().withScript("script") - .withType("Dockerfile")); - MachineConfig machineConfig = new MachineConfigImpl(false, "@name!", "machineType", - new MachineSourceImpl("Dockerfile", "location"), + new MachineSourceImpl("Dockerfile").setLocation("location"), new LimitsImpl(1024), Arrays.asList(new ServerConfImpl("ref1", "8080", @@ -217,7 +209,7 @@ private MachineConfigImpl createMachineConfig() { return new MachineConfigImpl(false, "MachineName", "docker", - new MachineSourceImpl("Dockerfile", "location"), + new MachineSourceImpl("Dockerfile").setLocation("location"), new LimitsImpl(1024), Arrays.asList(new ServerConfImpl("ref1", "8080", diff --git a/wsmaster/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/model/impl/adapter/MachineSourceAdapterTest.java b/wsmaster/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/model/impl/adapter/MachineSourceAdapterTest.java new file mode 100644 index 00000000000..32d5d5030d8 --- /dev/null +++ b/wsmaster/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/model/impl/adapter/MachineSourceAdapterTest.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.machine.server.model.impl.adapter; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; +import com.google.gson.JsonSerializationContext; + +import org.eclipse.che.api.core.model.machine.MachineSource; +import org.eclipse.che.api.machine.server.model.impl.MachineSourceImpl; +import org.mockito.Mockito; +import org.testng.annotations.Test; + +import java.io.StringReader; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.spy; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +/** + * Test {@link MachineSourceAdapter} on serialization + * + * @author Florent Benoit + */ +public class MachineSourceAdapterTest { + + /** + * Check we can transform object into JSON and JSON into object + */ + @Test + public void testSerializeAndDeserialize() { + + MachineSourceAdapter machineSourceAdapter = spy(new MachineSourceAdapter()); + + Gson gson = new GsonBuilder().registerTypeAdapter(MachineSource.class, machineSourceAdapter).setPrettyPrinting().create(); + + final String TYPE = "myType"; + final String LOCATION = "myLocation"; + final String CONTENT = "myContent"; + + // serialize + MachineSource machineSource = new MachineSourceImpl(TYPE).setLocation(LOCATION).setContent(CONTENT); + String json = gson.toJson(machineSource, MachineSource.class); + assertNotNull(json); + + // verify we called serializer + Mockito.verify(machineSourceAdapter).serialize(eq(machineSource), eq(MachineSource.class), any(JsonSerializationContext.class)); + + // now deserialize + MachineSource machineSourceDeserialize = gson.fromJson(new StringReader(json), MachineSource.class); + assertNotNull(machineSourceDeserialize); + assertEquals(machineSourceDeserialize.getLocation(), LOCATION); + assertEquals(machineSourceDeserialize.getType(), TYPE); + assertEquals(machineSourceDeserialize.getContent(), CONTENT); + // verify we called deserializer + Mockito.verify(machineSourceAdapter).deserialize(any(JsonElement.class), eq(MachineSource.class), any(JsonDeserializationContext.class)); + } +} diff --git a/wsmaster/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/util/RecipeRetrieverTest.java b/wsmaster/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/util/RecipeRetrieverTest.java new file mode 100644 index 00000000000..a36982819a6 --- /dev/null +++ b/wsmaster/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/util/RecipeRetrieverTest.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.machine.server.util; + +import org.eclipse.che.api.core.model.machine.MachineConfig; +import org.eclipse.che.api.core.model.machine.MachineSource; +import org.eclipse.che.api.core.model.machine.Recipe; +import org.eclipse.che.api.machine.server.exception.MachineException; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; + +/** + * Test of {@link RecipeRetriever} class + * @author Florent Benoit + */ +@Listeners(MockitoTestNGListener.class) +public class RecipeRetrieverTest { + + /** + * Typ of the recipe used in tests. + */ + private static final String RECIPE_TYPE = "MY_TYPE"; + + /** + * Downloader instance that might be used by recipe retriever for type = location. + */ + @Mock + private RecipeDownloader recipeDownloader; + + /** + * Machine config sent to recipe retriever. + */ + @Mock + private MachineConfig machineConfig; + + /** + * Machine source embedded in machine config. + */ + @Mock + private MachineSource machineSource; + + /** + * Instance used in tests. + */ + @InjectMocks + private RecipeRetriever recipeRetriever; + + + /** + * Setup the rules used in all tests. + */ + @BeforeMethod + public void init() { + when(machineConfig.getSource()).thenReturn(machineSource); + when(machineSource.getType()).thenReturn(RECIPE_TYPE); + } + + + /** + * Check that when content is set in machine source, recipe is based on this content. + * @throws MachineException if recipe is not retrieved + */ + @Test + public void checkWithContent() throws MachineException { + String RECIPE = "FROM TOTO"; + when(machineSource.getContent()).thenReturn(RECIPE); + Recipe recipe = recipeRetriever.getRecipe(machineConfig); + Assert.assertNotNull(recipe); + assertEquals(recipe.getType(), RECIPE_TYPE); + assertEquals(recipe.getScript(), RECIPE); + } + + + /** + * Check that when location is set in machine source, recipe retriever ask the recipe downloader. + * @throws MachineException if recipe is not retrieved + */ + @Test + public void checkWithLocation() throws MachineException { + String LOCATION = "http://eclipse.org/my-che.recipe"; + when(machineSource.getLocation()).thenReturn(LOCATION); + recipeRetriever.getRecipe(machineConfig); + verify(recipeDownloader).getRecipe(machineConfig); + } +} diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/DefaultWorkspaceValidator.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/DefaultWorkspaceValidator.java index 4d2ec348263..89f58ea80c1 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/DefaultWorkspaceValidator.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/DefaultWorkspaceValidator.java @@ -123,6 +123,9 @@ private void validateEnv(Environment environment, String workspaceName) throws B private void validateMachine(MachineConfig machineCfg, String envName) throws BadRequestException { checkArgument(!isNullOrEmpty(machineCfg.getName()), "Environment %s contains machine with null or empty name", envName); checkNotNull(machineCfg.getSource(), "Environment " + envName + " contains machine without source"); + checkArgument(!(machineCfg.getSource().getContent() == null && machineCfg.getSource().getLocation() == null), + "Environment " + envName + " contains machine with source but this source doesn't define a location or content"); + checkArgument(machineInstanceProviders.hasProvider(machineCfg.getType()), "Type %s of machine %s in environment %s is not supported. Supported values: %s.", machineCfg.getType(), diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceService.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceService.java index 27116287585..06c85ec23df 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceService.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceService.java @@ -712,7 +712,9 @@ public Response createMachine(@ApiParam("The workspace id") requiredNotNull(machineConfig.getType(), "Machine type"); requiredNotNull(machineConfig.getSource(), "Machine source"); requiredNotNull(machineConfig.getSource().getType(), "Machine source type"); - requiredNotNull(machineConfig.getSource().getLocation(), "Machine source location"); + // definition of source should come either with a content or with location + requiredOnlyOneNotNull(machineConfig.getSource().getLocation(), machineConfig.getSource().getContent(), + "Machine source should provide either location or content"); final WorkspaceImpl workspace = workspaceManager.getWorkspace(workspaceId); if (workspace.getRuntime() == null) { @@ -766,6 +768,27 @@ private void requiredNotNull(Object object, String subject) throws BadRequestExc } } + /** + * Checks only one of the given object reference is {@code null} + * + * @param object1 + * object reference to check + * @param object2 + * object reference to check + * @param subject + * used as subject of exception message "{subject} required" + * @throws BadRequestException + * when objects are both null or have both a value reference is {@code null} + */ + private void requiredOnlyOneNotNull(Object object1, Object object2, String subject) throws BadRequestException { + if (object1 == null && object2 == null) { + throw new BadRequestException(subject + " required"); + } + if (object1 != null && object2 != null) { + throw new BadRequestException(subject + " required"); + } + } + /* * Validate composite key. * diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/DefaultWorkspaceValidatorTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/DefaultWorkspaceValidatorTest.java index 77a846842b5..fb992f2c749 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/DefaultWorkspaceValidatorTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/DefaultWorkspaceValidatorTest.java @@ -12,7 +12,6 @@ import org.eclipse.che.api.core.BadRequestException; import org.eclipse.che.api.machine.server.MachineInstanceProviders; -import org.eclipse.che.api.machine.server.spi.InstanceProvider; import org.eclipse.che.api.machine.shared.dto.CommandDto; import org.eclipse.che.api.machine.shared.dto.MachineConfigDto; import org.eclipse.che.api.machine.shared.dto.MachineSourceDto; @@ -23,9 +22,7 @@ import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.testng.MockitoTestNGListener; -import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Listeners; @@ -40,8 +37,6 @@ import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static org.eclipse.che.dto.server.DtoFactory.newDto; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; /** @@ -507,6 +502,19 @@ public void shouldFailValidationIfEnvVarValueIsNull() throws Exception { wsValidator.validateConfig(config); } + @Test(expectedExceptions = BadRequestException.class, + expectedExceptionsMessageRegExp = "Environment dev-env contains machine with source but this source doesn't define a location or content") + public void shouldFailValidationIfMissingLocationOrContent() throws Exception { + final WorkspaceConfigDto config = createConfig(); + config.getEnvironments() + .get(0) + .getMachineConfigs() + .get(0) + .withSource(newDto(MachineSourceDto.class).withType("dockerfile")); + + wsValidator.validateConfig(config); + } + private static WorkspaceConfigDto createConfig() { final WorkspaceConfigDto workspaceConfigDto = newDto(WorkspaceConfigDto.class).withName("ws-name") .withDefaultEnv("dev-env"); diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceManagerTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceManagerTest.java index 66c756a1745..6dcbf2a636e 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceManagerTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceManagerTest.java @@ -607,7 +607,7 @@ private static WorkspaceConfigImpl createConfig() { .setDev(true) .setName("dev-machine") .setType("docker") - .setSource(new MachineSourceImpl("location", "dockerfile")) + .setSource(new MachineSourceImpl("location").setLocation("dockerfile")) .setServers(asList(new ServerConfImpl("ref1", "8080", "https", diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceRuntimesTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceRuntimesTest.java index ce8fd7fb46f..09c897e1461 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceRuntimesTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceRuntimesTest.java @@ -719,7 +719,7 @@ private static MachineConfigImpl createConfig(boolean isDev) { .setDev(isDev) .setType("docker") .setLimits(new LimitsImpl(1024)) - .setSource(new MachineSourceImpl("git", "location")) + .setSource(new MachineSourceImpl("git").setLocation("location")) .setName("dev-machine") .build(); } diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceServiceTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceServiceTest.java index 97b924346a1..608c1cb44f4 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceServiceTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceServiceTest.java @@ -721,7 +721,7 @@ private static EnvironmentDto createEnvDto() { .setDev(true) .setName("dev-machine") .setType("docker") - .setSource(new MachineSourceImpl("location", "recipe")) + .setSource(new MachineSourceImpl("location").setLocation("recipe")) .setServers(asList(new ServerConfImpl("wsagent", "8080", "https", diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/stack/StackServiceTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/stack/StackServiceTest.java index e53cc02de99..128ed707aa6 100644 --- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/stack/StackServiceTest.java +++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/stack/StackServiceTest.java @@ -158,7 +158,7 @@ public void setUp() throws IOException, ConflictException { componentsImpl = Collections.singletonList(new StackComponentImpl(COMPONENT_NAME, COMPONENT_VERSION)); stackSourceImpl = new StackSourceImpl(SOURCE_TYPE, SOURCE_ORIGIN); CommandImpl command = new CommandImpl(COMMAND_NAME, COMMAND_LINE, COMMAND_TYPE); - MachineSourceImpl machineSource = new MachineSourceImpl(MACHINE_SOURCE_TYPE, MACHINE_SOURCE_LOCATION); + MachineSourceImpl machineSource = new MachineSourceImpl(MACHINE_SOURCE_TYPE).setLocation(MACHINE_SOURCE_LOCATION); int limitMemory = 1000; LimitsImpl limits = new LimitsImpl(limitMemory); MachineConfigImpl machineConfig = new MachineConfigImpl(IS_DEV, diff --git a/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalSnapshotDaoImpl.java b/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalSnapshotDaoImpl.java index 81098ef9338..23c5d79d9bc 100644 --- a/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalSnapshotDaoImpl.java +++ b/wsmaster/wsmaster-local/src/main/java/org/eclipse/che/api/local/LocalSnapshotDaoImpl.java @@ -13,13 +13,13 @@ import com.google.common.reflect.TypeToken; import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.model.machine.MachineSource; import org.eclipse.che.api.local.storage.LocalStorage; import org.eclipse.che.api.local.storage.LocalStorageFactory; import org.eclipse.che.api.machine.server.dao.SnapshotDao; import org.eclipse.che.api.machine.server.exception.SnapshotException; import org.eclipse.che.api.machine.server.model.impl.SnapshotImpl; -import org.eclipse.che.api.machine.server.recipe.adapters.InstanceKeyAdapter; -import org.eclipse.che.api.machine.server.spi.InstanceKey; +import org.eclipse.che.api.machine.server.model.impl.adapter.MachineSourceAdapter; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -50,7 +50,7 @@ public class LocalSnapshotDaoImpl implements SnapshotDao { @Inject public LocalSnapshotDaoImpl(LocalStorageFactory storageFactory) throws IOException { snapshots = new HashMap<>(); - snapshotStorage = storageFactory.create("snapshots.json", singletonMap(InstanceKey.class, new InstanceKeyAdapter())); + snapshotStorage = storageFactory.create("snapshots.json", singletonMap(MachineSource.class, new MachineSourceAdapter())); } @Override diff --git a/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalRecipeDaoImplTest.java b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalRecipeDaoImplTest.java index 3831c9cc04f..e59c26f68e9 100644 --- a/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalRecipeDaoImplTest.java +++ b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalRecipeDaoImplTest.java @@ -17,8 +17,6 @@ import org.eclipse.che.api.core.acl.AclEntryImpl; import org.eclipse.che.api.local.storage.LocalStorageFactory; import org.eclipse.che.api.machine.server.recipe.RecipeImpl; -import org.eclipse.che.api.machine.server.recipe.adapters.InstanceKeyAdapter; -import org.eclipse.che.api.machine.server.spi.InstanceKey; import org.mockito.testng.MockitoTestNGListener; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Listeners; diff --git a/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalSnapshotDaoTest.java b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalSnapshotDaoTest.java index 07edb5bb5de..3c04c4c99d8 100644 --- a/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalSnapshotDaoTest.java +++ b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalSnapshotDaoTest.java @@ -13,17 +13,17 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import org.eclipse.che.api.core.model.machine.MachineSource; import org.eclipse.che.api.local.storage.LocalStorageFactory; import org.eclipse.che.api.machine.server.model.impl.SnapshotImpl; -import org.eclipse.che.api.machine.server.recipe.adapters.InstanceKeyAdapter; -import org.eclipse.che.api.machine.server.spi.InstanceKey; +import org.eclipse.che.api.machine.server.model.impl.adapter.MachineSourceAdapter; +import org.mockito.Mock; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Map; import static java.nio.file.Files.readAllBytes; import static java.nio.file.Files.write; @@ -39,12 +39,15 @@ public class LocalSnapshotDaoTest { private static Gson GSON = new GsonBuilder().setPrettyPrinting() - .registerTypeAdapter(InstanceKey.class, new InstanceKeyAdapter()) + .registerTypeAdapter(MachineSource.class, new MachineSourceAdapter()) .create(); private LocalSnapshotDaoImpl snapshotDao; private Path snapshotsPath; + @Mock + private MachineSource machineSource; + @BeforeMethod public void setUp() throws Exception { final URL url = Thread.currentThread().getContextClassLoader().getResource("."); @@ -80,7 +83,7 @@ private SnapshotImpl createSnapshot() { return SnapshotImpl.builder() .generateId() .setType("docker") - .setInstanceKey(new DummyInstanceKey()) + .setMachineSource(machineSource) .setNamespace("user123") .setWorkspaceId("workspace123") .setMachineName("machine123") @@ -91,18 +94,4 @@ private SnapshotImpl createSnapshot() { .build(); } - private static class DummyInstanceKey implements InstanceKey { - - Map fields = singletonMap("field1", "value1"); - - @Override - public Map getFields() { - return fields; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof InstanceKey && ((InstanceKey)obj).getFields().equals(getFields()); - } - } } diff --git a/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalWorkspaceDaoTest.java b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalWorkspaceDaoTest.java index d3f72f00b0e..dbd9796166c 100644 --- a/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalWorkspaceDaoTest.java +++ b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalWorkspaceDaoTest.java @@ -94,7 +94,7 @@ private static WorkspaceImpl createWorkspace() { recipe.setType("dockerfile"); recipe.setScript("FROM codenvy/jdk7\nCMD tail -f /dev/null"); - final MachineSourceImpl machineSource = new MachineSourceImpl("recipe", "recipe-url"); + final MachineSourceImpl machineSource = new MachineSourceImpl("recipe").setLocation("recipe-url"); final MachineConfigImpl machineCfg1 = new MachineConfigImpl(true, "dev-machine", "machine-type",