Add uninstall method to plugin manager

This commit is contained in:
Rene Pfeuffer
2019-09-16 13:22:26 +02:00
parent 14451897b2
commit 88ed3ff023
3 changed files with 88 additions and 7 deletions

View File

@@ -81,6 +81,14 @@ public interface PluginManager {
*/ */
void install(String name, boolean restartAfterInstallation); void install(String name, boolean restartAfterInstallation);
/**
* Marks the plugin with the given name for uninstall.
*
* @param name plugin name
* @param restartAfterInstallation restart context after plugin has been marked to really uninstall the plugin
*/
void uninstall(String name, boolean restartAfterInstallation);
/** /**
* Install all pending plugins and restart the scm context. * Install all pending plugins and restart the scm context.
*/ */

View File

@@ -33,8 +33,7 @@
package sonia.scm.plugin; package sonia.scm.plugin;
//~--- non-JDK imports -------------------------------------------------------- import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -43,9 +42,11 @@ import sonia.scm.NotFoundException;
import sonia.scm.event.ScmEventBus; import sonia.scm.event.ScmEventBus;
import sonia.scm.lifecycle.RestartEvent; import sonia.scm.lifecycle.RestartEvent;
//~--- JDK imports ------------------------------------------------------------
import javax.inject.Inject; import javax.inject.Inject;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@@ -54,6 +55,8 @@ import java.util.stream.Collectors;
import static sonia.scm.ContextEntry.ContextBuilder.entity; import static sonia.scm.ContextEntry.ContextBuilder.entity;
//~--- JDK imports ------------------------------------------------------------
/** /**
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
@@ -67,7 +70,8 @@ public class DefaultPluginManager implements PluginManager {
private final PluginLoader loader; private final PluginLoader loader;
private final PluginCenter center; private final PluginCenter center;
private final PluginInstaller installer; private final PluginInstaller installer;
private final List<PendingPluginInstallation> pendingQueue = new ArrayList<>(); private final Collection<PendingPluginInstallation> pendingQueue = new ArrayList<>();
private final PluginDependencyTracker dependencyTracker = new PluginDependencyTracker();
@Inject @Inject
public DefaultPluginManager(ScmEventBus eventBus, PluginLoader loader, PluginCenter center, PluginInstaller installer) { public DefaultPluginManager(ScmEventBus eventBus, PluginLoader loader, PluginCenter center, PluginInstaller installer) {
@@ -75,6 +79,16 @@ public class DefaultPluginManager implements PluginManager {
this.loader = loader; this.loader = loader;
this.center = center; this.center = center;
this.installer = installer; this.installer = installer;
this.computeRequiredPlugins();
}
@VisibleForTesting
synchronized void computeRequiredPlugins() {
loader.getInstalledPlugins()
.stream()
.map(InstalledPlugin::getDescriptor)
.forEach(dependencyTracker::addInstalled);
} }
@Override @Override
@@ -153,6 +167,21 @@ public class DefaultPluginManager implements PluginManager {
} }
} }
@Override
public void uninstall(String name, boolean restartAfterInstallation) {
PluginPermissions.manage().check();
InstalledPlugin installed = getInstalled(name)
.orElseThrow(() -> NotFoundException.notFound(entity(InstalledPlugin.class, name)));
dependencyTracker.removeInstalled(installed.getDescriptor());
try {
Files.createFile(installed.getDirectory().resolve("uninstall"));
} catch (IOException e) {
throw new PluginException("could not mark plugin " + name + " in path " + installed.getDirectory() + " for uninstall", e);
}
}
@Override @Override
public void installPendingAndRestart() { public void installPendingAndRestart() {
PluginPermissions.manage().check(); PluginPermissions.manage().check();

View File

@@ -10,26 +10,37 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers; import org.junitpioneer.jupiter.TempDirectory;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.NotFoundException; import sonia.scm.NotFoundException;
import sonia.scm.ScmConstraintViolationException;
import sonia.scm.event.ScmEventBus; import sonia.scm.event.ScmEventBus;
import sonia.scm.lifecycle.RestartEvent; import sonia.scm.lifecycle.RestartEvent;
import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.in;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static sonia.scm.plugin.PluginTestHelper.createAvailable; import static sonia.scm.plugin.PluginTestHelper.createAvailable;
import static sonia.scm.plugin.PluginTestHelper.createInstalled; import static sonia.scm.plugin.PluginTestHelper.createInstalled;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@ExtendWith(TempDirectory.class)
class DefaultPluginManagerTest { class DefaultPluginManagerTest {
@Mock @Mock
@@ -305,6 +316,34 @@ class DefaultPluginManagerTest {
assertThat(available.get(0).isPending()).isTrue(); assertThat(available.get(0).isPending()).isTrue();
} }
@Test
void shouldThrowExceptionWhenUninstallingUnknownPlugin() {
assertThrows(NotFoundException.class, () -> manager.uninstall("no-such-plugin", false));
}
@Test
void shouldUseDependencyTrackerForUninstall() {
InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin");
InstalledPlugin reviewPlugin = createInstalled("scm-review-plugin");
when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin"));
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(mailPlugin, reviewPlugin));
manager.computeRequiredPlugins();
assertThrows(ScmConstraintViolationException.class, () -> manager.uninstall("scm-mail-plugin", false));
}
@Test
void shouldCreateUninstallFile(@TempDirectory.TempDir Path temp) {
InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin");
when(mailPlugin.getDirectory()).thenReturn(temp);
when(loader.getInstalledPlugins()).thenReturn(singletonList(mailPlugin));
manager.uninstall("scm-mail-plugin", false);
assertThat(temp.resolve("uninstall")).exists();
}
} }
@Nested @Nested
@@ -350,6 +389,11 @@ class DefaultPluginManagerTest {
assertThrows(AuthorizationException.class, () -> manager.install("test", false)); assertThrows(AuthorizationException.class, () -> manager.install("test", false));
} }
@Test
void shouldThrowAuthorizationExceptionsForUninstallMethod() {
assertThrows(AuthorizationException.class, () -> manager.uninstall("test", false));
}
@Test @Test
void shouldThrowAuthorizationExceptionsForInstallPendingAndRestart() { void shouldThrowAuthorizationExceptionsForInstallPendingAndRestart() {
assertThrows(AuthorizationException.class, () -> manager.installPendingAndRestart()); assertThrows(AuthorizationException.class, () -> manager.installPendingAndRestart());