Add cancel method to remove install and uninstall files

This commit is contained in:
Rene Pfeuffer
2019-09-26 17:50:54 +02:00
parent ac4eca7520
commit 3145b751c6
4 changed files with 78 additions and 17 deletions

View File

@@ -93,4 +93,6 @@ public interface PluginManager {
* Install all pending plugins and restart the scm context. * Install all pending plugins and restart the scm context.
*/ */
void executePendingAndRestart(); void executePendingAndRestart();
void cancelInstallations();
} }

View File

@@ -44,8 +44,8 @@ import sonia.scm.lifecycle.RestartEvent;
import sonia.scm.version.Version; import sonia.scm.version.Version;
import javax.inject.Inject; import javax.inject.Inject;
import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -72,7 +72,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 Collection<PendingPluginInstallation> pendingQueue = new ArrayList<>(); private final Collection<PendingPluginInstallation> pendingInstallQueue = new ArrayList<>();
private final Collection<PendingPluginUninstallation> pendingUninstallQueue = new ArrayList<>();
private final PluginDependencyTracker dependencyTracker = new PluginDependencyTracker(); private final PluginDependencyTracker dependencyTracker = new PluginDependencyTracker();
@Inject @Inject
@@ -106,7 +107,7 @@ public class DefaultPluginManager implements PluginManager {
} }
private Optional<AvailablePlugin> getPending(String name) { private Optional<AvailablePlugin> getPending(String name) {
return pendingQueue return pendingInstallQueue
.stream() .stream()
.map(PendingPluginInstallation::getPlugin) .map(PendingPluginInstallation::getPlugin)
.filter(filterByName(name)) .filter(filterByName(name))
@@ -179,7 +180,7 @@ public class DefaultPluginManager implements PluginManager {
if (restartAfterInstallation) { if (restartAfterInstallation) {
restart("plugin installation"); restart("plugin installation");
} else { } else {
pendingQueue.addAll(pendingInstallations); pendingInstallQueue.addAll(pendingInstallations);
updateMayUninstallFlag(); updateMayUninstallFlag();
} }
} }
@@ -192,15 +193,8 @@ public class DefaultPluginManager implements PluginManager {
.orElseThrow(() -> NotFoundException.notFound(entity(InstalledPlugin.class, name))); .orElseThrow(() -> NotFoundException.notFound(entity(InstalledPlugin.class, name)));
doThrow().violation("plugin is a core plugin and cannot be uninstalled").when(installed.isCore()); doThrow().violation("plugin is a core plugin and cannot be uninstalled").when(installed.isCore());
dependencyTracker.removeInstalled(installed.getDescriptor());
try { markForUninstall(installed);
createMarkerFile(installed, InstalledPlugin.UNINSTALL_MARKER_FILENAME);
installed.setMarkedForUninstall(true);
} catch (RuntimeException e) {
dependencyTracker.addInstalled(installed.getDescriptor());
throw e;
}
if (restartAfterInstallation) { if (restartAfterInstallation) {
restart("plugin installation"); restart("plugin installation");
@@ -220,18 +214,22 @@ public class DefaultPluginManager implements PluginManager {
&& dependencyTracker.mayUninstall(p.getDescriptor().getInformation().getName()); && dependencyTracker.mayUninstall(p.getDescriptor().getInformation().getName());
} }
private void createMarkerFile(InstalledPlugin plugin, String markerFile) { private void markForUninstall(InstalledPlugin plugin) {
dependencyTracker.removeInstalled(plugin.getDescriptor());
try { try {
Files.createFile(plugin.getDirectory().resolve(markerFile)); Path file = Files.createFile(plugin.getDirectory().resolve(InstalledPlugin.UNINSTALL_MARKER_FILENAME));
} catch (IOException e) { pendingUninstallQueue.add(new PendingPluginUninstallation(plugin, file));
throw new PluginException("could not mark plugin " + plugin.getId() + " in path " + plugin.getDirectory() + "as " + markerFile, e); plugin.setMarkedForUninstall(true);
} catch (Exception e) {
dependencyTracker.addInstalled(plugin.getDescriptor());
throw new PluginException("could not mark plugin " + plugin.getId() + " in path " + plugin.getDirectory() + "as " + InstalledPlugin.UNINSTALL_MARKER_FILENAME, e);
} }
} }
@Override @Override
public void executePendingAndRestart() { public void executePendingAndRestart() {
PluginPermissions.manage().check(); PluginPermissions.manage().check();
if (!pendingQueue.isEmpty() || getInstalled().stream().anyMatch(InstalledPlugin::isMarkedForUninstall)) { if (!pendingInstallQueue.isEmpty() || getInstalled().stream().anyMatch(InstalledPlugin::isMarkedForUninstall)) {
restart("execute pending plugin changes"); restart("execute pending plugin changes");
} }
} }
@@ -274,4 +272,10 @@ public class DefaultPluginManager implements PluginManager {
private boolean isUpdatable(String name) { private boolean isUpdatable(String name) {
return getAvailable(name).isPresent() && !getPending(name).isPresent(); return getAvailable(name).isPresent() && !getPending(name).isPresent();
} }
@Override
public void cancelInstallations() {
pendingUninstallQueue.forEach(PendingPluginUninstallation::cancel);
pendingInstallQueue.forEach(PendingPluginInstallation::cancel);
}
} }

View File

@@ -0,0 +1,31 @@
package sonia.scm.plugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
class PendingPluginUninstallation {
private static final Logger LOG = LoggerFactory.getLogger(PendingPluginUninstallation.class);
private final InstalledPlugin plugin;
private final Path uninstallFile;
PendingPluginUninstallation(InstalledPlugin plugin, Path uninstallFile) {
this.plugin = plugin;
this.uninstallFile = uninstallFile;
}
void cancel() {
String name = plugin.getDescriptor().getInformation().getName();
LOG.info("cancel uninstallation of plugin {}", name);
try {
Files.delete(uninstallFile);
} catch (IOException ex) {
throw new PluginFailedToCancelInstallationException("failed to cancel uninstallation of plugin " + name, ex);
}
}
}

View File

@@ -20,6 +20,8 @@ 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.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@@ -445,6 +447,28 @@ class DefaultPluginManagerTest {
verify(eventBus).post(any(RestartEvent.class)); verify(eventBus).post(any(RestartEvent.class));
} }
@Test
void shouldUndoPendingInstallations(@TempDirectory.TempDir Path temp) throws IOException {
InstalledPlugin mailPlugin = createInstalled("scm-ssh-plugin");
Path mailPluginPath = temp.resolve("scm-mail-plugin");
Files.createDirectories(mailPluginPath);
when(mailPlugin.getDirectory()).thenReturn(mailPluginPath);
when(loader.getInstalledPlugins()).thenReturn(singletonList(mailPlugin));
AvailablePlugin git = createAvailable("scm-git-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(git));
PendingPluginInstallation gitPendingPluginInformation = mock(PendingPluginInstallation.class);
when(installer.install(git)).thenReturn(gitPendingPluginInformation);
manager.install("scm-git-plugin", false);
manager.uninstall("scm-ssh-plugin", false);
manager.cancelInstallations();
assertThat(mailPluginPath.resolve("uninstall")).doesNotExist();
verify(gitPendingPluginInformation).cancel();
}
} }
@Nested @Nested