From 411dd89cd54ab566ec89a26c1e03fa0742f67328 Mon Sep 17 00:00:00 2001 From: Mihail Kuznyetsov Date: Tue, 12 Jul 2016 17:59:53 +0300 Subject: [PATCH] CODENVY-556 Add service for fetching recipe script In order to fetch recipe script from external host, and not end up with 'Mixed content' error, when trying to send HTTP request on HTTPS installation, we will do it on server side instead. from HTT --- .../RecipeScriptDownloadServiceClient.java | 29 +++++++++++ ...RecipeScriptDownloadServiceClientImpl.java | 41 +++++++++++++++ .../client/inject/MachineGinModule.java | 4 ++ .../appliance/recipe/RecipeTabPresenter.java | 46 +++++++---------- .../recipe/RecipeTabPresenterTest.java | 24 ++++++--- .../che-plugin-machine-ext-server/pom.xml | 4 ++ .../ide/ext/machine/server/MachineModule.java | 3 ++ .../server/RecipeScriptDownloadService.java | 50 +++++++++++++++++++ .../ssh/SshMachineInstanceProviderTest.java | 4 +- .../machine/server/util/RecipeDownloader.java | 2 +- .../server/DefaultWorkspaceValidator.java | 15 ++++++ .../server/DefaultWorkspaceValidatorTest.java | 28 ++++++++++- 12 files changed, 212 insertions(+), 38 deletions(-) create mode 100644 plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/RecipeScriptDownloadServiceClient.java create mode 100644 plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/RecipeScriptDownloadServiceClientImpl.java create mode 100644 plugins/plugin-machine/che-plugin-machine-ext-server/src/main/java/org/eclipse/che/ide/ext/machine/server/RecipeScriptDownloadService.java diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/RecipeScriptDownloadServiceClient.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/RecipeScriptDownloadServiceClient.java new file mode 100644 index 00000000000..897316694bf --- /dev/null +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/RecipeScriptDownloadServiceClient.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * 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; + +import org.eclipse.che.api.promises.client.Promise; +import org.eclipse.che.ide.extension.machine.client.machine.Machine; + +/** + * @author Mihail Kuznyetsov. + */ +public interface RecipeScriptDownloadServiceClient { + + /** + * Fetch recipe script for machine source location + * + * @param machine + * machine to fetch script for + * @return content of the recipe script + */ + Promise getRecipeScript(Machine machine); +} diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/RecipeScriptDownloadServiceClientImpl.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/RecipeScriptDownloadServiceClientImpl.java new file mode 100644 index 00000000000..3223ce99676 --- /dev/null +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/RecipeScriptDownloadServiceClientImpl.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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; + +import org.eclipse.che.api.promises.client.Promise; +import org.eclipse.che.ide.extension.machine.client.machine.Machine; +import org.eclipse.che.ide.rest.AsyncRequestFactory; +import org.eclipse.che.ide.rest.RestContext; +import org.eclipse.che.ide.rest.StringUnmarshaller; + +import javax.inject.Inject; + +/** + * @author Mihail Kuznyetsov. + */ +public class RecipeScriptDownloadServiceClientImpl implements RecipeScriptDownloadServiceClient { + + private final String restContext; + private final AsyncRequestFactory asyncRequestFactory; + + @Inject + public RecipeScriptDownloadServiceClientImpl(@RestContext String restContext, AsyncRequestFactory asyncRequestFactory) { + this.restContext = restContext; + this.asyncRequestFactory = asyncRequestFactory; + } + + @Override + public Promise getRecipeScript(Machine machine) { + return asyncRequestFactory + .createGetRequest(restContext + "/recipe/script/" + machine.getId()) + .send(new StringUnmarshaller()); + } +} diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/inject/MachineGinModule.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/inject/MachineGinModule.java index 3b62ccbdb53..4c40cc1675e 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/inject/MachineGinModule.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/inject/MachineGinModule.java @@ -22,6 +22,8 @@ import org.eclipse.che.ide.api.machine.MachineManager; import org.eclipse.che.ide.api.outputconsole.OutputConsole; import org.eclipse.che.ide.api.parts.Perspective; +import org.eclipse.che.ide.extension.machine.client.RecipeScriptDownloadServiceClient; +import org.eclipse.che.ide.extension.machine.client.RecipeScriptDownloadServiceClientImpl; import org.eclipse.che.ide.extension.machine.client.command.CommandType; import org.eclipse.che.ide.extension.machine.client.command.custom.CustomCommandType; import org.eclipse.che.ide.extension.machine.client.command.edit.EditCommandsView; @@ -129,6 +131,8 @@ protected void configure() { bind(Target.class).to(BaseTarget.class); + bind(RecipeScriptDownloadServiceClient.class).to(RecipeScriptDownloadServiceClientImpl.class).in(Singleton.class); + final GinMultibinder categoryPageBinder = GinMultibinder.newSetBinder(binder(), CategoryPage.class); categoryPageBinder.addBinding().to(SshCategoryPresenter.class); categoryPageBinder.addBinding().to(DockerCategoryPresenter.class); diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/recipe/RecipeTabPresenter.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/recipe/RecipeTabPresenter.java index 079b043407b..0be05a5a0a2 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/recipe/RecipeTabPresenter.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/recipe/RecipeTabPresenter.java @@ -10,23 +10,20 @@ *******************************************************************************/ package org.eclipse.che.ide.extension.machine.client.perspective.widgets.machine.appliance.recipe; -import com.google.gwt.http.client.Request; -import com.google.gwt.http.client.RequestBuilder; -import com.google.gwt.http.client.RequestCallback; -import com.google.gwt.http.client.RequestException; -import com.google.gwt.http.client.Response; import com.google.gwt.user.client.ui.AcceptsOneWidget; import com.google.gwt.user.client.ui.IsWidget; import com.google.inject.Inject; +import org.eclipse.che.api.promises.client.Operation; +import org.eclipse.che.api.promises.client.OperationException; +import org.eclipse.che.api.promises.client.PromiseError; +import org.eclipse.che.ide.extension.machine.client.RecipeScriptDownloadServiceClient; import org.eclipse.che.ide.extension.machine.client.machine.Machine; import org.eclipse.che.ide.extension.machine.client.perspective.widgets.tab.content.TabPresenter; import org.eclipse.che.ide.util.loging.Log; import javax.validation.constraints.NotNull; -import static com.google.common.base.Strings.isNullOrEmpty; - /** * The class contains business logic which allows update a recipe for current machine. The class is a tab presenter and * shows current machine recipe. @@ -35,11 +32,13 @@ */ public class RecipeTabPresenter implements TabPresenter { - private final RecipeView view; + private final RecipeView view; + private final RecipeScriptDownloadServiceClient recipeScriptClient; @Inject - public RecipeTabPresenter(RecipeView view) { + public RecipeTabPresenter(RecipeView view, RecipeScriptDownloadServiceClient recipeScriptClient) { this.view = view; + this.recipeScriptClient = recipeScriptClient; } /** @@ -48,28 +47,19 @@ public RecipeTabPresenter(RecipeView view) { * @param machine * machine for which need update information */ - public void updateInfo(@NotNull Machine machine) { - String scriptLocation = machine.getRecipeLocation(); - if (!isNullOrEmpty(scriptLocation)) { //TODO : need to add test this block - RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, scriptLocation); - try { - requestBuilder.sendRequest(null, new RequestCallback() { + public void updateInfo(@NotNull final Machine machine) { + recipeScriptClient.getRecipeScript(machine).then(new Operation() { @Override - public void onResponseReceived(Request request, Response response) { - view.setScript(response.getText()); - } - - @Override - public void onError(Request request, Throwable exception) { - + public void apply(String recipe) throws OperationException { + view.setScript(recipe); } - }); - } catch (RequestException exception) { - Log.error(getClass(), exception); + }).catchError(new Operation() { + @Override + public void apply(PromiseError error) throws OperationException { + Log.error(RecipeTabPresenter.class, + "Failed to get recipe script for machine " + machine.getId() + ": " + error.getMessage()); } - } else if (!isNullOrEmpty(machine.getRecipeContent())) { - view.setScript(machine.getRecipeContent()); - } + }); } /** {@inheritDoc} */ diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/recipe/RecipeTabPresenterTest.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/recipe/RecipeTabPresenterTest.java index 455d84cd08c..450f25afdbc 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/recipe/RecipeTabPresenterTest.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/perspective/widgets/machine/appliance/recipe/RecipeTabPresenterTest.java @@ -11,9 +11,9 @@ package org.eclipse.che.ide.extension.machine.client.perspective.widgets.machine.appliance.recipe; import org.eclipse.che.api.promises.client.Operation; -import org.eclipse.che.api.promises.client.PromiseError; +import org.eclipse.che.api.promises.client.Promise; +import org.eclipse.che.ide.extension.machine.client.RecipeScriptDownloadServiceClient; import org.eclipse.che.ide.extension.machine.client.machine.Machine; -import org.eclipse.che.ide.websocket.rest.RequestCallback; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -23,7 +23,7 @@ import org.mockito.runners.MockitoJUnitRunner; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -33,12 +33,17 @@ @RunWith(MockitoJUnitRunner.class) public class RecipeTabPresenterTest { @Mock - private RecipeView view; + private RecipeView view; @Mock - private Machine machine; + private Machine machine; + @Mock + private RecipeScriptDownloadServiceClient recipeScriptClient; + + @Mock + private Promise recipePromise; @Captor - private ArgumentCaptor argumentCaptor; + private ArgumentCaptor> argumentCaptor; @InjectMocks private RecipeTabPresenter presenter; @@ -64,8 +69,13 @@ public void tabShouldBeVisible() throws Exception { @Test public void tabGetScriptAsContent() throws Exception { - when(machine.getRecipeContent()).thenReturn("test content"); + when(recipeScriptClient.getRecipeScript(any(Machine.class))).thenReturn(recipePromise); + when(recipePromise.then(any(Operation.class))).thenReturn(recipePromise); + presenter.updateInfo(machine); + + verify(recipePromise).then(argumentCaptor.capture()); + argumentCaptor.getValue().apply("test content"); verify(view).setScript("test content"); } diff --git a/plugins/plugin-machine/che-plugin-machine-ext-server/pom.xml b/plugins/plugin-machine/che-plugin-machine-ext-server/pom.xml index 3ddb4ed3a66..68e3ab08fd8 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-server/pom.xml +++ b/plugins/plugin-machine/che-plugin-machine-ext-server/pom.xml @@ -32,6 +32,10 @@ javax.inject javax.inject + + javax.ws.rs + javax.ws.rs-api + org.eclipse.che.core che-core-api-core diff --git a/plugins/plugin-machine/che-plugin-machine-ext-server/src/main/java/org/eclipse/che/ide/ext/machine/server/MachineModule.java b/plugins/plugin-machine/che-plugin-machine-ext-server/src/main/java/org/eclipse/che/ide/ext/machine/server/MachineModule.java index 669c3c2a4b2..b574d837110 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-server/src/main/java/org/eclipse/che/ide/ext/machine/server/MachineModule.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-server/src/main/java/org/eclipse/che/ide/ext/machine/server/MachineModule.java @@ -12,6 +12,7 @@ import com.google.inject.AbstractModule; +import org.eclipse.che.api.machine.server.MachineService; import org.eclipse.che.ide.ext.machine.server.ssh.KeysInjector; import org.eclipse.che.inject.DynaModule; @@ -19,5 +20,7 @@ public class MachineModule extends AbstractModule { protected void configure() { bind(KeysInjector.class).asEagerSingleton(); + + bind(RecipeScriptDownloadService.class); } } diff --git a/plugins/plugin-machine/che-plugin-machine-ext-server/src/main/java/org/eclipse/che/ide/ext/machine/server/RecipeScriptDownloadService.java b/plugins/plugin-machine/che-plugin-machine-ext-server/src/main/java/org/eclipse/che/ide/ext/machine/server/RecipeScriptDownloadService.java new file mode 100644 index 00000000000..3a33afe6340 --- /dev/null +++ b/plugins/plugin-machine/che-plugin-machine-ext-server/src/main/java/org/eclipse/che/ide/ext/machine/server/RecipeScriptDownloadService.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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.ext.machine.server; + + +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.rest.Service; +import org.eclipse.che.api.machine.server.MachineManager; +import org.eclipse.che.api.machine.server.util.RecipeRetriever; + +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * Service for downloading recipe script for machine. + * + * @author Mihail Kuznyetsov. + */ +@Path("/recipe/script") +public class RecipeScriptDownloadService extends Service { + + private final MachineManager machineManager; + private final RecipeRetriever recipeRetriever; + + @Inject + public RecipeScriptDownloadService(MachineManager machineManager, RecipeRetriever recipeRetriever) { + this.machineManager = machineManager; + this.recipeRetriever = recipeRetriever; + } + + @GET + @Path("/{machineId}") + @Produces(MediaType.TEXT_PLAIN) + public String getRecipeScript(@PathParam("machineId") String machineId) throws ServerException, NotFoundException { + return recipeRetriever.getRecipe(machineManager.getMachine(machineId).getConfig()).getScript(); + } +} 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 1f67c2879ad..173b98ed012 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 @@ -47,6 +47,8 @@ */ @Listeners(MockitoTestNGListener.class) public class SshMachineInstanceProviderTest { + private static final String RECIPE_SCRIPT = new Gson().toJson(new SshMachineRecipe("localhost", 22, "user", "password")); + @Mock private RecipeDownloader recipeDownloader; @@ -70,7 +72,7 @@ public void setUp() throws Exception { "user", "password"); recipe = new RecipeImpl().withType("ssh-config") - .withScript(new Gson().toJson(sshMachineRecipe)); + .withScript(RECIPE_SCRIPT); } @Test diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeDownloader.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeDownloader.java index 066f626f0fd..75a72cc5a59 100644 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeDownloader.java +++ b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeDownloader.java @@ -77,7 +77,7 @@ public RecipeImpl getRecipe(MachineConfig machineConfig) throws MachineException return new RecipeImpl().withType(machineConfig.getSource().getType()) .withScript(IoUtil.readAndCloseQuietly(new FileInputStream(file))); } catch (IOException | IllegalArgumentException e) { - throw new MachineException(format("Can't start machine %s because machine recipe downloading failed. Recipe url %s. Error: %s", + throw new MachineException(format("Failed to download recipe for machine %s. Recipe url %s. Error: %s", machineConfig.getName(), location, e.getLocalizedMessage())); 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 89f58ea80c1..f0bef907f71 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 @@ -23,6 +23,8 @@ import javax.inject.Inject; import javax.inject.Singleton; +import java.net.MalformedURLException; +import java.net.URL; import java.util.Map; import java.util.regex.Pattern; @@ -126,6 +128,7 @@ private void validateMachine(MachineConfig machineCfg, String envName) throws Ba 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(), @@ -133,6 +136,18 @@ private void validateMachine(MachineConfig machineCfg, String envName) throws Ba envName, Joiner.on(", ").join(machineInstanceProviders.getProviderTypes())); + if (machineCfg.getSource().getType().equals("dockerfile") && machineCfg.getSource().getLocation() != null) { + try { + final String protocol = new URL(machineCfg.getSource().getLocation()).getProtocol(); + checkArgument(protocol.equals("http") || protocol.equals("https"), + "Environment " + envName + " contains machine with invalid source location protocol: " + + machineCfg.getSource().getLocation()); + } catch (MalformedURLException e) { + throw new BadRequestException("Environment " + envName + " contains machine with invalid source location: " + + machineCfg.getSource().getLocation()); + } + } + for (ServerConf serverConf : machineCfg.getServers()) { checkArgument(serverConf.getPort() != null && SERVER_PORT.matcher(serverConf.getPort()).matches(), "Machine %s contains server conf with invalid port %s", 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 fb992f2c749..e0b9922fe59 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 @@ -515,6 +515,32 @@ public void shouldFailValidationIfMissingLocationOrContent() throws Exception { wsValidator.validateConfig(config); } + @Test(expectedExceptions = BadRequestException.class, + expectedExceptionsMessageRegExp = "Environment dev-env contains machine with invalid source location: localhost") + public void shouldFailValidationIfLocationIsInvalidUrl() throws Exception { + final WorkspaceConfigDto config = createConfig(); + config.getEnvironments() + .get(0) + .getMachineConfigs() + .get(0) + .withSource(newDto(MachineSourceDto.class).withType("dockerfile").withLocation("localhost")); + + wsValidator.validateConfig(config); + } + + @Test(expectedExceptions = BadRequestException.class, + expectedExceptionsMessageRegExp = "Environment dev-env contains machine with invalid source location protocol: ftp://localhost") + public void shouldFailValidationIfLocationHasInvalidProtocol() throws Exception { + final WorkspaceConfigDto config = createConfig(); + config.getEnvironments() + .get(0) + .getMachineConfigs() + .get(0) + .withSource(newDto(MachineSourceDto.class).withType("dockerfile").withLocation("ftp://localhost")); + + wsValidator.validateConfig(config); + } + private static WorkspaceConfigDto createConfig() { final WorkspaceConfigDto workspaceConfigDto = newDto(WorkspaceConfigDto.class).withName("ws-name") .withDefaultEnv("dev-env"); @@ -530,7 +556,7 @@ private static WorkspaceConfigDto createConfig() { MachineConfigDto devMachine = newDto(MachineConfigDto.class).withDev(true) .withName("dev-machine") .withType("docker") - .withSource(newDto(MachineSourceDto.class).withLocation("location") + .withSource(newDto(MachineSourceDto.class).withLocation("http://location") .withType("dockerfile")) .withServers(serversConf) .withEnvVariables(new HashMap<>(singletonMap("key1", "value1")));