From de7d18e02632e87a70a75574fd5adf46c4783289 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 21 Aug 2019 08:42:57 +0200 Subject: [PATCH] cancel all pending installations, if a dependency failed to install --- .../scm/plugin/DefaultPluginManager.java | 47 ++++++++++++++----- .../scm/plugin/DefaultPluginManagerTest.java | 24 ++++++++++ 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index 77a13686c8..d670a6609c 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -43,6 +43,7 @@ import sonia.scm.NotFoundException; //~--- JDK imports ------------------------------------------------------------ import javax.inject.Inject; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; @@ -99,7 +100,7 @@ public class DefaultPluginManager implements PluginManager { } private Predicate filterByName(String name) { - return (plugin) -> name.equals(plugin.getDescriptor().getInformation().getName()); + return plugin -> name.equals(plugin.getDescriptor().getInformation().getName()); } private boolean isNotInstalled(AvailablePlugin availablePlugin) { @@ -108,18 +109,42 @@ public class DefaultPluginManager implements PluginManager { @Override public void install(String name) { - if (getInstalled(name).isPresent()){ - LOG.info("plugin {} is already installed, skipping installation", name); - return; - } - AvailablePlugin plugin = getAvailable(name).orElseThrow(() -> NotFoundException.notFound(entity(AvailablePlugin.class, name))); - Set dependencies = plugin.getDescriptor().getDependencies(); - if (dependencies != null) { - for (String dependency: dependencies){ - install(dependency); + List plugins = collectPluginsToInstall(name); + List pendingInstallations = new ArrayList<>(); + for (AvailablePlugin plugin : plugins) { + try { + PendingPluginInstallation pending = installer.install(plugin); + pendingInstallations.add(pending); + } catch (PluginInstallException ex) { + cancelPending(pendingInstallations); + throw ex; } } + } - installer.install(plugin); + private void cancelPending(List pendingInstallations) { + pendingInstallations.forEach(PendingPluginInstallation::cancel); + } + + private List collectPluginsToInstall(String name) { + List plugins = new ArrayList<>(); + collectPluginsToInstall(plugins, name); + return plugins; + } + private void collectPluginsToInstall(List plugins, String name) { + if (!getInstalled(name).isPresent()) { + AvailablePlugin plugin = getAvailable(name).orElseThrow(() -> NotFoundException.notFound(entity(AvailablePlugin.class, name))); + + Set dependencies = plugin.getDescriptor().getDependencies(); + if (dependencies != null) { + for (String dependency: dependencies){ + collectPluginsToInstall(plugins, dependency); + } + } + + plugins.add(plugin); + } else { + LOG.info("plugin {} is already installed, skipping installation", name); + } } } diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java index c23f9394c3..2ab111689a 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) @@ -153,6 +154,29 @@ class DefaultPluginManagerTest { verify(installer).install(review); } + @Test + void shouldRollbackOnFailedInstallation() { + AvailablePlugin review = createAvailable("scm-review-plugin"); + when(review.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("scm-mail-plugin")); + AvailablePlugin mail = createAvailable("scm-mail-plugin"); + when(mail.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("scm-notification-plugin")); + AvailablePlugin notification = createAvailable("scm-notification-plugin"); + when(center.getAvailable()).thenReturn(ImmutableSet.of(review, mail, notification)); + + PendingPluginInstallation pendingNotification = mock(PendingPluginInstallation.class); + doReturn(pendingNotification).when(installer).install(notification); + + PendingPluginInstallation pendingMail = mock(PendingPluginInstallation.class); + doReturn(pendingMail).when(installer).install(mail); + + doThrow(new PluginChecksumMismatchException("checksum does not match")).when(installer).install(review); + + assertThrows(PluginInstallException.class, () -> manager.install("scm-review-plugin")); + + verify(pendingNotification).cancel(); + verify(pendingMail).cancel(); + } + private AvailablePlugin createAvailable(String name) { PluginInformation information = new PluginInformation(); information.setName(name);