diff --git a/agents/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/model/impl/AgentImpl.java b/agents/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/model/impl/AgentImpl.java index adb3cf691c..0eab4d7318 100644 --- a/agents/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/model/impl/AgentImpl.java +++ b/agents/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/model/impl/AgentImpl.java @@ -19,8 +19,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import static java.util.stream.Collectors.toMap; - /** * @author Anatoliy Bazko */ @@ -50,9 +48,7 @@ public class AgentImpl implements Agent { this.properties = properties; this.script = script; if (servers != null) { - this.servers = servers.entrySet() - .stream() - .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); + this.servers = new HashMap<>(servers); } } diff --git a/assembly/assembly-wsmaster-war/pom.xml b/assembly/assembly-wsmaster-war/pom.xml index 7911a72b40..e02b597d34 100644 --- a/assembly/assembly-wsmaster-war/pom.xml +++ b/assembly/assembly-wsmaster-war/pom.xml @@ -62,6 +62,12 @@ javax.inject javax.inject + + org.eclipse.che + bootstrapper + tar.gz + linux_amd64 + org.eclipse.che exec-agent @@ -312,6 +318,25 @@ true + + copy-bootstrapper + process-resources + + copy + + + + + org.eclipse.che + bootstrapper + linux_amd64 + tar.gz + bootstrapper.tar.gz + + + ${project.build.directory}/classes + + diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties index 8f875bda6f..09a686417a 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties @@ -103,6 +103,9 @@ che.workspace.hosts=NULL # che-host is a hostname entry added to /etc/hosts of the workspace by the Che server. che.workspace.che_server_endpoint=http://che-host:${SERVER_PORT}/wsmaster/api +# This is the webscoket base endpoint of the workspace master running within the core Che server. +che.workspace.che_server_websocket_endpoint_base=ws://che-host:${SERVER_PORT}/wsmaster + ### AGENTS # When the Che server launches a new workspace, Che pings a mini Che server running inside of the # workspace runtime. We call this mini-Che an "agent". The Che server knows that the workspace @@ -259,6 +262,20 @@ che.docker.tcp_connection_read_timeout_ms=600000 # to determine swap size. To disable swap set to 0. che.docker.swap=-1 +### Che docker infrastructure parameters + +# Time (in minutes) given for bootstrapping. +# If boostrapping is not finished in time it will be failed and workspace start will fail. +che.infra.docker.bootstrapper.timeout_min=10 + +# Time (in seconds) given for one installer to complete its installation. +# If installation is not finished in time it will be interrupted. +che.infra.docker.bootstrapper.installer_timeout_sec=180 + +# Time(in seconds) between servers availability checks. +# Once servers for one installer available - checks stopped. +che.infra.docker.bootstrapper.server_check_period_sec=3 + ### INTERNAL # Remove locations where internal message bus events should be propagated to. # For debugging - set to retrieve internal events from external clients. diff --git a/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/runtime/BootstrapperStatus.java b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/runtime/BootstrapperStatus.java index ad4a907339..ba72c76029 100644 --- a/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/runtime/BootstrapperStatus.java +++ b/core/che-core-api-model/src/main/java/org/eclipse/che/api/core/model/workspace/runtime/BootstrapperStatus.java @@ -18,9 +18,9 @@ package org.eclipse.che.api.core.model.workspace.runtime; public enum BootstrapperStatus { /** - * Bootstrapper is ready to work, start installers, push events, etc. + * Bootstrapping is in progress. */ - AVAILABLE, + STARTING, /** * Bootstrapping done, everything is started ok. diff --git a/infrastructures/docker/pom.xml b/infrastructures/docker/pom.xml index 770699e6dc..572f1a0e01 100644 --- a/infrastructures/docker/pom.xml +++ b/infrastructures/docker/pom.xml @@ -38,6 +38,10 @@ com.fasterxml.jackson.dataformat jackson-dataformat-yaml + + com.google.code.gson + gson + com.google.guava guava diff --git a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/Bootstrapper.java b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/Bootstrapper.java new file mode 100644 index 0000000000..6076a5f715 --- /dev/null +++ b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/Bootstrapper.java @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.workspace.infrastructure.docker; + +import com.google.gson.Gson; +import com.google.inject.assistedinject.Assisted; + +import org.eclipse.che.api.agent.server.model.impl.AgentImpl; +import org.eclipse.che.api.core.model.workspace.runtime.BootstrapperStatus; +import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.core.notification.EventSubscriber; +import org.eclipse.che.api.core.util.FileCleaner; +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.eclipse.che.api.workspace.server.spi.InternalInfrastructureException; +import org.eclipse.che.api.workspace.shared.dto.RuntimeIdentityDto; +import org.eclipse.che.api.workspace.shared.dto.event.BootstrapperStatusEvent; +import org.eclipse.che.commons.lang.TarUtils; +import org.eclipse.che.workspace.infrastructure.docker.server.InstallerEndpoint; + +import javax.inject.Inject; +import javax.inject.Named; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Bootstraps installers. + * + * @author Sergii Leshchenko + */ +public class Bootstrapper { + private static final Gson GSON = new Gson(); + private static final AtomicInteger ENDPOINT_IDS = new AtomicInteger(); + + private static final String BOOTSTRAPPER_BASE_DIR = "/tmp/"; + private static final String BOOTSTRAPPER_DIR = BOOTSTRAPPER_BASE_DIR + "bootstrapper/"; + private static final String BOOTSTRAPPER_FILE = "bootstrapper"; + private static final String CONFIG_FILE = "config.json"; + + private final String machineName; + private final RuntimeIdentity runtimeIdentity; + private final DockerMachine dockerMachine; + private final List agents; + private final int bootstrappingTimeoutMinutes; + private final int serverCheckPeriodSeconds; + private final int installerTimeoutSeconds; + private final String installerEndpoint; + private final EventService eventService; + private final EventSubscriber bootstrapperStatusListener; + private CompletableFuture finishEventFuture; + + @Inject + public Bootstrapper(@Assisted String machineName, + @Assisted RuntimeIdentity runtimeIdentity, + @Assisted DockerMachine dockerMachine, + @Assisted List agents, + @Named("che.workspace.che_server_websocket_endpoint_base") String websocketBaseEndpoint, + @Named("che.infra.docker.bootstrapper.timeout_min") int bootstrappingTimeoutMinutes, + @Named("che.infra.docker.bootstrapper.installer_timeout_sec") int installerTimeoutSeconds, + @Named("che.infra.docker.bootstrapper.server_check_period_sec") int serverCheckPeriodSeconds, + EventService eventService) { + this.machineName = machineName; + this.runtimeIdentity = runtimeIdentity; + this.dockerMachine = dockerMachine; + this.agents = agents; + this.installerEndpoint = websocketBaseEndpoint + InstallerEndpoint.INSTALLER_WEBSOCKET_ENDPOINT_BASE; + this.bootstrappingTimeoutMinutes = bootstrappingTimeoutMinutes; + this.serverCheckPeriodSeconds = serverCheckPeriodSeconds; + this.installerTimeoutSeconds = installerTimeoutSeconds; + this.eventService = eventService; + this.bootstrapperStatusListener = event -> { + BootstrapperStatus status = event.getStatus(); + //skip starting status event + if (status.equals(BootstrapperStatus.DONE) || status.equals(BootstrapperStatus.FAILED)) { + //check boostrapper belongs to current runtime and machine + RuntimeIdentityDto runtimeId = event.getRuntimeId(); + if (event.getMachineName().equals(machineName) + && runtimeIdentity.getEnvName().equals(runtimeId.getEnvName()) + && runtimeIdentity.getOwner().equals(runtimeId.getOwner()) + && runtimeIdentity.getWorkspaceId().equals(runtimeId.getWorkspaceId())) { + + finishEventFuture.complete(event); + } + } + }; + } + + /** + * Bootstraps installers and wait while they finished. + * + * @throws InfrastructureException + * when bootstrapping timeout reached + * @throws InfrastructureException + * when bootstrapping failed + * @throws InfrastructureException + * when any other error occurs while bootstrapping + */ + public void bootstrap() throws InfrastructureException { + if (finishEventFuture != null) { + throw new IllegalStateException("Bootstrap method must be called only once."); + } + this.finishEventFuture = new CompletableFuture<>(); + + injectBootstrapper(); + + this.eventService.subscribe(bootstrapperStatusListener, BootstrapperStatusEvent.class); + try { + String endpoint = installerEndpoint + ENDPOINT_IDS.getAndIncrement(); + dockerMachine.exec(BOOTSTRAPPER_DIR + BOOTSTRAPPER_FILE + + " -machine-name " + machineName + + " -runtime-id " + String.format("%s:%s:%s", runtimeIdentity.getWorkspaceId(), + runtimeIdentity.getEnvName(), + runtimeIdentity.getOwner()) + + " -push-endpoint " + endpoint + + " -push-logs-endpoint " + endpoint + + " -server-check-period " + serverCheckPeriodSeconds + + " -installer-timeout " + installerTimeoutSeconds + + " -file " + BOOTSTRAPPER_DIR + CONFIG_FILE, + null); + + //waiting for DONE or FAILED bootstrapper status event + BootstrapperStatusEvent resultEvent = finishEventFuture.get(bootstrappingTimeoutMinutes, TimeUnit.MINUTES); + if (resultEvent.getStatus().equals(BootstrapperStatus.FAILED)) { + throw new InfrastructureException(resultEvent.getError()); + } + } catch (ExecutionException e) { + throw new InfrastructureException(e.getCause().getMessage(), e); + } catch (TimeoutException e) { + throw new InfrastructureException("Bootstrapping of machine " + machineName + " reached timeout"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new InfrastructureException("Bootstrapping of machine " + machineName + " was interrupted"); + } finally { + this.eventService.unsubscribe(bootstrapperStatusListener, BootstrapperStatusEvent.class); + } + } + + private void injectBootstrapper() throws InfrastructureException { + dockerMachine.putResource(BOOTSTRAPPER_BASE_DIR, Thread.currentThread() + .getContextClassLoader() + .getResourceAsStream("bootstrapper.tar.gz")); + // inject config file + File configFileArchive = null; + try { + configFileArchive = createArchive(CONFIG_FILE, GSON.toJson(agents)); + dockerMachine.putResource(BOOTSTRAPPER_DIR, new FileInputStream(configFileArchive)); + } catch (FileNotFoundException e) { + throw new InternalInfrastructureException(e.getMessage(), e); + } finally { + if (configFileArchive != null) { + FileCleaner.addFile(configFileArchive); + } + } + } + + private File createArchive(String filename, String content) throws InfrastructureException { + Path bootstrapperConfTmp = null; + try { + bootstrapperConfTmp = Files.createTempDirectory(filename); + Path configFile = bootstrapperConfTmp.resolve(filename); + Files.copy(new ByteArrayInputStream(content.getBytes()), configFile); + Path bootstrapperConfArchive = Files.createTempFile(filename, ".tar.gz"); + TarUtils.tarFiles(bootstrapperConfArchive.toFile(), configFile.toFile()); + return bootstrapperConfArchive.toFile(); + } catch (IOException e) { + throw new InternalInfrastructureException("Error occurred while injecting bootstrapping conf. " + + e.getMessage(), e); + } finally { + if (bootstrapperConfTmp != null) { + FileCleaner.addFile(bootstrapperConfTmp.toFile()); + } + } + } +} diff --git a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/BootstrapperFactory.java b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/BootstrapperFactory.java new file mode 100644 index 0000000000..039251c06e --- /dev/null +++ b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/BootstrapperFactory.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.workspace.infrastructure.docker; + +import com.google.inject.assistedinject.Assisted; + +import org.eclipse.che.api.agent.server.model.impl.AgentImpl; +import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; + +import java.util.List; + +/** + * @author Sergii Leshchenko + */ +public interface BootstrapperFactory { + Bootstrapper create(@Assisted String machineName, + @Assisted RuntimeIdentity runtimeIdentity, + @Assisted DockerMachine dockerMachine, + @Assisted List agents); +} diff --git a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerInfraModule.java b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerInfraModule.java index 7f0546dcb7..a261c2aef7 100644 --- a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerInfraModule.java +++ b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerInfraModule.java @@ -108,5 +108,9 @@ public class DockerInfraModule extends AbstractModule { install(new FactoryModuleBuilder() .implement(DockerInternalRuntime.class, DockerInternalRuntime.class) .build(DockerRuntimeFactory.class)); + + install(new FactoryModuleBuilder() + .implement(Bootstrapper.class, Bootstrapper.class) + .build(BootstrapperFactory.class)); } } diff --git a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerInternalRuntime.java b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerInternalRuntime.java index be2503f730..c20bd1f512 100644 --- a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerInternalRuntime.java +++ b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerInternalRuntime.java @@ -13,7 +13,6 @@ package org.eclipse.che.workspace.infrastructure.docker; import com.google.common.collect.ImmutableMap; import com.google.inject.assistedinject.Assisted; -import org.eclipse.che.api.agent.shared.model.impl.AgentImpl; import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.model.machine.MachineSource; import org.eclipse.che.api.core.model.workspace.runtime.Machine; @@ -33,7 +32,6 @@ import org.eclipse.che.api.workspace.server.spi.InternalRuntime; import org.eclipse.che.api.workspace.shared.dto.event.MachineStatusEvent; import org.eclipse.che.api.workspace.shared.dto.event.ServerStatusEvent; import org.eclipse.che.dto.server.DtoFactory; -import org.eclipse.che.plugin.docker.client.MessageProcessor; import org.eclipse.che.workspace.infrastructure.docker.exception.SourceNotFoundException; import org.eclipse.che.workspace.infrastructure.docker.model.DockerBuildContext; import org.eclipse.che.workspace.infrastructure.docker.model.DockerContainerConfig; @@ -85,6 +83,7 @@ public class DockerInternalRuntime extends InternalRuntime private final DockerRegistryClient dockerRegistryClient; private final RuntimeIdentity identity; private final EventService eventService; + private final BootstrapperFactory bootstrapperFactory; @Inject public DockerInternalRuntime(@Assisted DockerRuntimeContext context, @@ -98,12 +97,14 @@ public class DockerInternalRuntime extends InternalRuntime DockerMachineStarter serviceStarter, SnapshotDao snapshotDao, DockerRegistryClient dockerRegistryClient, - EventService eventService) { + EventService eventService, + BootstrapperFactory bootstrapperFactory) { super(context, urlRewriter); this.devMachineName = devMachineName; this.dockerEnvironment = dockerEnvironment; this.identity = identity; this.eventService = eventService; + this.bootstrapperFactory = bootstrapperFactory; this.properties = new HashMap<>(); this.startSynchronizer = new StartSynchronizer(); this.contextsStorage = contextsStorage; @@ -235,34 +236,24 @@ public class DockerInternalRuntime extends InternalRuntime identity, isDev); } + try { checkStartInterruption(); startSynchronizer.addMachine(name, dockerMachine); + + InternalMachineConfig machineConfig = getContext().getMachineConfigs().get(name); + if (machineConfig == null) { + throw new InfrastructureException("Machine %s is not found in internal machines config of RuntimeContext"); + } + + bootstrapperFactory.create(name, identity, dockerMachine, machineConfig.getAgents()) + .bootstrap(); + + checkServersReadiness(name, dockerMachine); } catch (InfrastructureException e) { destroyMachineQuietly(name, dockerMachine); throw e; } - startAgents(name, dockerMachine); - checkServersReadiness(name, dockerMachine); - } - - // TODO rework to agent launchers - private void startAgents(String machineName, DockerMachine dockerMachine) throws InfrastructureException { - InternalMachineConfig machineConfig = getContext().getMachineConfigs().get(machineName); - if (machineConfig == null) { - throw new InfrastructureException("Machine %s is not found in internal machines config of RuntimeContext"); - } - for (AgentImpl agent : machineConfig.getAgents()) { - Thread thread = new Thread(() -> { - try { - dockerMachine.exec(agent.getScript(), MessageProcessor.getDevNull()); - } catch (InfrastructureException e) { - LOG.error(e.getLocalizedMessage(), e); - } - }); - thread.setDaemon(true); - thread.start(); - } } private void checkServersReadiness(String machineName, DockerMachine dockerMachine) diff --git a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerMachine.java b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerMachine.java index 97132d48be..19fe4aabe8 100644 --- a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerMachine.java +++ b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/DockerMachine.java @@ -27,6 +27,7 @@ import org.eclipse.che.plugin.docker.client.json.ContainerInfo; import org.eclipse.che.plugin.docker.client.params.CommitParams; import org.eclipse.che.plugin.docker.client.params.CreateExecParams; import org.eclipse.che.plugin.docker.client.params.PushParams; +import org.eclipse.che.plugin.docker.client.params.PutResourceParams; import org.eclipse.che.plugin.docker.client.params.RemoveContainerParams; import org.eclipse.che.plugin.docker.client.params.RemoveImageParams; import org.eclipse.che.plugin.docker.client.params.StartExecParams; @@ -38,6 +39,7 @@ import org.slf4j.Logger; import javax.inject.Inject; import java.io.IOException; +import java.io.InputStream; import java.util.Collections; import java.util.Map; @@ -148,6 +150,14 @@ public class DockerMachine implements Machine { server.setStatus(status); } + public void putResource(String targetPath, InputStream sourceStream) throws InfrastructureException { + try { + docker.putResource(PutResourceParams.create(container, targetPath, sourceStream)); + } catch (IOException e) { + throw new InternalInfrastructureException(e.getMessage(), e); + } + } + public void exec(String script, MessageProcessor messageProcessor) throws InfrastructureException { try { Exec exec = docker.createExec(CreateExecParams.create(container, diff --git a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/server/InstallerEndpoint.java b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/server/InstallerEndpoint.java index bf0441c01c..227867acc5 100644 --- a/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/server/InstallerEndpoint.java +++ b/infrastructures/docker/src/main/java/org/eclipse/che/workspace/infrastructure/docker/server/InstallerEndpoint.java @@ -25,9 +25,11 @@ import javax.websocket.server.ServerEndpoint; * @author Max Shaposhnik (mshaposhnik@codenvy.com) */ -@ServerEndpoint(value = "/installer/websocket/{endpoint-id}", configurator = GuiceInjectorEndpointConfigurator.class) +@ServerEndpoint(value = InstallerEndpoint.INSTALLER_WEBSOCKET_ENDPOINT_BASE + "{endpoint-id}", configurator = GuiceInjectorEndpointConfigurator.class) public class InstallerEndpoint extends BasicWebSocketEndpoint { + public static final String INSTALLER_WEBSOCKET_ENDPOINT_BASE = "/installer/websocket/"; + @Inject public InstallerEndpoint(WebSocketSessionRegistry registry, MessagesReSender reSender, diff --git a/pom.xml b/pom.xml index 1c99261c97..e4fd52d1b0 100644 --- a/pom.xml +++ b/pom.xml @@ -106,6 +106,18 @@ ${che.version} war + + org.eclipse.che + bootstrapper + ${che.version} + + + org.eclipse.che + bootstrapper + ${che.version} + tar.gz + linux_amd64 + org.eclipse.che exec-agent diff --git a/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/event/BootstrapperStatusEvent.java b/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/event/BootstrapperStatusEvent.java index 6361f9a0a6..be75beb02a 100644 --- a/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/event/BootstrapperStatusEvent.java +++ b/wsmaster/che-core-api-workspace-shared/src/main/java/org/eclipse/che/api/workspace/shared/dto/event/BootstrapperStatusEvent.java @@ -28,14 +28,6 @@ public interface BootstrapperStatusEvent { BootstrapperStatusEvent withStatus(BootstrapperStatus status); - - String getInstaller(); - - void setInstaller(String installer); - - BootstrapperStatusEvent withInstaller(String installer); - - String getMachineName(); void setMachineName(String machineName); diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/spi/InternalMachineConfig.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/spi/InternalMachineConfig.java index 7a685cc772..87e842d67c 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/spi/InternalMachineConfig.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/spi/InternalMachineConfig.java @@ -53,8 +53,8 @@ public class InternalMachineConfig { this.servers.putAll(originalConfig.getServers()); this.attributes = new HashMap<>(originalConfig.getAttributes()); - if(agentRegistry != null && agentSorter != null) - initAgents(originalConfig.getAgents()); + if (agentRegistry != null && agentSorter != null) + initAgents(originalConfig.getAgents()); } /**