From 67568ba08032b9a09645f3c45c6232c37b75f6a7 Mon Sep 17 00:00:00 2001 From: David Festal Date: Tue, 15 Jan 2019 21:48:48 +0100 Subject: [PATCH 01/27] Endpoint to retrieve the up-to-date CDN.json file for the default editor This is related to issue https://github.com/redhat-developer/rh-che/issues/1071 Signed-off-by: David Festal --- assembly/assembly-wsmaster-war/pom.xml | 4 + assembly/fabric8-ide-dashboard-war/pom.xml | 3 +- .../ide-fetcher/che-ide-fetcher.service.ts | 155 +++++++++ dockerfiles/che-fabric8/Dockerfile | 1 + plugins/fabric8-cdn-support/pom.xml | 114 +++++++ .../che/cdnSupport/CdnSupportService.java | 315 ++++++++++++++++++ .../cdnSupport/CdnSupportWsMasterModule.java | 29 ++ .../che/cdnSupport/CdnSupportServiceTest.java | 274 +++++++++++++++ plugins/pom.xml | 1 + pom.xml | 6 + 10 files changed, 901 insertions(+), 1 deletion(-) create mode 100644 assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts create mode 100644 plugins/fabric8-cdn-support/pom.xml create mode 100644 plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java create mode 100644 plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportWsMasterModule.java create mode 100644 plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java diff --git a/assembly/assembly-wsmaster-war/pom.xml b/assembly/assembly-wsmaster-war/pom.xml index d02883b0d..f6f3c23af 100644 --- a/assembly/assembly-wsmaster-war/pom.xml +++ b/assembly/assembly-wsmaster-war/pom.xml @@ -44,6 +44,10 @@ com.redhat.che che-plugin-analytics-wsmaster + + com.redhat.che + fabric8-cdn-support + com.redhat.che fabric8-end2end-flow diff --git a/assembly/fabric8-ide-dashboard-war/pom.xml b/assembly/fabric8-ide-dashboard-war/pom.xml index 1b670f964..3b001ee42 100644 --- a/assembly/fabric8-ide-dashboard-war/pom.xml +++ b/assembly/fabric8-ide-dashboard-war/pom.xml @@ -212,7 +212,8 @@ - > + + > diff --git a/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts b/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts new file mode 100644 index 000000000..01ec1787a --- /dev/null +++ b/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2016-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +'use strict'; +import {CheBranding} from '../branding/che-branding.factory'; + +const IDE_FETCHER_CALLBACK_ID = 'cheIdeFetcherCallback'; + +/** + * Provides a way to download IDE .js and then cache it before trying to load the IDE + * @author Florent Benoit + * @author Oleksii Orel + */ +export class CheIdeFetcher { + + static $inject = ['$log', '$http', '$window', 'cheBranding']; + + private $log: ng.ILogService; + private $http: ng.IHttpService; + private $window: ng.IWindowService; + private cheBranding: CheBranding; + private userAgent: string; + + /** + * Default constructor that is using resource injection + */ + constructor($log: ng.ILogService, $http: ng.IHttpService, $window: ng.IWindowService, cheBranding: CheBranding) { + this.$log = $log; + this.$http = $http; + this.$window = $window; + this.cheBranding = cheBranding; + + this.userAgent = this.getUserAgent(); + + const callback = () => { + this.findMappingFile(); + this.cheBranding.unregisterCallback(IDE_FETCHER_CALLBACK_ID); + }; + this.cheBranding.registerCallback(IDE_FETCHER_CALLBACK_ID, callback.bind(this)); + } + + /** + * Gets user agent. + * @returns {string} + */ + getUserAgent(): string { + const userAgent = this.$window.navigator.userAgent.toLowerCase(); + const docMode = (this.$window.document).documentMode; + + if (userAgent.indexOf('webkit') !== -1) { + return 'safari'; + } else if (userAgent.indexOf('msie') !== -1) { + if (docMode >= 10 && docMode < 11) { + return 'ie10'; + } else if (docMode >= 9 && docMode < 11) { + return 'ie9'; + } else if (docMode >= 8 && docMode < 11) { + return 'ie8'; + } + } else if (userAgent.indexOf('gecko') !== -1) { + return 'gecko1_8'; + } + + return 'unknown'; + } + + /** + * Finds mapping file. + */ + findMappingFile(): void { + // get the content of the compilation mapping file + const randVal = Math.floor((Math.random() * 1000000) + 1); + const resourcesPath = this.cheBranding.getIdeResourcesPath(); + if (!resourcesPath) { + this.$log.log('Unable to get IDE resources path'); + return; + } + + const fileMappingUrl = `${resourcesPath}compilation-mappings.txt?uid=${randVal}`; + + this.$http.get(fileMappingUrl).then((response: {data: any}) => { + if (!response || !response.data) { + return; + } + let urlToLoad = this.getIdeUrlMappingFile(response.data); + // load the url + if (angular.isDefined(urlToLoad)) { + this.$log.log('Preloading IDE javascript', urlToLoad); + this.$http.get(urlToLoad, { cache: true}); + } else { + this.$log.error('Unable to find the IDE javascript file to cache'); + } + }, (error: any) => { + this.$log.log('unable to find compilation mapping file', error); + }); + + this.$http.get('/api/cdn-support/paths').then((response: {data: any}) => { + if (!response || !response.data) { + return; + } + const data = response.data; + this.$log.log("response.data = ", data); + data.forEach((entry) => { + let urlToLoad = entry.cdn; + // load the url + if (angular.isDefined(urlToLoad)) { + this.$log.log('Preloading Theia resource', urlToLoad); + const link = document.createElement("link"); + link.rel='prefetch'; + link.href=urlToLoad; + document.head.appendChild(link); + } else { + this.$log.error('Unable to find the Theia resource file to cache'); + } + }); + }, (error: any) => { + this.$log.log('unable to find Theia CDN resources to cache', error); + }); + } + + /** + * Gets URL of mapping file. + * @param data {string} + * @returns {string} + */ + getIdeUrlMappingFile(data: string): string { + let mappingFileUrl: string; + let javascriptFileName: string; + const mappings = data.split(new RegExp('^\\n', 'gm')); + const isPasses = mappings.some((mapping: string) => { + const subMappings = mapping.split('\n'); + const userAgent = subMappings.find((subMapping: string) => { + return subMapping.startsWith('user.agent '); + }).split(' ')[1]; + javascriptFileName = subMappings.find((subMapping: string) => { + return subMapping.endsWith('.cache.js'); + }); + return javascriptFileName && userAgent && this.userAgent === userAgent; + }); + if (isPasses && javascriptFileName) { + mappingFileUrl = this.cheBranding.getIdeResourcesPath() + javascriptFileName; + } + + return mappingFileUrl; + } + +} diff --git a/dockerfiles/che-fabric8/Dockerfile b/dockerfiles/che-fabric8/Dockerfile index 644c972ca..2fab89014 100644 --- a/dockerfiles/che-fabric8/Dockerfile +++ b/dockerfiles/che-fabric8/Dockerfile @@ -23,6 +23,7 @@ RUN yum -y update && \ curl -sSL "https://${DOCKER_BUCKET}/builds/Linux/x86_64/docker-${DOCKER_VERSION}" -o /usr/bin/docker && \ chmod +x /usr/bin/docker && \ yum -y remove openssl && \ + yum -y install skopeo \ yum clean all && \ echo "%root ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers && \ sed -i 's/Defaults requiretty/#Defaults requiretty/g' /etc/sudoers && \ diff --git a/plugins/fabric8-cdn-support/pom.xml b/plugins/fabric8-cdn-support/pom.xml new file mode 100644 index 000000000..cda0cc645 --- /dev/null +++ b/plugins/fabric8-cdn-support/pom.xml @@ -0,0 +1,114 @@ + + + + 4.0.0 + + fabric8-ide-plugins-parent + com.redhat.che + 1.0.0-SNAPSHOT + ../pom.xml + + fabric8-cdn-support + Fabric8 IDE :: Plugins :: CDN Support + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + com.google.guava + guava + + + com.google.inject + guice + + + javax.inject + javax.inject + + + javax.ws.rs + javax.ws.rs-api + + + org.eclipse.che.core + che-core-api-core + + + org.eclipse.che.core + che-core-api-workspace + + + org.eclipse.che.core + che-core-api-workspace-shared + + + org.eclipse.che.core + che-core-commons-annotations + + + org.eclipse.che.core + che-core-commons-inject + + + org.slf4j + slf4j-api + + + javax.servlet + javax.servlet-api + provided + + + ch.qos.logback + logback-classic + test + + + org.eclipse.che.core + che-core-db + test + + + org.hamcrest + hamcrest-core + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-testng + test + + + org.testng + testng + test + + + diff --git a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java new file mode 100644 index 000000000..1f7ba1a7c --- /dev/null +++ b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2016-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package com.redhat.che.cdnSupport; + +import static java.lang.String.format; +import static java.util.Spliterators.spliteratorUnknownSize; +import static java.util.concurrent.CompletableFuture.allOf; +import static java.util.concurrent.CompletableFuture.runAsync; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Stream.of; +import static java.util.stream.StreamSupport.stream; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.eclipse.che.api.core.util.ProcessUtil.process; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import org.eclipse.che.api.core.rest.Service; +import org.eclipse.che.api.core.util.LineConsumer; +import org.eclipse.che.api.core.util.ListLineConsumer; +import org.eclipse.che.api.core.util.ProcessUtil; +import org.eclipse.che.api.workspace.server.wsplugins.PluginMetaRetriever; +import org.eclipse.che.api.workspace.server.wsplugins.model.PluginMeta; +import org.eclipse.che.api.workspace.shared.Constants; +import org.eclipse.che.commons.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Singleton +@Path("cdn-support") +public class CdnSupportService extends Service { + private static final Logger LOG = LoggerFactory.getLogger(CdnSupportService.class); + private static final ObjectMapper YAML_PARSER = new ObjectMapper(new YAMLFactory()); + private static final ObjectMapper JSON_PARSER = new ObjectMapper(new JsonFactory()); + private static final String SKOPEO_BINARY = "skopeo"; + private static final String[] SKOPEO_HELP_ARGS = new String[] {"--help"}; + private static final long SKOPEO_HELP_TIMEOUT_SECONDS = 2; + private static final String SKOPEO_INSPECT_ARG = "inspect"; + private static final String SKOPEO_IMAGE_PREFIX = "docker://"; + private static final long SKOPEO_INSPECT_TIMEOUT_SECONDS = 5; + private static final String TAR_BINARY = "tar"; + private static final String TAR_ARG = "-xvf"; + private static final long TAR_TIMEOUT_SECONDS = 5; + private static final String CHE_PLUGIN_YAML_FILE_NAME = "che-plugin.yaml"; + private static final String CHE_PLUGIN_TGZ_FILE_NAME = "che-plugin.tar.gz"; + private static final String TEMP_DIR_PREFIX = "che-plugin-archive-dir"; + private static final String LABEL_NAME = "che-theia-plugin.cdn.artifacts"; + + private static final CompletableFuture[] FUTURE_ARRAY = new CompletableFuture[0]; + private static final CommandRunner RUNNER = new CommandRunner(); + + private final String editorToPrefetch; + private final CommandRunner commandRunner; + private final PluginMetaRetriever pluginMetaRetriever; + @VisibleForTesting String editorDefinitionUrl = null; + @VisibleForTesting String dockerImage = null; + + @Inject + public CdnSupportService( + PluginMetaRetriever pluginMetaRetriever, + @Nullable @Named("che.fabric8.cdn.prefetch.editor") String editorToPrefetch) { + this(RUNNER, pluginMetaRetriever, editorToPrefetch); + } + + CdnSupportService( + CommandRunner commandRunner, + PluginMetaRetriever pluginMetaRetriever, + @Nullable @Named("che.fabric8.cdn.prefetch.editor") String editorToPrefetch) { + this.pluginMetaRetriever = pluginMetaRetriever; + this.editorToPrefetch = editorToPrefetch; + this.commandRunner = commandRunner; + + // Test that the skopeo process is available + + try { + commandRunner.runCommand( + SKOPEO_BINARY, + SKOPEO_HELP_ARGS, + null, + SKOPEO_HELP_TIMEOUT_SECONDS, + TimeUnit.SECONDS, + null, + null); + } catch (IOException e) { + throw new RuntimeException( + "The `skopeo` command is not available. Please chack that is has been correctly installed in the Che server Docker image", + e); + } catch (ExecutionException | InterruptedException | TimeoutException e) { + LOG.warn("Exception during the `skopeo --help` command execution", e); + } + } + + @GET + @Path("paths") + @Produces(APPLICATION_JSON) + public String getPaths(@Context HttpServletRequest servletRequest) throws Exception { + final String empty = "{}"; + if (editorToPrefetch == null) { + return empty; + } + + LOG.debug("Searching the editor plugin entry in the plugin registry"); + + Collection plugins = + pluginMetaRetriever.get( + ImmutableMap.of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, editorToPrefetch)); + if (plugins.size() != 1) { + return empty; + } + + PluginMeta editor = plugins.toArray(new PluginMeta[] {})[0]; + if (editor == null) { + return empty; + } + + LOG.debug("Retrieving editor URL"); + + String url = editor.getUrl(); + if (url == null) { + return empty; + } + if (!url.equals(editorDefinitionUrl)) { + editorDefinitionUrl = url; + dockerImage = null; + } else { + LOG.debug("Editor full definition did't change"); + } + + if (dockerImage == null) { + dockerImage = readDockerImageName(editorDefinitionUrl); + } + + if (dockerImage == null) { + return empty; + } + + LOG.debug("Running {} on image {}", SKOPEO_BINARY, dockerImage); + final ListLineConsumer out = commandRunner.newOutputConsumer(); + final ListLineConsumer err = commandRunner.newErrorConsumer(); + Process skopeoInspect = + commandRunner.runCommand( + SKOPEO_BINARY, + new String[] {SKOPEO_INSPECT_ARG, SKOPEO_IMAGE_PREFIX + dockerImage}, + null, + SKOPEO_INSPECT_TIMEOUT_SECONDS, + TimeUnit.SECONDS, + out, + err); + if (skopeoInspect.exitValue() != 0) { + LOG.warn( + "{} failed when trying to retrieve the CDN label of docker image {} - exit code: {} - error output: {}", + SKOPEO_BINARY, + dockerImage, + skopeoInspect.exitValue(), + err.getText()); + return empty; + } + LOG.debug("Result of running skopeo on image {}: {}", dockerImage, out.getText()); + + JsonNode json = JSON_PARSER.readTree(out.getText()); + return json.path("Labels").path(LABEL_NAME).asText(empty); + } + + private String readDockerImageName(String editorDefinitionUrl) + throws JsonProcessingException, IOException, TimeoutException, InterruptedException, + ExecutionException { + LOG.debug("Creating temp folder"); + java.nio.file.Path archiveDir = + Files.createTempDirectory(FileSystems.getDefault().getPath("/tmp"), TEMP_DIR_PREFIX); + java.nio.file.Path archive = archiveDir.resolve(CHE_PLUGIN_TGZ_FILE_NAME); + + LOG.debug("Downloading Editor definition at URL {} into {}", editorDefinitionUrl, archive); + try (InputStream in = URI.create(editorDefinitionUrl).toURL().openStream()) { + Files.copy(in, archive); + } catch (IOException e) { + LOG.warn("Exception while downloading", e); + throw e; + } + + LOG.debug("Unzipping archive"); + final ListLineConsumer err = commandRunner.newErrorConsumer(); + Process tar = + commandRunner.runCommand( + TAR_BINARY, + new String[] {TAR_ARG, archive.toRealPath().toString()}, + archiveDir.toRealPath().toFile(), + TAR_TIMEOUT_SECONDS, + TimeUnit.SECONDS, + null, + err); + if (tar.exitValue() != 0) { + LOG.warn( + "Tar command failed with error status: {} and error log: {}", + tar.exitValue(), + err.getText()); + return null; + } + java.nio.file.Path pluginYaml = archiveDir.resolve(CHE_PLUGIN_YAML_FILE_NAME); + + LOG.debug("Parse Plugin YAML file: {}", pluginYaml.toAbsolutePath().toString()); + JsonNode json = YAML_PARSER.readTree(pluginYaml.toFile()); + + LOG.debug("Plugin YAML file content: {}", json.toString()); + return stream(spliteratorUnknownSize(json.path("containers").elements(), 0), false) + .findFirst() + .map(node -> node.path("image").asText()) + .orElse(null); + } + + static class CommandRunner { + Process runCommand( + String command, + String[] arguments, + File directory, + long timeout, + TimeUnit timeUnit, + LineConsumer outputConsumer, + LineConsumer errorConsumer) + throws IOException, TimeoutException, InterruptedException, ExecutionException { + final String[] commandLine = new String[arguments.length + 1]; + Lists.asList(command, arguments).toArray(commandLine); + ProcessBuilder processBuilder = new ProcessBuilder(); + if (directory != null) { + processBuilder.directory(directory); + } + processBuilder.command(commandLine); + LOG.debug("Command: {}", processBuilder.command()); + LOG.debug("Directory: {}", processBuilder.directory()); + Process process = processBuilder.start(); + CompletableFuture readers = + allOf( + of(outputConsumer, errorConsumer) + .map( + (LineConsumer consumer) -> { + return runAsync( + () -> { + if (outputConsumer != null) { + try { + // consume logs until process ends + process(process, outputConsumer); + } catch (IOException e) { + LOG.error( + format( + "Failed to complete reading of the process '%s' output or error due to occurred error", + Joiner.on(" ").join(commandLine)), + e); + } + } + }); + }) + .collect(toList()) + .toArray(FUTURE_ARRAY)); + try { + if (!process.waitFor(timeout, timeUnit)) { + try { + ProcessUtil.kill(process); + } catch (RuntimeException x) { + LOG.error( + "An error occurred while killing process '{}'", Joiner.on(" ").join(commandLine)); + } + throw new TimeoutException( + format( + "Process '%s' was terminated by timeout %s %s.", + Joiner.on(" ").join(commandLine), timeout, timeUnit.name().toLowerCase())); + } + } finally { + readers.get(2, TimeUnit.SECONDS); + } + + return process; + } + + ListLineConsumer newOutputConsumer() { + return new ListLineConsumer(); + } + + ListLineConsumer newErrorConsumer() { + return new ListLineConsumer(); + } + } +} diff --git a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportWsMasterModule.java b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportWsMasterModule.java new file mode 100644 index 000000000..4486fb11a --- /dev/null +++ b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportWsMasterModule.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2016-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package com.redhat.che.cdnSupport; + +import com.google.inject.AbstractModule; +import org.eclipse.che.inject.DynaModule; + +/** + * Module that allows pushing workspace events to the Segment Analytics tracking tool + * + * @author David festal + */ +@DynaModule +public class CdnSupportWsMasterModule extends AbstractModule { + + @Override + protected void configure() { + bind(CdnSupportService.class); + } +} diff --git a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java new file mode 100644 index 000000000..d546eb83e --- /dev/null +++ b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2016-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package com.redhat.che.cdnSupport; + +import static com.google.common.collect.ImmutableMap.of; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.testng.Assert.*; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; +import java.util.Collections; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; +import org.eclipse.che.api.core.util.ListLineConsumer; +import org.eclipse.che.api.workspace.server.wsplugins.PluginMetaRetriever; +import org.eclipse.che.api.workspace.server.wsplugins.model.PluginMeta; +import org.eclipse.che.api.workspace.shared.Constants; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; +import org.testng.collections.Lists; + +@Listeners(MockitoTestNGListener.class) +public class CdnSupportServiceTest { + private static String EDITOR_REF = "editor"; + private static String EDITOR_URL = "http://editorURL"; + private static String IMAGE_REF = "imageRef"; + + @Mock private CdnSupportService.CommandRunner commandRunner; + @Mock private PluginMetaRetriever metaRetriever; + @Mock private URLConnection urlConnection; + @Mock private HttpServletRequest servletRequest; + @Mock private PluginMeta pluginMeta; + @Mock private Process tarProcess; + @Mock private Process skopeoHelpProcess; + @Mock private Process skopeoInspectProcess; + @Mock private ListLineConsumer skopeoOutputConsumer; + @Mock private ListLineConsumer skopeoErrorConsumer; + @Mock private ListLineConsumer tarOutputConsumer; + @Mock private ListLineConsumer tarErrorConsumer; + + private CdnSupportService service; + + @BeforeClass + public void registerURLHandler() { + URL.setURLStreamHandlerFactory( + new URLStreamHandlerFactory() { + + @Override + public URLStreamHandler createURLStreamHandler(String protocol) { + return new URLStreamHandler() { + @Override + protected URLConnection openConnection(URL u) throws IOException { + return urlConnection; + } + }; + } + }); + } + + @BeforeMethod + public void setUp() throws Exception { + service = null; + } + + private void setupURLConnectionInputStream(String resourceName) throws IOException { + InputStream is = this.getClass().getResourceAsStream(resourceName); + doReturn(is).when(urlConnection).getInputStream(); + } + + @Test( + expectedExceptions = {RuntimeException.class}, + expectedExceptionsMessageRegExp = + "The `skopeo` command is not available. Please chack that is has been correctly installed in the Che server Docker image") + public void throwAtStartIfNoSkopeoBinary() throws Exception { + doThrow(IOException.class) + .when(commandRunner) + .runCommand("skopeo", new String[] {"--help"}, null, 2, TimeUnit.SECONDS, null, null); + new CdnSupportService(commandRunner, metaRetriever, ""); + } + + @Test + public void returnEmptyArrayWhenNoPreferredEditor() throws Exception { + service = new CdnSupportService(commandRunner, metaRetriever, null); + assertEquals(service.getPaths(servletRequest), "{}"); + } + + @Test + public void returnEmptyArrayWhenEditorNotFound() throws Exception { + doReturn(Collections.emptyList()).when(metaRetriever).get(any()); + service = new CdnSupportService(commandRunner, metaRetriever, "unknownEditor"); + assertEquals(service.getPaths(servletRequest), "{}"); + } + + @Test + public void returnEmptyArrayWhenEditorIsNull() throws Exception { + doReturn(Lists.newArrayList(new PluginMeta[] {null})) + .when(metaRetriever) + .get(any()); + service = new CdnSupportService(commandRunner, metaRetriever, "unknownEditor"); + assertEquals(service.getPaths(servletRequest), "{}"); + } + + @Test + public void returnEmptyArrayWhenEditorPluginUrlIsNull() throws Exception { + doReturn(Lists.newArrayList(new PluginMeta[] {pluginMeta})) + .when(metaRetriever) + .get(any()); + doReturn(null).when(pluginMeta).getUrl(); + service = new CdnSupportService(commandRunner, metaRetriever, EDITOR_REF); + + assertEquals(service.getPaths(servletRequest), "{}"); + verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); + } + + @Test + public void returnEmptyArrayWhenSkopeoFailsWithNonZeroCode() throws Exception { + doReturn(Lists.newArrayList(new PluginMeta[] {pluginMeta})) + .when(metaRetriever) + .get(any()); + + doReturn(EDITOR_URL).when(pluginMeta).getUrl(); + + lenient() + .when(commandRunner.runCommand(eq("skopeo"), any(), any(), anyLong(), any(), any(), any())) + .thenReturn(skopeoHelpProcess, skopeoInspectProcess); + doReturn(0).when(skopeoHelpProcess).exitValue(); + doReturn(1).when(skopeoInspectProcess).exitValue(); + when(commandRunner.newOutputConsumer()).thenReturn(skopeoOutputConsumer); + when(commandRunner.newErrorConsumer()).thenReturn(skopeoErrorConsumer); + when(skopeoErrorConsumer.getText()).thenReturn("error output"); + + service = new CdnSupportService(commandRunner, metaRetriever, EDITOR_REF); + service.editorDefinitionUrl = EDITOR_URL; + service.dockerImage = IMAGE_REF; + + try { + assertEquals(service.getPaths(servletRequest), "{}"); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + + verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); + } + + @Test + public void reuseExistingImageRefAndReturnLabelWhenSkopeoSucceeds() throws Exception { + doReturn(Lists.newArrayList(new PluginMeta[] {pluginMeta})) + .when(metaRetriever) + .get(any()); + + doReturn(EDITOR_URL).when(pluginMeta).getUrl(); + + lenient() + .when(commandRunner.runCommand(eq("skopeo"), any(), any(), anyLong(), any(), any(), any())) + .thenReturn(skopeoHelpProcess, skopeoInspectProcess); + doReturn(0).when(skopeoHelpProcess).exitValue(); + doReturn(0).when(skopeoInspectProcess).exitValue(); + when(commandRunner.newOutputConsumer()).thenReturn(skopeoOutputConsumer); + when(commandRunner.newErrorConsumer()).thenReturn(skopeoErrorConsumer); + when(skopeoErrorConsumer.getText()).thenReturn("skopeo error output content"); + when(skopeoOutputConsumer.getText()) + .thenReturn("{\"Labels\": { \"che-theia-plugin.cdn.artifacts\": \"cdnJsonContent\" }}"); + + service = new CdnSupportService(commandRunner, metaRetriever, EDITOR_REF); + service.editorDefinitionUrl = EDITOR_URL; + service.dockerImage = IMAGE_REF; + + try { + assertEquals(service.getPaths(servletRequest), "cdnJsonContent"); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + + verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); + verify(commandRunner, never()) + .runCommand(eq("tar"), any(), any(), anyLong(), any(), any(), any()); + } + + @Test + public void searchForImageRefAndReturnEmptyArrayWhenTarFails() throws Exception { + doReturn(Lists.newArrayList(new PluginMeta[] {pluginMeta})) + .when(metaRetriever) + .get(any()); + + doReturn(EDITOR_URL).when(pluginMeta).getUrl(); + + lenient() + .when(commandRunner.runCommand(eq("skopeo"), any(), any(), anyLong(), any(), any(), any())) + .thenReturn(skopeoHelpProcess); + doReturn(0).when(skopeoHelpProcess).exitValue(); + lenient() + .when(commandRunner.runCommand(eq("tar"), any(), any(), anyLong(), any(), any(), any())) + .thenReturn(tarProcess); + doReturn(1).when(tarProcess).exitValue(); + when(commandRunner.newErrorConsumer()).thenReturn(tarErrorConsumer); + when(tarErrorConsumer.getText()).thenReturn("tar error output content"); + + setupURLConnectionInputStream("/che-editor-plugin.tar.gz"); + service = new CdnSupportService(commandRunner, metaRetriever, EDITOR_REF); + + try { + assertEquals(service.getPaths(servletRequest), "{}"); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + + verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); + verify(commandRunner, times(1)) + .runCommand(eq("skopeo"), any(), any(), anyLong(), any(), any(), any()); + verify(commandRunner, times(1)) + .runCommand(eq("tar"), any(), any(), anyLong(), any(), any(), any()); + } + + @Test + public void searchForImageRefAndReturnLabelWhenSkopeoSucceeds() throws Exception { + doReturn(Lists.newArrayList(new PluginMeta[] {pluginMeta})) + .when(metaRetriever) + .get(any()); + + doReturn(EDITOR_URL).when(pluginMeta).getUrl(); + + lenient() + .when(commandRunner.runCommand(eq("skopeo"), any(), any(), anyLong(), any(), any(), any())) + .thenReturn(skopeoHelpProcess, skopeoInspectProcess); + doReturn(0).when(skopeoHelpProcess).exitValue(); + doReturn(0).when(skopeoInspectProcess).exitValue(); + lenient() + .when(commandRunner.runCommand(eq("tar"), any(), any(), anyLong(), any(), any(), any())) + .thenCallRealMethod(); + doReturn(0).when(tarProcess).exitValue(); + when(commandRunner.newOutputConsumer()).thenReturn(skopeoOutputConsumer); + when(commandRunner.newErrorConsumer()).thenReturn(tarErrorConsumer, skopeoErrorConsumer); + when(skopeoErrorConsumer.getText()).thenReturn("skopeo error output content"); + when(skopeoOutputConsumer.getText()) + .thenReturn("{\"Labels\": { \"che-theia-plugin.cdn.artifacts\": \"cdnJsonContent\" }}"); + + setupURLConnectionInputStream("/che-editor-plugin.tar.gz"); + service = new CdnSupportService(commandRunner, metaRetriever, EDITOR_REF); + + try { + assertEquals(service.getPaths(servletRequest), "cdnJsonContent"); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + + verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); + verify(commandRunner, times(2)) + .runCommand(eq("skopeo"), any(), any(), anyLong(), any(), any(), any()); + verify(commandRunner, times(1)) + .runCommand(eq("tar"), any(), any(), anyLong(), any(), any(), any()); + } +} diff --git a/plugins/pom.xml b/plugins/pom.xml index 5c0a07fce..d50be9e7e 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -30,5 +30,6 @@ fabric8-multi-tenant-manager che-plugin-analytics fabric8-end2end-flow + fabric8-cdn-support diff --git a/pom.xml b/pom.xml index 158b1cd97..3b8b7c127 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,12 @@ ${rh.che.plugins.version} jar + + com.redhat.che + fabric8-cdn-support + ${rh.che.plugins.version} + jar + com.redhat.che fabric8-end2end-flow From 270fd0ed7e3acb15ba05f38bb17d5ee557fed13c Mon Sep 17 00:00:00 2001 From: David Festal Date: Tue, 15 Jan 2019 22:25:08 +0100 Subject: [PATCH 02/27] small fix Signed-off-by: David Festal --- assembly/fabric8-ide-dashboard-war/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/assembly/fabric8-ide-dashboard-war/pom.xml b/assembly/fabric8-ide-dashboard-war/pom.xml index 3b001ee42..88c8aa75a 100644 --- a/assembly/fabric8-ide-dashboard-war/pom.xml +++ b/assembly/fabric8-ide-dashboard-war/pom.xml @@ -213,7 +213,6 @@ - > From aeffa37076ad3cb2355dcf617ee4049200a77bc2 Mon Sep 17 00:00:00 2001 From: David Festal Date: Wed, 16 Jan 2019 10:29:29 +0100 Subject: [PATCH 03/27] Start tests in a forked process to have the URL handler factory correcty overridden Signed-off-by: David Festal --- plugins/fabric8-cdn-support/pom.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/plugins/fabric8-cdn-support/pom.xml b/plugins/fabric8-cdn-support/pom.xml index cda0cc645..9a29cedbf 100644 --- a/plugins/fabric8-cdn-support/pom.xml +++ b/plugins/fabric8-cdn-support/pom.xml @@ -111,4 +111,17 @@ test + + + + org.apache.maven.plugins + maven-surefire-plugin + + 1 + false + + + + + From 67c2b9a24d3b9c210aed2905245ed528d1e242bd Mon Sep 17 00:00:00 2001 From: David Festal Date: Wed, 16 Jan 2019 16:34:39 +0100 Subject: [PATCH 04/27] useSystemClassLoader => true Signed-off-by: David Festal --- plugins/fabric8-cdn-support/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/fabric8-cdn-support/pom.xml b/plugins/fabric8-cdn-support/pom.xml index 9a29cedbf..bfae5b747 100644 --- a/plugins/fabric8-cdn-support/pom.xml +++ b/plugins/fabric8-cdn-support/pom.xml @@ -119,6 +119,7 @@ 1 false + true From 518620c615caa94b811871e8ee63fa9e4b7aae3e Mon Sep 17 00:00:00 2001 From: David Festal Date: Wed, 16 Jan 2019 17:31:16 +0100 Subject: [PATCH 05/27] troubleshooting logs Signed-off-by: David Festal --- .../che/cdnSupport/CdnSupportServiceTest.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java index d546eb83e..f7031f28d 100644 --- a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java +++ b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java @@ -65,12 +65,17 @@ public void registerURLHandler() { @Override public URLStreamHandler createURLStreamHandler(String protocol) { - return new URLStreamHandler() { - @Override - protected URLConnection openConnection(URL u) throws IOException { - return urlConnection; - } - }; + if (protocol.equals("http")) { + System.out.println("!!!!!! in createURLStreamHandler for : " + protocol); + return new URLStreamHandler() { + @Override + protected URLConnection openConnection(URL u) throws IOException { + return urlConnection; + } + }; + } else { + return null; + } } }); } @@ -82,6 +87,7 @@ public void setUp() throws Exception { private void setupURLConnectionInputStream(String resourceName) throws IOException { InputStream is = this.getClass().getResourceAsStream(resourceName); + System.out.println("!!!!!! is = " + is); doReturn(is).when(urlConnection).getInputStream(); } From 46b1c53ba025df8ff637f8f65cb8f07663515a04 Mon Sep 17 00:00:00 2001 From: David Festal Date: Wed, 16 Jan 2019 17:49:47 +0100 Subject: [PATCH 06/27] Adding test resources Signed-off-by: David Festal --- plugins/fabric8-cdn-support/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/fabric8-cdn-support/pom.xml b/plugins/fabric8-cdn-support/pom.xml index bfae5b747..dc2ad9e4b 100644 --- a/plugins/fabric8-cdn-support/pom.xml +++ b/plugins/fabric8-cdn-support/pom.xml @@ -112,6 +112,11 @@ + + + ${project.basedir}/src/test/resources + + org.apache.maven.plugins From bf3e34a526e1e17430d76c42ced7e1a7b1fa55a2 Mon Sep 17 00:00:00 2001 From: David Festal Date: Wed, 16 Jan 2019 18:10:30 +0100 Subject: [PATCH 07/27] Add the `tar.gz` test resource and clean the `pom.xml` Signed-off-by: David Festal --- plugins/fabric8-cdn-support/pom.xml | 19 ------------------ .../test/resources/che-editor-plugin.tar.gz | Bin 0 -> 577 bytes 2 files changed, 19 deletions(-) create mode 100644 plugins/fabric8-cdn-support/src/test/resources/che-editor-plugin.tar.gz diff --git a/plugins/fabric8-cdn-support/pom.xml b/plugins/fabric8-cdn-support/pom.xml index dc2ad9e4b..cda0cc645 100644 --- a/plugins/fabric8-cdn-support/pom.xml +++ b/plugins/fabric8-cdn-support/pom.xml @@ -111,23 +111,4 @@ test - - - - ${project.basedir}/src/test/resources - - - - - org.apache.maven.plugins - maven-surefire-plugin - - 1 - false - true - - - - - diff --git a/plugins/fabric8-cdn-support/src/test/resources/che-editor-plugin.tar.gz b/plugins/fabric8-cdn-support/src/test/resources/che-editor-plugin.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..9aba015c00b8232c98a626082d73b03049a2cb43 GIT binary patch literal 577 zcmV-H0>1qpiwFP#TmD-B1MO5@Z{jcz>~nsF#d{Km@R>JM&=slWDz$w^##s_mC${Vj zg@1oe0;Q!r-RY$2REa?lGV9$Pk2m2t_*zz(D1$vKlW@XQ(VPXuGK`>vW;HPXWQct7_Q6>w3OQ9_UJcj_9grHd(w98PSblJ0Z z6?v>fWUb6AQ#X1Mn3L=2!g^D%+z5@0o_9&!oY!2AsBIF4dSwHA5k(eoRfO88wUDqB z1kJ6p{1{%lq4>AM?e-F-XXk$;w5g;!OIf zFz)EX_GM+yE8P5ic$`fSC6@KwVsbb8P>Kf4r{=dxR>jE@SE*1{iT4b1XY{d8=TxF? z^r8RzETcBS@sqSepyq2w-u8u^Ru Date: Wed, 16 Jan 2019 19:07:02 +0100 Subject: [PATCH 08/27] Add missing property Signed-off-by: David Festal --- .../src/main/webapp/WEB-INF/classes/che/rh-che.properties | 4 ++++ openshift/rh-che.config.yaml | 1 + 2 files changed, 5 insertions(+) diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/rh-che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/rh-che.properties index ca81aee0a..f2d53ef5a 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/rh-che.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/rh-che.properties @@ -41,3 +41,7 @@ che.fabric8.end2end.protect.secret_key=NULL # End2End Flow bot protection: option to enable the use of the client IP # in the captcha server-side verification che.fabric8.end2end.protect.verify_with_ip=false + +# Full identifier of the default editor whose CDN resources +# should be prefetched during the dashboard client-side load +che.fabric8.cdn.prefetch.editor=NULL \ No newline at end of file diff --git a/openshift/rh-che.config.yaml b/openshift/rh-che.config.yaml index 1176c9527..5459b6edc 100644 --- a/openshift/rh-che.config.yaml +++ b/openshift/rh-che.config.yaml @@ -70,3 +70,4 @@ data: CHE_INFRA_KUBERNETES_SERVICE__ACCOUNT__NAME: "che-workspace" CHE_WORKSPACE_STORAGE: "/home/user/che/workspaces" CHE_WORKSPACE_PLUGIN__BROKER_INIT_IMAGE: "eclipse/che-init-plugin-broker:v0.7.1" + CHE_FABRIC8_CDN_PREFETCH_EDITOR: 'NULL' From 487247dbd8898ca328242adb24017d68c92fdb07 Mon Sep 17 00:00:00 2001 From: David Festal Date: Thu, 17 Jan 2019 10:21:11 +0100 Subject: [PATCH 09/27] Fix a small bug in the custom deployment, in order to debug more easily Signed-off-by: David Festal --- dev-scripts/deploy_custom_rh-che.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev-scripts/deploy_custom_rh-che.sh b/dev-scripts/deploy_custom_rh-che.sh index 2be554b6d..9bcdd7bd5 100755 --- a/dev-scripts/deploy_custom_rh-che.sh +++ b/dev-scripts/deploy_custom_rh-che.sh @@ -306,7 +306,8 @@ CHE_CONFIG_YAML=$(yq ".\"data\".\"CHE_KEYCLOAK_REALM\" = \"NULL\" | .\"data\".\"che.jdbc.username\" = \"$RH_CHE_JDBC_USERNAME\" | .\"data\".\"che.jdbc.password\" = \"$RH_CHE_JDBC_PASSWORD\" | .\"data\".\"che.jdbc.url\" = \"$RH_CHE_JDBC_URL\" | - .\"data\".\"CHE_LOG_LEVEL\" = \"plaintext\" " ${RH_CHE_CONFIG}) + .\"data\".\"CHE_LOG_LEVEL\" = \"INFO\"| + .\"data\".\"CHE_LOGS_APPENDERS_IMPL\" = \"plaintext\" " ${RH_CHE_CONFIG}) CHE_CONFIG_YAML=$(echo "$CHE_CONFIG_YAML" | \ yq ".\"data\".\"CHE_HOST\" = \"rhche-$RH_CHE_PROJECT_NAMESPACE.devtools-dev.ext.devshift.net\" | From 9a202a97a9d0c2eecb21d5147de6c3b37976f4c6 Mon Sep 17 00:00:00 2001 From: David Festal Date: Thu, 17 Jan 2019 17:06:41 +0100 Subject: [PATCH 10/27] Add missing new line Signed-off-by: David Festal --- .../src/main/webapp/WEB-INF/classes/che/rh-che.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/rh-che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/rh-che.properties index f2d53ef5a..583357cf9 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/rh-che.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/rh-che.properties @@ -44,4 +44,4 @@ che.fabric8.end2end.protect.verify_with_ip=false # Full identifier of the default editor whose CDN resources # should be prefetched during the dashboard client-side load -che.fabric8.cdn.prefetch.editor=NULL \ No newline at end of file +che.fabric8.cdn.prefetch.editor=NULL From db32c3f9cf04366d9a68377301f6576266f04106 Mon Sep 17 00:00:00 2001 From: David Festal Date: Thu, 17 Jan 2019 17:25:37 +0100 Subject: [PATCH 11/27] clean `system.out` calls Signed-off-by: David Festal --- .../java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java index f7031f28d..34beaa53b 100644 --- a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java +++ b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java @@ -66,7 +66,6 @@ public void registerURLHandler() { @Override public URLStreamHandler createURLStreamHandler(String protocol) { if (protocol.equals("http")) { - System.out.println("!!!!!! in createURLStreamHandler for : " + protocol); return new URLStreamHandler() { @Override protected URLConnection openConnection(URL u) throws IOException { @@ -87,7 +86,6 @@ public void setUp() throws Exception { private void setupURLConnectionInputStream(String resourceName) throws IOException { InputStream is = this.getClass().getResourceAsStream(resourceName); - System.out.println("!!!!!! is = " + is); doReturn(is).when(urlConnection).getInputStream(); } From 547d00f42e2b890f6bcff1ff067621906a540f87 Mon Sep 17 00:00:00 2001 From: David Festal Date: Thu, 17 Jan 2019 17:26:27 +0100 Subject: [PATCH 12/27] safer code Signed-off-by: David Festal --- .../java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java index 34beaa53b..34cec56a2 100644 --- a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java +++ b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java @@ -65,7 +65,7 @@ public void registerURLHandler() { @Override public URLStreamHandler createURLStreamHandler(String protocol) { - if (protocol.equals("http")) { + if ("http".equals(protocol)) { return new URLStreamHandler() { @Override protected URLConnection openConnection(URL u) throws IOException { From 3e0f807bd8bdec7d327493b148fbcf28947827e6 Mon Sep 17 00:00:00 2001 From: David Festal Date: Thu, 17 Jan 2019 17:28:25 +0100 Subject: [PATCH 13/27] Add myself in the authors Signed-off-by: David Festal --- .../src/components/ide-fetcher/che-ide-fetcher.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts b/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts index 01ec1787a..277bdb859 100644 --- a/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts +++ b/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts @@ -18,6 +18,7 @@ const IDE_FETCHER_CALLBACK_ID = 'cheIdeFetcherCallback'; * Provides a way to download IDE .js and then cache it before trying to load the IDE * @author Florent Benoit * @author Oleksii Orel + * @author David Festal */ export class CheIdeFetcher { From b6a5c32a397a06abedb1a4f1ab92136b6a29245a Mon Sep 17 00:00:00 2001 From: David Festal Date: Thu, 17 Jan 2019 17:31:46 +0100 Subject: [PATCH 14/27] remove unuseful debugging message Signed-off-by: David Festal --- .../src/components/ide-fetcher/che-ide-fetcher.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts b/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts index 277bdb859..af784f161 100644 --- a/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts +++ b/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts @@ -108,7 +108,6 @@ export class CheIdeFetcher { return; } const data = response.data; - this.$log.log("response.data = ", data); data.forEach((entry) => { let urlToLoad = entry.cdn; // load the url From 8594506a690ff3a3bbed754331d04de8b5a28d7c Mon Sep 17 00:00:00 2001 From: David Festal Date: Thu, 17 Jan 2019 17:51:15 +0100 Subject: [PATCH 15/27] minor typo Signed-off-by: David Festal --- .../main/java/com/redhat/che/cdnSupport/CdnSupportService.java | 2 +- .../java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java index 1f7ba1a7c..a461e0e37 100644 --- a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java +++ b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java @@ -117,7 +117,7 @@ public CdnSupportService( null); } catch (IOException e) { throw new RuntimeException( - "The `skopeo` command is not available. Please chack that is has been correctly installed in the Che server Docker image", + "The `skopeo` command is not available. Please check that is has been correctly installed in the Che server Docker image", e); } catch (ExecutionException | InterruptedException | TimeoutException e) { LOG.warn("Exception during the `skopeo --help` command execution", e); diff --git a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java index 34cec56a2..17105a2fe 100644 --- a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java +++ b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java @@ -92,7 +92,7 @@ private void setupURLConnectionInputStream(String resourceName) throws IOExcepti @Test( expectedExceptions = {RuntimeException.class}, expectedExceptionsMessageRegExp = - "The `skopeo` command is not available. Please chack that is has been correctly installed in the Che server Docker image") + "The `skopeo` command is not available. Please check that is has been correctly installed in the Che server Docker image") public void throwAtStartIfNoSkopeoBinary() throws Exception { doThrow(IOException.class) .when(commandRunner) From ded8b24926e126ba84f0ca05566000c4d0ecd373 Mon Sep 17 00:00:00 2001 From: David Festal Date: Thu, 17 Jan 2019 19:03:54 +0100 Subject: [PATCH 16/27] throw instead of returning an empty array in case of errors Signed-off-by: David Festal --- .../che/cdnSupport/CdnSupportService.java | 48 ++++++---- .../che/cdnSupport/CdnSupportServiceTest.java | 94 ++++++++++--------- 2 files changed, 79 insertions(+), 63 deletions(-) diff --git a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java index a461e0e37..5ae423d55 100644 --- a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java +++ b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java @@ -36,6 +36,7 @@ import java.net.URI; import java.nio.file.FileSystems; import java.nio.file.Files; +import java.text.MessageFormat; import java.util.Collection; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -49,6 +50,10 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; +import org.eclipse.che.api.core.ApiException; +import org.eclipse.che.api.core.BadRequestException; +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.core.util.LineConsumer; import org.eclipse.che.api.core.util.ListLineConsumer; @@ -128,9 +133,8 @@ public CdnSupportService( @Path("paths") @Produces(APPLICATION_JSON) public String getPaths(@Context HttpServletRequest servletRequest) throws Exception { - final String empty = "{}"; if (editorToPrefetch == null) { - return empty; + throw new NotFoundException("No editor is configured for CDN resource pre-fetching"); } LOG.debug("Searching the editor plugin entry in the plugin registry"); @@ -139,19 +143,19 @@ public String getPaths(@Context HttpServletRequest servletRequest) throws Except pluginMetaRetriever.get( ImmutableMap.of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, editorToPrefetch)); if (plugins.size() != 1) { - return empty; + throw new NotFoundException("Editor '" + editorToPrefetch + "' is unknown"); } PluginMeta editor = plugins.toArray(new PluginMeta[] {})[0]; if (editor == null) { - return empty; + throw new NotFoundException("Editor '" + editorToPrefetch + "' is unknown"); } LOG.debug("Retrieving editor URL"); String url = editor.getUrl(); if (url == null) { - return empty; + throw new BadRequestException("URL of editor '" + editorToPrefetch + "' is null"); } if (!url.equals(editorDefinitionUrl)) { editorDefinitionUrl = url; @@ -165,7 +169,10 @@ public String getPaths(@Context HttpServletRequest servletRequest) throws Except } if (dockerImage == null) { - return empty; + throw new BadRequestException( + "Plugin container image not found in the plugin descriptor of '" + + editorToPrefetch + + "'"); } LOG.debug("Running {} on image {}", SKOPEO_BINARY, dockerImage); @@ -181,23 +188,22 @@ public String getPaths(@Context HttpServletRequest servletRequest) throws Except out, err); if (skopeoInspect.exitValue() != 0) { - LOG.warn( - "{} failed when trying to retrieve the CDN label of docker image {} - exit code: {} - error output: {}", - SKOPEO_BINARY, - dockerImage, - skopeoInspect.exitValue(), - err.getText()); - return empty; + String message = + MessageFormat.format( + "{0} failed when trying to retrieve the CDN label of docker image {1} - exit code: {2} - error output: {3}", + SKOPEO_BINARY, dockerImage, skopeoInspect.exitValue(), err.getText()); + LOG.warn(message); + throw new ServerException(message); } LOG.debug("Result of running skopeo on image {}: {}", dockerImage, out.getText()); JsonNode json = JSON_PARSER.readTree(out.getText()); - return json.path("Labels").path(LABEL_NAME).asText(empty); + return json.path("Labels").path(LABEL_NAME).asText("{}"); } private String readDockerImageName(String editorDefinitionUrl) throws JsonProcessingException, IOException, TimeoutException, InterruptedException, - ExecutionException { + ExecutionException, ApiException { LOG.debug("Creating temp folder"); java.nio.file.Path archiveDir = Files.createTempDirectory(FileSystems.getDefault().getPath("/tmp"), TEMP_DIR_PREFIX); @@ -223,11 +229,13 @@ private String readDockerImageName(String editorDefinitionUrl) null, err); if (tar.exitValue() != 0) { - LOG.warn( - "Tar command failed with error status: {} and error log: {}", - tar.exitValue(), - err.getText()); - return null; + String message = + "Tar command failed with error status: " + + tar.exitValue() + + " and error log: " + + err.getText(); + LOG.warn(message); + throw new ServerException(message); } java.nio.file.Path pluginYaml = archiveDir.resolve(CHE_PLUGIN_YAML_FILE_NAME); diff --git a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java index 17105a2fe..1cfca1ffa 100644 --- a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java +++ b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java @@ -25,6 +25,9 @@ import java.util.Collections; import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest; +import org.eclipse.che.api.core.BadRequestException; +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.util.ListLineConsumer; import org.eclipse.che.api.workspace.server.wsplugins.PluginMetaRetriever; import org.eclipse.che.api.workspace.server.wsplugins.model.PluginMeta; @@ -100,42 +103,58 @@ public void throwAtStartIfNoSkopeoBinary() throws Exception { new CdnSupportService(commandRunner, metaRetriever, ""); } - @Test - public void returnEmptyArrayWhenNoPreferredEditor() throws Exception { + @Test( + expectedExceptions = {NotFoundException.class}, + expectedExceptionsMessageRegExp = "No editor is configured for CDN resource pre-fetching") + public void throwWhenNoPreferredEditor() throws Exception { service = new CdnSupportService(commandRunner, metaRetriever, null); - assertEquals(service.getPaths(servletRequest), "{}"); + service.getPaths(servletRequest); } - @Test - public void returnEmptyArrayWhenEditorNotFound() throws Exception { + @Test( + expectedExceptions = {NotFoundException.class}, + expectedExceptionsMessageRegExp = "Editor 'unknownEditor' is unknown") + public void throwWhenEditorNotFound() throws Exception { doReturn(Collections.emptyList()).when(metaRetriever).get(any()); service = new CdnSupportService(commandRunner, metaRetriever, "unknownEditor"); - assertEquals(service.getPaths(servletRequest), "{}"); + service.getPaths(servletRequest); } - @Test - public void returnEmptyArrayWhenEditorIsNull() throws Exception { + @Test( + expectedExceptions = {NotFoundException.class}, + expectedExceptionsMessageRegExp = "Editor 'unknownEditor' is unknown") + public void throwWhenEditorIsNull() throws Exception { doReturn(Lists.newArrayList(new PluginMeta[] {null})) .when(metaRetriever) .get(any()); service = new CdnSupportService(commandRunner, metaRetriever, "unknownEditor"); - assertEquals(service.getPaths(servletRequest), "{}"); + service.getPaths(servletRequest); } - @Test - public void returnEmptyArrayWhenEditorPluginUrlIsNull() throws Exception { + @Test( + expectedExceptions = {BadRequestException.class}, + expectedExceptionsMessageRegExp = "URL of editor 'editor' is null") + public void throwWhenEditorPluginUrlIsNull() throws Exception { doReturn(Lists.newArrayList(new PluginMeta[] {pluginMeta})) .when(metaRetriever) .get(any()); doReturn(null).when(pluginMeta).getUrl(); service = new CdnSupportService(commandRunner, metaRetriever, EDITOR_REF); - assertEquals(service.getPaths(servletRequest), "{}"); - verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); + try { + service.getPaths(servletRequest); + } catch (Exception e) { + throw e; + } finally { + verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); + } } - @Test - public void returnEmptyArrayWhenSkopeoFailsWithNonZeroCode() throws Exception { + @Test( + expectedExceptions = {ServerException.class}, + expectedExceptionsMessageRegExp = + "skopeo failed when trying to retrieve the CDN label of docker image imageRef - exit code: 1 - error output: skopeo error output") + public void throwWhenSkopeoFailsWithNonZeroCode() throws Exception { doReturn(Lists.newArrayList(new PluginMeta[] {pluginMeta})) .when(metaRetriever) .get(any()); @@ -149,20 +168,19 @@ public void returnEmptyArrayWhenSkopeoFailsWithNonZeroCode() throws Exception { doReturn(1).when(skopeoInspectProcess).exitValue(); when(commandRunner.newOutputConsumer()).thenReturn(skopeoOutputConsumer); when(commandRunner.newErrorConsumer()).thenReturn(skopeoErrorConsumer); - when(skopeoErrorConsumer.getText()).thenReturn("error output"); + when(skopeoErrorConsumer.getText()).thenReturn("skopeo error output"); service = new CdnSupportService(commandRunner, metaRetriever, EDITOR_REF); service.editorDefinitionUrl = EDITOR_URL; service.dockerImage = IMAGE_REF; try { - assertEquals(service.getPaths(servletRequest), "{}"); + service.getPaths(servletRequest); } catch (Exception e) { - e.printStackTrace(); throw e; + } finally { + verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); } - - verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); } @Test @@ -176,11 +194,9 @@ public void reuseExistingImageRefAndReturnLabelWhenSkopeoSucceeds() throws Excep lenient() .when(commandRunner.runCommand(eq("skopeo"), any(), any(), anyLong(), any(), any(), any())) .thenReturn(skopeoHelpProcess, skopeoInspectProcess); - doReturn(0).when(skopeoHelpProcess).exitValue(); doReturn(0).when(skopeoInspectProcess).exitValue(); when(commandRunner.newOutputConsumer()).thenReturn(skopeoOutputConsumer); when(commandRunner.newErrorConsumer()).thenReturn(skopeoErrorConsumer); - when(skopeoErrorConsumer.getText()).thenReturn("skopeo error output content"); when(skopeoOutputConsumer.getText()) .thenReturn("{\"Labels\": { \"che-theia-plugin.cdn.artifacts\": \"cdnJsonContent\" }}"); @@ -188,20 +204,18 @@ public void reuseExistingImageRefAndReturnLabelWhenSkopeoSucceeds() throws Excep service.editorDefinitionUrl = EDITOR_URL; service.dockerImage = IMAGE_REF; - try { - assertEquals(service.getPaths(servletRequest), "cdnJsonContent"); - } catch (Exception e) { - e.printStackTrace(); - throw e; - } + assertEquals(service.getPaths(servletRequest), "cdnJsonContent"); verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); verify(commandRunner, never()) .runCommand(eq("tar"), any(), any(), anyLong(), any(), any(), any()); } - @Test - public void searchForImageRefAndReturnEmptyArrayWhenTarFails() throws Exception { + @Test( + expectedExceptions = {ServerException.class}, + expectedExceptionsMessageRegExp = + "Tar command failed with error status: 1 and error log: tar error output content") + public void searchForImageRefAndThrowWhenTarFails() throws Exception { doReturn(Lists.newArrayList(new PluginMeta[] {pluginMeta})) .when(metaRetriever) .get(any()); @@ -225,15 +239,14 @@ public void searchForImageRefAndReturnEmptyArrayWhenTarFails() throws Exception try { assertEquals(service.getPaths(servletRequest), "{}"); } catch (Exception e) { - e.printStackTrace(); throw e; + } finally { + verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); + verify(commandRunner, times(1)) + .runCommand(eq("skopeo"), any(), any(), anyLong(), any(), any(), any()); + verify(commandRunner, times(1)) + .runCommand(eq("tar"), any(), any(), anyLong(), any(), any(), any()); } - - verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); - verify(commandRunner, times(1)) - .runCommand(eq("skopeo"), any(), any(), anyLong(), any(), any(), any()); - verify(commandRunner, times(1)) - .runCommand(eq("tar"), any(), any(), anyLong(), any(), any(), any()); } @Test @@ -262,12 +275,7 @@ public void searchForImageRefAndReturnLabelWhenSkopeoSucceeds() throws Exception setupURLConnectionInputStream("/che-editor-plugin.tar.gz"); service = new CdnSupportService(commandRunner, metaRetriever, EDITOR_REF); - try { - assertEquals(service.getPaths(servletRequest), "cdnJsonContent"); - } catch (Exception e) { - e.printStackTrace(); - throw e; - } + assertEquals(service.getPaths(servletRequest), "cdnJsonContent"); verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); verify(commandRunner, times(2)) From 353045f369cd440dc1ae53e3bba7f1fdd3df038f Mon Sep 17 00:00:00 2001 From: David Festal Date: Thu, 17 Jan 2019 19:04:55 +0100 Subject: [PATCH 17/27] typo Signed-off-by: David Festal --- .../src/components/ide-fetcher/che-ide-fetcher.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts b/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts index af784f161..d9e91bb84 100644 --- a/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts +++ b/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts @@ -122,7 +122,7 @@ export class CheIdeFetcher { } }); }, (error: any) => { - this.$log.log('unable to find Theia CDN resources to cache', error); + this.$log.log('Unable to find Theia CDN resources to cache', error); }); } From 9a69f5828a3681daf716b49747d92d59366b6e62 Mon Sep 17 00:00:00 2001 From: David Festal Date: Fri, 18 Jan 2019 11:15:10 +0100 Subject: [PATCH 18/27] Add examples of the new property Signed-off-by: David Festal --- .../src/main/webapp/WEB-INF/classes/che/rh-che.properties | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/rh-che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/rh-che.properties index 583357cf9..e119f7687 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/rh-che.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/rh-che.properties @@ -44,4 +44,8 @@ che.fabric8.end2end.protect.verify_with_ip=false # Full identifier of the default editor whose CDN resources # should be prefetched during the dashboard client-side load +# - Can be the identifier of an editor plugin in the plugin registry, like that: +# org.eclipse.che.editor.theia:1.0.0 +# - or the full path to a custom editor plugin, like that: +# https://raw.githubusercontent.com/davidfestal/che-theia-cdn/master/plugins/org.eclipse.che.editor.theia.david:1.0.2 che.fabric8.cdn.prefetch.editor=NULL From f4e2ec0651d0d01df5e392bda0751b60634742b0 Mon Sep 17 00:00:00 2001 From: David Festal Date: Fri, 18 Jan 2019 11:16:50 +0100 Subject: [PATCH 19/27] typo Signed-off-by: David Festal --- .../main/java/com/redhat/che/cdnSupport/CdnSupportService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java index 5ae423d55..5624df0db 100644 --- a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java +++ b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java @@ -161,7 +161,7 @@ public String getPaths(@Context HttpServletRequest servletRequest) throws Except editorDefinitionUrl = url; dockerImage = null; } else { - LOG.debug("Editor full definition did't change"); + LOG.debug("Editor full definition didn't change"); } if (dockerImage == null) { From d57be5ec742bced3525a58abba81fd5a0b2200ae Mon Sep 17 00:00:00 2001 From: David Festal Date: Fri, 18 Jan 2019 11:25:51 +0100 Subject: [PATCH 20/27] Use format instead of concatenation Signed-off-by: David Festal --- .../che/cdnSupport/CdnSupportService.java | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java index 5624df0db..522f68eb8 100644 --- a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java +++ b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java @@ -36,7 +36,6 @@ import java.net.URI; import java.nio.file.FileSystems; import java.nio.file.Files; -import java.text.MessageFormat; import java.util.Collection; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -155,7 +154,7 @@ public String getPaths(@Context HttpServletRequest servletRequest) throws Except String url = editor.getUrl(); if (url == null) { - throw new BadRequestException("URL of editor '" + editorToPrefetch + "' is null"); + throw new BadRequestException(format("URL of editor '%s' is null", editorToPrefetch)); } if (!url.equals(editorDefinitionUrl)) { editorDefinitionUrl = url; @@ -170,9 +169,9 @@ public String getPaths(@Context HttpServletRequest servletRequest) throws Except if (dockerImage == null) { throw new BadRequestException( - "Plugin container image not found in the plugin descriptor of '" - + editorToPrefetch - + "'"); + format( + "Plugin container image not found in the plugin descriptor of '%s'", + editorToPrefetch)); } LOG.debug("Running {} on image {}", SKOPEO_BINARY, dockerImage); @@ -189,8 +188,8 @@ public String getPaths(@Context HttpServletRequest servletRequest) throws Except err); if (skopeoInspect.exitValue() != 0) { String message = - MessageFormat.format( - "{0} failed when trying to retrieve the CDN label of docker image {1} - exit code: {2} - error output: {3}", + format( + "%s failed when trying to retrieve the CDN label of docker image %s - exit code: %d - error output: %s", SKOPEO_BINARY, dockerImage, skopeoInspect.exitValue(), err.getText()); LOG.warn(message); throw new ServerException(message); @@ -230,10 +229,9 @@ private String readDockerImageName(String editorDefinitionUrl) err); if (tar.exitValue() != 0) { String message = - "Tar command failed with error status: " - + tar.exitValue() - + " and error log: " - + err.getText(); + format( + "Tar command failed with error status: %d and error log: %s", + tar.exitValue(), err.getText()); LOG.warn(message); throw new ServerException(message); } From 68e1099a65c66b1542722bf2be608fb514e9eb0b Mon Sep 17 00:00:00 2001 From: David Festal Date: Fri, 18 Jan 2019 11:53:52 +0100 Subject: [PATCH 21/27] Make the docker image label name more general Signed-off-by: David Festal --- .../java/com/redhat/che/cdnSupport/CdnSupportService.java | 2 +- .../java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java index 522f68eb8..dca0cd28d 100644 --- a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java +++ b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java @@ -82,7 +82,7 @@ public class CdnSupportService extends Service { private static final String CHE_PLUGIN_YAML_FILE_NAME = "che-plugin.yaml"; private static final String CHE_PLUGIN_TGZ_FILE_NAME = "che-plugin.tar.gz"; private static final String TEMP_DIR_PREFIX = "che-plugin-archive-dir"; - private static final String LABEL_NAME = "che-theia-plugin.cdn.artifacts"; + private static final String LABEL_NAME = "che-plugin.cdn.artifacts"; private static final CompletableFuture[] FUTURE_ARRAY = new CompletableFuture[0]; private static final CommandRunner RUNNER = new CommandRunner(); diff --git a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java index 1cfca1ffa..1bb2f8eae 100644 --- a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java +++ b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java @@ -198,7 +198,7 @@ public void reuseExistingImageRefAndReturnLabelWhenSkopeoSucceeds() throws Excep when(commandRunner.newOutputConsumer()).thenReturn(skopeoOutputConsumer); when(commandRunner.newErrorConsumer()).thenReturn(skopeoErrorConsumer); when(skopeoOutputConsumer.getText()) - .thenReturn("{\"Labels\": { \"che-theia-plugin.cdn.artifacts\": \"cdnJsonContent\" }}"); + .thenReturn("{\"Labels\": { \"che-plugin.cdn.artifacts\": \"cdnJsonContent\" }}"); service = new CdnSupportService(commandRunner, metaRetriever, EDITOR_REF); service.editorDefinitionUrl = EDITOR_URL; @@ -270,7 +270,7 @@ public void searchForImageRefAndReturnLabelWhenSkopeoSucceeds() throws Exception when(commandRunner.newErrorConsumer()).thenReturn(tarErrorConsumer, skopeoErrorConsumer); when(skopeoErrorConsumer.getText()).thenReturn("skopeo error output content"); when(skopeoOutputConsumer.getText()) - .thenReturn("{\"Labels\": { \"che-theia-plugin.cdn.artifacts\": \"cdnJsonContent\" }}"); + .thenReturn("{\"Labels\": { \"che-plugin.cdn.artifacts\": \"cdnJsonContent\" }}"); setupURLConnectionInputStream("/che-editor-plugin.tar.gz"); service = new CdnSupportService(commandRunner, metaRetriever, EDITOR_REF); From da0589e169e0ffc70cde49bcc1020cc8926d5c8d Mon Sep 17 00:00:00 2001 From: David Festal Date: Fri, 18 Jan 2019 12:37:03 +0100 Subject: [PATCH 22/27] return empty array instead of empty object Signed-off-by: David Festal --- .../main/java/com/redhat/che/cdnSupport/CdnSupportService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java index dca0cd28d..4a84ee5ee 100644 --- a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java +++ b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java @@ -197,7 +197,7 @@ public String getPaths(@Context HttpServletRequest servletRequest) throws Except LOG.debug("Result of running skopeo on image {}: {}", dockerImage, out.getText()); JsonNode json = JSON_PARSER.readTree(out.getText()); - return json.path("Labels").path(LABEL_NAME).asText("{}"); + return json.path("Labels").path(LABEL_NAME).asText("[]"); } private String readDockerImageName(String editorDefinitionUrl) From 02a8a37a385a120c1930de11e57e4cf72f724008 Mon Sep 17 00:00:00 2001 From: David Festal Date: Fri, 18 Jan 2019 12:37:23 +0100 Subject: [PATCH 23/27] never cache the request to `paths` Signed-off-by: David Festal --- .../src/components/ide-fetcher/che-ide-fetcher.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts b/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts index d9e91bb84..00d81694f 100644 --- a/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts +++ b/assembly/fabric8-ide-dashboard-war/src/components/ide-fetcher/che-ide-fetcher.service.ts @@ -103,7 +103,7 @@ export class CheIdeFetcher { this.$log.log('unable to find compilation mapping file', error); }); - this.$http.get('/api/cdn-support/paths').then((response: {data: any}) => { + this.$http.get('/api/cdn-support/paths', {cache: false}).then((response: {data: any}) => { if (!response || !response.data) { return; } From 5f86c230cb8c38e70fa3ab1561d2bed7c599e4a3 Mon Sep 17 00:00:00 2001 From: David Festal Date: Fri, 18 Jan 2019 18:55:53 +0100 Subject: [PATCH 24/27] Changes proposed by the PR quality review Signed-off-by: David Festal --- .../redhat/che/{cdnSupport => cdn}/CdnSupportService.java | 7 ++++++- .../che/{cdnSupport => cdn}/CdnSupportWsMasterModule.java | 2 +- .../che/{cdnSupport => cdn}/CdnSupportServiceTest.java | 3 +-- 3 files changed, 8 insertions(+), 4 deletions(-) rename plugins/fabric8-cdn-support/src/main/java/com/redhat/che/{cdnSupport => cdn}/CdnSupportService.java (98%) rename plugins/fabric8-cdn-support/src/main/java/com/redhat/che/{cdnSupport => cdn}/CdnSupportWsMasterModule.java (95%) rename plugins/fabric8-cdn-support/src/test/java/com/redhat/che/{cdnSupport => cdn}/CdnSupportServiceTest.java (99%) diff --git a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportService.java similarity index 98% rename from plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java rename to plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportService.java index 4a84ee5ee..7f860d7a3 100644 --- a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportService.java +++ b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportService.java @@ -9,7 +9,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation */ -package com.redhat.che.cdnSupport; +package com.redhat.che.cdn; import static java.lang.String.format; import static java.util.Spliterators.spliteratorUnknownSize; @@ -100,6 +100,7 @@ public CdnSupportService( this(RUNNER, pluginMetaRetriever, editorToPrefetch); } + @VisibleForTesting CdnSupportService( CommandRunner commandRunner, PluginMetaRetriever pluginMetaRetriever, @@ -247,7 +248,9 @@ private String readDockerImageName(String editorDefinitionUrl) .orElse(null); } + @VisibleForTesting static class CommandRunner { + @VisibleForTesting Process runCommand( String command, String[] arguments, @@ -310,10 +313,12 @@ Process runCommand( return process; } + @VisibleForTesting ListLineConsumer newOutputConsumer() { return new ListLineConsumer(); } + @VisibleForTesting ListLineConsumer newErrorConsumer() { return new ListLineConsumer(); } diff --git a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportWsMasterModule.java b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportWsMasterModule.java similarity index 95% rename from plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportWsMasterModule.java rename to plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportWsMasterModule.java index 4486fb11a..e3031b84f 100644 --- a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdnSupport/CdnSupportWsMasterModule.java +++ b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportWsMasterModule.java @@ -9,7 +9,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation */ -package com.redhat.che.cdnSupport; +package com.redhat.che.cdn; import com.google.inject.AbstractModule; import org.eclipse.che.inject.DynaModule; diff --git a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdn/CdnSupportServiceTest.java similarity index 99% rename from plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java rename to plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdn/CdnSupportServiceTest.java index 1bb2f8eae..cd9e1c6f6 100644 --- a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdnSupport/CdnSupportServiceTest.java +++ b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdn/CdnSupportServiceTest.java @@ -9,7 +9,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation */ -package com.redhat.che.cdnSupport; +package com.redhat.che.cdn; import static com.google.common.collect.ImmutableMap.of; import static org.mockito.ArgumentMatchers.*; @@ -56,7 +56,6 @@ public class CdnSupportServiceTest { @Mock private Process skopeoInspectProcess; @Mock private ListLineConsumer skopeoOutputConsumer; @Mock private ListLineConsumer skopeoErrorConsumer; - @Mock private ListLineConsumer tarOutputConsumer; @Mock private ListLineConsumer tarErrorConsumer; private CdnSupportService service; From 31a5699bfb831264e863c35ff424e877f38604a1 Mon Sep 17 00:00:00 2001 From: David Festal Date: Mon, 21 Jan 2019 17:36:32 +0100 Subject: [PATCH 25/27] Fixes after @garagatyi comments Signed-off-by: David Festal --- .../com/redhat/che/cdn/CdnSupportService.java | 54 ++++++++++--------- .../redhat/che/cdn/CdnSupportServiceTest.java | 21 ++++---- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportService.java b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportService.java index 7f860d7a3..168639d9a 100644 --- a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportService.java +++ b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportService.java @@ -44,19 +44,17 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; -import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; import org.eclipse.che.api.core.ApiException; -import org.eclipse.che.api.core.BadRequestException; 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.core.util.LineConsumer; import org.eclipse.che.api.core.util.ListLineConsumer; import org.eclipse.che.api.core.util.ProcessUtil; +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; import org.eclipse.che.api.workspace.server.wsplugins.PluginMetaRetriever; import org.eclipse.che.api.workspace.server.wsplugins.model.PluginMeta; import org.eclipse.che.api.workspace.shared.Constants; @@ -132,31 +130,12 @@ public CdnSupportService( @GET @Path("paths") @Produces(APPLICATION_JSON) - public String getPaths(@Context HttpServletRequest servletRequest) throws Exception { + public String getPaths() throws Exception { if (editorToPrefetch == null) { throw new NotFoundException("No editor is configured for CDN resource pre-fetching"); } - LOG.debug("Searching the editor plugin entry in the plugin registry"); - - Collection plugins = - pluginMetaRetriever.get( - ImmutableMap.of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, editorToPrefetch)); - if (plugins.size() != 1) { - throw new NotFoundException("Editor '" + editorToPrefetch + "' is unknown"); - } - - PluginMeta editor = plugins.toArray(new PluginMeta[] {})[0]; - if (editor == null) { - throw new NotFoundException("Editor '" + editorToPrefetch + "' is unknown"); - } - - LOG.debug("Retrieving editor URL"); - - String url = editor.getUrl(); - if (url == null) { - throw new BadRequestException(format("URL of editor '%s' is null", editorToPrefetch)); - } + String url = retrieveEditorPluginUrl(); if (!url.equals(editorDefinitionUrl)) { editorDefinitionUrl = url; dockerImage = null; @@ -169,7 +148,7 @@ public String getPaths(@Context HttpServletRequest servletRequest) throws Except } if (dockerImage == null) { - throw new BadRequestException( + throw new ServerException( format( "Plugin container image not found in the plugin descriptor of '%s'", editorToPrefetch)); @@ -201,6 +180,31 @@ public String getPaths(@Context HttpServletRequest servletRequest) throws Except return json.path("Labels").path(LABEL_NAME).asText("[]"); } + private String retrieveEditorPluginUrl() + throws InfrastructureException, NotFoundException, ServerException { + LOG.debug("Searching the editor plugin entry in the plugin registry"); + + Collection plugins = + pluginMetaRetriever.get( + ImmutableMap.of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, editorToPrefetch)); + if (plugins.size() != 1) { + throw new NotFoundException("Editor '" + editorToPrefetch + "' is unknown"); + } + + PluginMeta editor = plugins.toArray(new PluginMeta[] {})[0]; + if (editor == null) { + throw new NotFoundException("Editor '" + editorToPrefetch + "' is unknown"); + } + + LOG.debug("Retrieving editor URL"); + + String url = editor.getUrl(); + if (url == null) { + throw new ServerException(format("URL of editor '%s' is null", editorToPrefetch)); + } + return url; + } + private String readDockerImageName(String editorDefinitionUrl) throws JsonProcessingException, IOException, TimeoutException, InterruptedException, ExecutionException, ApiException { diff --git a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdn/CdnSupportServiceTest.java b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdn/CdnSupportServiceTest.java index cd9e1c6f6..43dcc4eeb 100644 --- a/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdn/CdnSupportServiceTest.java +++ b/plugins/fabric8-cdn-support/src/test/java/com/redhat/che/cdn/CdnSupportServiceTest.java @@ -24,8 +24,6 @@ import java.net.URLStreamHandlerFactory; import java.util.Collections; import java.util.concurrent.TimeUnit; -import javax.servlet.http.HttpServletRequest; -import org.eclipse.che.api.core.BadRequestException; import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.util.ListLineConsumer; @@ -49,7 +47,6 @@ public class CdnSupportServiceTest { @Mock private CdnSupportService.CommandRunner commandRunner; @Mock private PluginMetaRetriever metaRetriever; @Mock private URLConnection urlConnection; - @Mock private HttpServletRequest servletRequest; @Mock private PluginMeta pluginMeta; @Mock private Process tarProcess; @Mock private Process skopeoHelpProcess; @@ -107,7 +104,7 @@ public void throwAtStartIfNoSkopeoBinary() throws Exception { expectedExceptionsMessageRegExp = "No editor is configured for CDN resource pre-fetching") public void throwWhenNoPreferredEditor() throws Exception { service = new CdnSupportService(commandRunner, metaRetriever, null); - service.getPaths(servletRequest); + service.getPaths(); } @Test( @@ -116,7 +113,7 @@ public void throwWhenNoPreferredEditor() throws Exception { public void throwWhenEditorNotFound() throws Exception { doReturn(Collections.emptyList()).when(metaRetriever).get(any()); service = new CdnSupportService(commandRunner, metaRetriever, "unknownEditor"); - service.getPaths(servletRequest); + service.getPaths(); } @Test( @@ -127,11 +124,11 @@ public void throwWhenEditorIsNull() throws Exception { .when(metaRetriever) .get(any()); service = new CdnSupportService(commandRunner, metaRetriever, "unknownEditor"); - service.getPaths(servletRequest); + service.getPaths(); } @Test( - expectedExceptions = {BadRequestException.class}, + expectedExceptions = {ServerException.class}, expectedExceptionsMessageRegExp = "URL of editor 'editor' is null") public void throwWhenEditorPluginUrlIsNull() throws Exception { doReturn(Lists.newArrayList(new PluginMeta[] {pluginMeta})) @@ -141,7 +138,7 @@ public void throwWhenEditorPluginUrlIsNull() throws Exception { service = new CdnSupportService(commandRunner, metaRetriever, EDITOR_REF); try { - service.getPaths(servletRequest); + service.getPaths(); } catch (Exception e) { throw e; } finally { @@ -174,7 +171,7 @@ public void throwWhenSkopeoFailsWithNonZeroCode() throws Exception { service.dockerImage = IMAGE_REF; try { - service.getPaths(servletRequest); + service.getPaths(); } catch (Exception e) { throw e; } finally { @@ -203,7 +200,7 @@ public void reuseExistingImageRefAndReturnLabelWhenSkopeoSucceeds() throws Excep service.editorDefinitionUrl = EDITOR_URL; service.dockerImage = IMAGE_REF; - assertEquals(service.getPaths(servletRequest), "cdnJsonContent"); + assertEquals(service.getPaths(), "cdnJsonContent"); verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); verify(commandRunner, never()) @@ -236,7 +233,7 @@ public void searchForImageRefAndThrowWhenTarFails() throws Exception { service = new CdnSupportService(commandRunner, metaRetriever, EDITOR_REF); try { - assertEquals(service.getPaths(servletRequest), "{}"); + assertEquals(service.getPaths(), "{}"); } catch (Exception e) { throw e; } finally { @@ -274,7 +271,7 @@ public void searchForImageRefAndReturnLabelWhenSkopeoSucceeds() throws Exception setupURLConnectionInputStream("/che-editor-plugin.tar.gz"); service = new CdnSupportService(commandRunner, metaRetriever, EDITOR_REF); - assertEquals(service.getPaths(servletRequest), "cdnJsonContent"); + assertEquals(service.getPaths(), "cdnJsonContent"); verify(metaRetriever).get(of(Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE, EDITOR_REF)); verify(commandRunner, times(2)) From 9123db6a7e676f3ddd42db1ea3c7f00c2e55d89a Mon Sep 17 00:00:00 2001 From: David Festal Date: Tue, 22 Jan 2019 14:35:31 +0100 Subject: [PATCH 26/27] Fix process output management Signed-off-by: David Festal --- .../com/redhat/che/cdn/CdnSupportService.java | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportService.java b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportService.java index 168639d9a..0ec8c8dea 100644 --- a/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportService.java +++ b/plugins/fabric8-cdn-support/src/main/java/com/redhat/che/cdn/CdnSupportService.java @@ -19,7 +19,6 @@ import static java.util.stream.Stream.of; import static java.util.stream.StreamSupport.stream; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; -import static org.eclipse.che.api.core.util.ProcessUtil.process; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonProcessingException; @@ -30,9 +29,11 @@ import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URI; import java.nio.file.FileSystems; import java.nio.file.Files; @@ -73,7 +74,7 @@ public class CdnSupportService extends Service { private static final long SKOPEO_HELP_TIMEOUT_SECONDS = 2; private static final String SKOPEO_INSPECT_ARG = "inspect"; private static final String SKOPEO_IMAGE_PREFIX = "docker://"; - private static final long SKOPEO_INSPECT_TIMEOUT_SECONDS = 5; + private static final long SKOPEO_INSPECT_TIMEOUT_SECONDS = 10; private static final String TAR_BINARY = "tar"; private static final String TAR_ARG = "-xvf"; private static final long TAR_TIMEOUT_SECONDS = 5; @@ -154,6 +155,13 @@ public String getPaths() throws Exception { editorToPrefetch)); } + JsonNode json = inspectDockerImage(); + return json.path("Labels").path(LABEL_NAME).asText("[]"); + } + + private JsonNode inspectDockerImage() + throws IOException, TimeoutException, InterruptedException, ExecutionException, + ServerException { LOG.debug("Running {} on image {}", SKOPEO_BINARY, dockerImage); final ListLineConsumer out = commandRunner.newOutputConsumer(); final ListLineConsumer err = commandRunner.newErrorConsumer(); @@ -174,10 +182,11 @@ public String getPaths() throws Exception { LOG.warn(message); throw new ServerException(message); } - LOG.debug("Result of running skopeo on image {}: {}", dockerImage, out.getText()); + String skopeoOutput = out.getText(); + LOG.debug("Result of running skopeo on image {}: {}", dockerImage, skopeoOutput); - JsonNode json = JSON_PARSER.readTree(out.getText()); - return json.path("Labels").path(LABEL_NAME).asText("[]"); + JsonNode json = JSON_PARSER.readTree(skopeoOutput); + return json; } private String retrieveEditorPluginUrl() @@ -281,10 +290,18 @@ Process runCommand( (LineConsumer consumer) -> { return runAsync( () -> { - if (outputConsumer != null) { - try { - // consume logs until process ends - process(process, outputConsumer); + if (consumer != null) { + InputStream is = + consumer == outputConsumer + ? process.getInputStream() + : process.getErrorStream(); + // consume logs until process ends + try (BufferedReader inputReader = + new BufferedReader(new InputStreamReader(is))) { + String line; + while ((line = inputReader.readLine()) != null) { + consumer.writeLine(line); + } } catch (IOException e) { LOG.error( format( From 25337fa42c42cee9c4fcd3a864e9aea738cda6ad Mon Sep 17 00:00:00 2001 From: David Festal Date: Tue, 22 Jan 2019 19:27:54 +0100 Subject: [PATCH 27/27] Add a space, just in case it would be that... Signed-off-by: David Festal --- dev-scripts/deploy_custom_rh-che.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-scripts/deploy_custom_rh-che.sh b/dev-scripts/deploy_custom_rh-che.sh index 9bcdd7bd5..d57630288 100755 --- a/dev-scripts/deploy_custom_rh-che.sh +++ b/dev-scripts/deploy_custom_rh-che.sh @@ -306,7 +306,7 @@ CHE_CONFIG_YAML=$(yq ".\"data\".\"CHE_KEYCLOAK_REALM\" = \"NULL\" | .\"data\".\"che.jdbc.username\" = \"$RH_CHE_JDBC_USERNAME\" | .\"data\".\"che.jdbc.password\" = \"$RH_CHE_JDBC_PASSWORD\" | .\"data\".\"che.jdbc.url\" = \"$RH_CHE_JDBC_URL\" | - .\"data\".\"CHE_LOG_LEVEL\" = \"INFO\"| + .\"data\".\"CHE_LOG_LEVEL\" = \"INFO\" | .\"data\".\"CHE_LOGS_APPENDERS_IMPL\" = \"plaintext\" " ${RH_CHE_CONFIG}) CHE_CONFIG_YAML=$(echo "$CHE_CONFIG_YAML" | \