From 8db2bbb28da9823a85f4b4ae1ee030390d756697 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 20 Aug 2019 16:38:29 +0200 Subject: [PATCH] PluginInstaller returns now PendingPluginInstallation, to abort the installation before restart --- .../scm/plugin/PendingPluginInstallation.java | 31 +++++++++++++ ...inFailedToCancelInstallationException.java | 7 +++ .../sonia/scm/plugin/PluginInstaller.java | 8 ++-- .../plugin/PendingPluginInstallationTest.java | 46 +++++++++++++++++++ .../sonia/scm/plugin/PluginInstallerTest.java | 11 +++++ 5 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PendingPluginInstallation.java create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginFailedToCancelInstallationException.java create mode 100644 scm-webapp/src/test/java/sonia/scm/plugin/PendingPluginInstallationTest.java diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PendingPluginInstallation.java b/scm-webapp/src/main/java/sonia/scm/plugin/PendingPluginInstallation.java new file mode 100644 index 0000000000..366558f3c9 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PendingPluginInstallation.java @@ -0,0 +1,31 @@ +package sonia.scm.plugin; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +class PendingPluginInstallation { + + private static final Logger LOG = LoggerFactory.getLogger(PendingPluginInstallation.class); + + private final AvailablePlugin plugin; + private final File file; + + PendingPluginInstallation(AvailablePlugin plugin, File file) { + this.plugin = plugin; + this.file = file; + } + + public AvailablePlugin getPlugin() { + return plugin; + } + + void cancel() { + String name = plugin.getDescriptor().getInformation().getName(); + LOG.info("cancel installation of plugin {}", name); + if (!file.delete()) { + throw new PluginFailedToCancelInstallationException("failed to cancel installation of plugin " + name); + } + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginFailedToCancelInstallationException.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginFailedToCancelInstallationException.java new file mode 100644 index 0000000000..2bb6db8125 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginFailedToCancelInstallationException.java @@ -0,0 +1,7 @@ +package sonia.scm.plugin; + +public class PluginFailedToCancelInstallationException extends RuntimeException { + public PluginFailedToCancelInstallationException(String message) { + super(message); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstaller.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstaller.java index 83650a54a1..e512ea3212 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstaller.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstaller.java @@ -1,7 +1,5 @@ package sonia.scm.plugin; -import com.google.common.base.Throwables; -import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; import com.google.common.io.ByteStreams; import com.google.common.io.Files; @@ -28,12 +26,16 @@ class PluginInstaller { this.client = client; } - public void install(AvailablePlugin plugin) { + public PendingPluginInstallation install(AvailablePlugin plugin) { File file = createFile(plugin); try (InputStream input = download(plugin); OutputStream output = new FileOutputStream(file)) { ByteStreams.copy(input, output); verifyChecksum(plugin, file); + + // TODO clean up in case of error + + return new PendingPluginInstallation(plugin, file); } catch (IOException ex) { throw new PluginDownloadException("failed to install plugin", ex); } diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PendingPluginInstallationTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PendingPluginInstallationTest.java new file mode 100644 index 0000000000..0e4ff9cbce --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PendingPluginInstallationTest.java @@ -0,0 +1,46 @@ +package sonia.scm.plugin; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junitpioneer.jupiter.TempDirectory; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.when; + +@ExtendWith({MockitoExtension.class, TempDirectory.class}) +class PendingPluginInstallationTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private AvailablePlugin plugin; + + @Test + void shouldDeleteFileOnCancel(@TempDirectory.TempDir Path directory) throws IOException { + Path file = directory.resolve("file"); + Files.write(file, "42".getBytes()); + + when(plugin.getDescriptor().getInformation().getName()).thenReturn("scm-awesome-plugin"); + + PendingPluginInstallation installation = new PendingPluginInstallation(plugin, file.toFile()); + installation.cancel(); + + assertThat(file).doesNotExist(); + } + + @Test + void shouldThrowExceptionIfCancelFailed(@TempDirectory.TempDir Path directory) { + Path file = directory.resolve("file"); + when(plugin.getDescriptor().getInformation().getName()).thenReturn("scm-awesome-plugin"); + + PendingPluginInstallation installation = new PendingPluginInstallation(plugin, file.toFile()); + assertThrows(PluginFailedToCancelInstallationException.class, installation::cancel); + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallerTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallerTest.java index 8801549130..e209641222 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallerTest.java @@ -51,6 +51,17 @@ class PluginInstallerTest { assertThat(directory.resolve("plugins").resolve("scm-git-plugin.smp")).hasContent("42"); } + @Test + void shouldReturnPendingPluginInstallation() throws IOException { + mockContent("42"); + AvailablePlugin gitPlugin = createGitPlugin(); + + PendingPluginInstallation pending = installer.install(gitPlugin); + + assertThat(pending).isNotNull(); + assertThat(pending.getPlugin()).isSameAs(gitPlugin); + } + private void mockContent(String content) throws IOException { when(client.get("https://download.hitchhiker.com").request().contentAsStream()) .thenReturn(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));