]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10033 handle failed install and uninstall on restart
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Mon, 30 Oct 2017 13:48:47 +0000 (14:48 +0100)
committerDuarte Meneses <duarte.meneses@sonarsource.com>
Mon, 30 Oct 2017 15:23:39 +0000 (16:23 +0100)
server/sonar-server/src/main/java/org/sonar/server/edition/CommitPendingEditionOnStartup.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/edition/FinalizeEditionChange.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java
server/sonar-server/src/test/java/org/sonar/server/edition/CommitPendingEditionOnStartupTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/edition/FinalizeEditionChangeTest.java [new file with mode: 0644]

diff --git a/server/sonar-server/src/main/java/org/sonar/server/edition/CommitPendingEditionOnStartup.java b/server/sonar-server/src/main/java/org/sonar/server/edition/CommitPendingEditionOnStartup.java
deleted file mode 100644 (file)
index 1c8c404..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.edition;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.sonar.api.Startable;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.server.license.LicenseCommit;
-
-public class CommitPendingEditionOnStartup implements Startable {
-  private static final Logger LOG = Loggers.get(CommitPendingEditionOnStartup.class);
-
-  private final MutableEditionManagementState editionManagementState;
-  @CheckForNull
-  private final LicenseCommit licenseCommit;
-
-  /**
-   * Used by Pico when license-manager is not installed and therefore no implementation of {@link LicenseCommit} is
-   * is available.
-   */
-  public CommitPendingEditionOnStartup(MutableEditionManagementState editionManagementState) {
-    this(editionManagementState, null);
-  }
-
-  public CommitPendingEditionOnStartup(MutableEditionManagementState editionManagementState, @Nullable LicenseCommit licenseCommit) {
-    this.editionManagementState = editionManagementState;
-    this.licenseCommit = licenseCommit;
-  }
-
-  @Override
-  public void start() {
-    EditionManagementState.PendingStatus status = editionManagementState.getPendingInstallationStatus();
-    switch (status) {
-      case NONE:
-        editionManagementState.clearInstallErrorMessage();
-        return;
-      case MANUAL_IN_PROGRESS:
-      case AUTOMATIC_READY:
-        finalizeInstall(status);
-        break;
-      case AUTOMATIC_IN_PROGRESS:
-        editionManagementState.installFailed("SonarQube was restarted before asynchronous installation of edition completed");
-        break;
-      case UNINSTALL_IN_PROGRESS:
-        failIfLicenseCommitIsPresent();
-        editionManagementState.finalizeInstallation(null);
-        break;
-      default:
-        throw new IllegalStateException("Unsupported status " + status);
-    }
-  }
-
-  private void failIfLicenseCommitIsPresent() {
-    if (licenseCommit != null) {
-      throw new IllegalStateException("License Manager plugin is still present after uninstallation of the edition. Please remove it.");
-    }
-  }
-
-  private void finalizeInstall(EditionManagementState.PendingStatus status) {
-    // license manager is not installed, can't finalize
-    if (licenseCommit == null) {
-      LOG.info("No LicenseCommit instance is not available, can not finalize installation");
-      return;
-    }
-
-    String newLicense = editionManagementState.getPendingLicense()
-      .orElseThrow(() -> new IllegalStateException(String.format("When state is %s, a license should be available in staging", status)));
-    try {
-      licenseCommit.update(newLicense);
-      editionManagementState.finalizeInstallation(null);
-    } catch (IllegalArgumentException e) {
-      String errorMessage = "Invalid staged license could not be commit on startup. Please input a new license.";
-      LOG.warn(errorMessage, e);
-      editionManagementState.finalizeInstallation(errorMessage);
-    }
-  }
-
-  @Override
-  public void stop() {
-    // nothing to do
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/edition/FinalizeEditionChange.java b/server/sonar-server/src/main/java/org/sonar/server/edition/FinalizeEditionChange.java
new file mode 100644 (file)
index 0000000..3f73a32
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.edition;
+
+import java.util.Optional;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.Startable;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.server.edition.EditionManagementState.PendingStatus;
+import org.sonar.server.license.LicenseCommit;
+
+public class FinalizeEditionChange implements Startable {
+  private static final Logger LOG = Loggers.get(FinalizeEditionChange.class);
+
+  private final MutableEditionManagementState editionManagementState;
+  @CheckForNull
+  private final LicenseCommit licenseCommit;
+
+  /**
+   * Used by Pico when license-manager is not installed and therefore no implementation of {@link LicenseCommit} is
+   * is available.
+   */
+  public FinalizeEditionChange(MutableEditionManagementState editionManagementState) {
+    this(editionManagementState, null);
+  }
+
+  public FinalizeEditionChange(MutableEditionManagementState editionManagementState, @Nullable LicenseCommit licenseCommit) {
+    this.editionManagementState = editionManagementState;
+    this.licenseCommit = licenseCommit;
+  }
+
+  @Override
+  public void start() {
+    EditionManagementState.PendingStatus status = editionManagementState.getPendingInstallationStatus();
+    switch (status) {
+      case NONE:
+        editionManagementState.clearInstallErrorMessage();
+        return;
+      case MANUAL_IN_PROGRESS:
+      case AUTOMATIC_READY:
+        finalizeInstall();
+        break;
+      case AUTOMATIC_IN_PROGRESS:
+        editionManagementState.installFailed("SonarQube was restarted before asynchronous installation of edition completed");
+        break;
+      case UNINSTALL_IN_PROGRESS:
+        failIfLicenseCommitIsPresent();
+        editionManagementState.finalizeInstallation(null);
+        break;
+      default:
+        throw new IllegalStateException("Unsupported status " + status);
+    }
+  }
+
+  private void failIfLicenseCommitIsPresent() {
+    if (licenseCommit != null) {
+      throw new IllegalStateException("License Manager plugin is still present after uninstallation of the edition. Please remove it.");
+    }
+  }
+
+  private void finalizeInstall() {
+    String errorMessage = null;
+
+    try {
+      if (licenseCommit == null) {
+        errorMessage = "Edition installation didn't complete. Some plugins were not installed.";
+        LOG.warn(errorMessage);
+        return;
+      }
+
+      Optional<String> newLicense = editionManagementState.getPendingLicense();
+      if (!newLicense.isPresent()) {
+        errorMessage = "Edition installation didn't complete. License was not found.";
+        LOG.warn(errorMessage);
+        return;
+      }
+
+      try {
+        licenseCommit.update(newLicense.get());
+      } catch (IllegalArgumentException e) {
+        errorMessage = "Edition installation didn't complete. License is not valid. Please set a new license.";
+        LOG.warn(errorMessage, e);
+      }
+    } finally {
+      editionManagementState.finalizeInstallation(errorMessage);
+    }
+  }
+
+  @Override
+  public void stop() {
+    EditionManagementState.PendingStatus status = editionManagementState.getPendingInstallationStatus();
+    if (status == PendingStatus.UNINSTALL_IN_PROGRESS) {
+      if (licenseCommit != null) {
+        LOG.debug("Removing license");
+        licenseCommit.delete();
+      } else {
+        LOG.warn("License Manager plugin not found - cannot remove the license");
+      }
+    }
+  }
+}
index 62c9dd6737c4f451dd2b1d02694136868d071f11..9a92243526e60e5c23016010d3e073b51a841fd1 100644 (file)
@@ -20,7 +20,7 @@
 package org.sonar.server.platform.platformlevel;
 
 import org.sonar.server.app.ProcessCommandWrapper;
-import org.sonar.server.edition.CommitPendingEditionOnStartup;
+import org.sonar.server.edition.FinalizeEditionChange;
 import org.sonar.server.es.IndexerStartupTask;
 import org.sonar.server.organization.DefaultOrganizationEnforcer;
 import org.sonar.server.platform.ServerLifecycleNotifier;
@@ -60,7 +60,7 @@ public class PlatformLevelStartup extends PlatformLevel {
       RegisterMetrics.class,
       RegisterQualityGates.class,
       RegisterRules.class,
-      CommitPendingEditionOnStartup.class);
+      FinalizeEditionChange.class);
     add(BuiltInQProfileLoader.class);
     addIfStartupLeader(
       BuiltInQualityProfilesUpdateListener.class,
diff --git a/server/sonar-server/src/test/java/org/sonar/server/edition/CommitPendingEditionOnStartupTest.java b/server/sonar-server/src/test/java/org/sonar/server/edition/CommitPendingEditionOnStartupTest.java
deleted file mode 100644 (file)
index e1e1e91..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.edition;
-
-import java.util.Optional;
-import org.apache.commons.lang.RandomStringUtils;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.api.utils.log.LoggerLevel;
-import org.sonar.server.license.LicenseCommit;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-import static org.sonar.server.edition.EditionManagementState.PendingStatus.AUTOMATIC_IN_PROGRESS;
-import static org.sonar.server.edition.EditionManagementState.PendingStatus.AUTOMATIC_READY;
-import static org.sonar.server.edition.EditionManagementState.PendingStatus.MANUAL_IN_PROGRESS;
-import static org.sonar.server.edition.EditionManagementState.PendingStatus.NONE;
-
-public class CommitPendingEditionOnStartupTest {
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-  @Rule
-  public LogTester logTester = new LogTester();
-
-  private MutableEditionManagementState editionManagementState = mock(MutableEditionManagementState.class);
-  private LicenseCommit licenseCommit = mock(LicenseCommit.class);
-  private CommitPendingEditionOnStartup underTest = new CommitPendingEditionOnStartup(editionManagementState);
-  private CommitPendingEditionOnStartup underTestWithLicenseCommit = new CommitPendingEditionOnStartup(editionManagementState, licenseCommit);
-
-  @Test
-  public void start_clears_error_message_when_status_is_NONE_without_LicenseCommit() {
-    when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE);
-
-    underTest.start();
-
-    verify(editionManagementState).getPendingInstallationStatus();
-    verify(editionManagementState).clearInstallErrorMessage();
-    verifyNoMoreInteractions(editionManagementState);
-  }
-
-  @Test
-  public void start_clears_error_message_when_status_is_NONE_with_LicenseCommit() {
-    when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE);
-
-    underTestWithLicenseCommit.start();
-
-    verify(editionManagementState).getPendingInstallationStatus();
-    verify(editionManagementState).clearInstallErrorMessage();
-    verifyNoMoreInteractions(editionManagementState);
-    verifyZeroInteractions(licenseCommit);
-  }
-
-  @Test
-  public void start_has_no_effect_when_status_is_AUTOMATIC_READY_and_no_LicenseCommit_is_available_but_logs_at_debug_level() {
-    when(editionManagementState.getPendingInstallationStatus()).thenReturn(AUTOMATIC_READY);
-
-    underTest.start();
-
-    verify(editionManagementState).getPendingInstallationStatus();
-    verifyNoMoreInteractions(editionManagementState);
-    verifyZeroInteractions(licenseCommit);
-    assertThat(logTester.logs()).hasSize(1);
-    assertThat(logTester.logs(LoggerLevel.INFO))
-      .containsOnly("No LicenseCommit instance is not available, can not finalize installation");
-  }
-
-  @Test
-  public void start_commit_license_and_finalizeInstallation_in_editionManagementState_when_status_is_AUTOMATIC_READY_and_LicenseCommit_is_available() {
-    when(editionManagementState.getPendingInstallationStatus()).thenReturn(AUTOMATIC_READY);
-    String license = RandomStringUtils.randomAlphanumeric(20);
-    when(editionManagementState.getPendingLicense()).thenReturn(Optional.of(license));
-
-    underTestWithLicenseCommit.start();
-
-    verify(editionManagementState).getPendingInstallationStatus();
-    verify(editionManagementState).getPendingLicense();
-    verify(editionManagementState).finalizeInstallation(null);
-    verifyNoMoreInteractions(editionManagementState);
-    verify(licenseCommit).update(license);
-    verifyNoMoreInteractions(licenseCommit);
-  }
-
-  @Test
-  public void start_commit_license_and_finalizeInstallation_with_error_in_editionManagementState_when_status_is_AUTOMATIC_READY_and_license_is_invalid() {
-    when(editionManagementState.getPendingInstallationStatus()).thenReturn(AUTOMATIC_READY);
-    String license = RandomStringUtils.randomAlphanumeric(20);
-    when(editionManagementState.getPendingLicense()).thenReturn(Optional.of(license));
-    doThrow(new IllegalArgumentException("Faking an IAE because of an invalid license"))
-      .when(licenseCommit)
-      .update(license);
-
-    underTestWithLicenseCommit.start();
-
-    verify(editionManagementState).getPendingInstallationStatus();
-    verify(editionManagementState).getPendingLicense();
-    verify(editionManagementState).finalizeInstallation("Invalid staged license could not be commit on startup. Please input a new license.");
-    verifyNoMoreInteractions(editionManagementState);
-    verify(licenseCommit).update(license);
-    verifyNoMoreInteractions(licenseCommit);
-    assertThat(logTester.logs()).hasSize(1);
-    assertThat(logTester.logs(LoggerLevel.WARN))
-      .containsOnly("Invalid staged license could not be commit on startup. Please input a new license.");
-  }
-
-  @Test
-  public void starts_has_no_effect_when_status_is_MANUAL_IN_PROGRESS_and_no_LicenseCommit_is_available_but_logs_at_debug_level() {
-    when(editionManagementState.getPendingInstallationStatus()).thenReturn(MANUAL_IN_PROGRESS);
-
-    underTest.start();
-
-    verify(editionManagementState).getPendingInstallationStatus();
-    verifyNoMoreInteractions(editionManagementState);
-    verifyZeroInteractions(licenseCommit);
-    assertThat(logTester.logs()).hasSize(1);
-    assertThat(logTester.logs(LoggerLevel.INFO))
-      .containsOnly("No LicenseCommit instance is not available, can not finalize installation");
-  }
-
-  @Test
-  public void start_commit_license_and_finalizeInstallation_in_editionManagementState_when_status_is_MANUAL_IN_PROGRESS_and_LicenseCommit_is_available() {
-    when(editionManagementState.getPendingInstallationStatus()).thenReturn(MANUAL_IN_PROGRESS);
-    String license = RandomStringUtils.randomAlphanumeric(20);
-    when(editionManagementState.getPendingLicense()).thenReturn(Optional.of(license));
-
-    underTestWithLicenseCommit.start();
-
-    verify(editionManagementState).getPendingInstallationStatus();
-    verify(editionManagementState).getPendingLicense();
-    verify(editionManagementState).finalizeInstallation(null);
-    verifyNoMoreInteractions(editionManagementState);
-    verify(licenseCommit).update(license);
-    verifyNoMoreInteractions(licenseCommit);
-  }
-
-  @Test
-  public void start_commit_license_and_finalizeInstallation_with_error_in_editionManagementState_when_status_is_MANUAL_IN_PROGRESS_and_license_is_invalid() {
-    when(editionManagementState.getPendingInstallationStatus()).thenReturn(MANUAL_IN_PROGRESS);
-    String license = RandomStringUtils.randomAlphanumeric(20);
-    when(editionManagementState.getPendingLicense()).thenReturn(Optional.of(license));
-    doThrow(new IllegalArgumentException("Faking an IAE because of an invalid license"))
-      .when(licenseCommit)
-      .update(license);
-
-    underTestWithLicenseCommit.start();
-
-    verify(editionManagementState).getPendingInstallationStatus();
-    verify(editionManagementState).getPendingLicense();
-    verify(editionManagementState).finalizeInstallation("Invalid staged license could not be commit on startup. Please input a new license.");
-    verifyNoMoreInteractions(editionManagementState);
-    verify(licenseCommit).update(license);
-    verifyNoMoreInteractions(licenseCommit);
-    assertThat(logTester.logs()).hasSize(1);
-    assertThat(logTester.logs(LoggerLevel.WARN))
-      .containsOnly("Invalid staged license could not be commit on startup. Please input a new license.");
-  }
-
-  @Test
-  public void starts_put_editionManagement_set_in_automaticInstallError_when_status_is_AUTOMATIC_PROGRESS_and_no_LicenseCommit_is_available() {
-    when(editionManagementState.getPendingInstallationStatus()).thenReturn(AUTOMATIC_IN_PROGRESS);
-
-    underTest.start();
-
-    verify(editionManagementState).getPendingInstallationStatus();
-    verify(editionManagementState).installFailed("SonarQube was restarted before asynchronous installation of edition completed");
-    verifyNoMoreInteractions(editionManagementState);
-    verifyZeroInteractions(licenseCommit);
-  }
-
-  @Test
-  public void starts_put_editionManagement_set_in_automaticInstallError_when_status_is_AUTOMATIC_PROGRESS_and_LicenseCommit_is_available() {
-    when(editionManagementState.getPendingInstallationStatus()).thenReturn(AUTOMATIC_IN_PROGRESS);
-
-    underTestWithLicenseCommit.start();
-
-    verify(editionManagementState).getPendingInstallationStatus();
-    verify(editionManagementState).installFailed("SonarQube was restarted before asynchronous installation of edition completed");
-    verifyNoMoreInteractions(editionManagementState);
-    verifyZeroInteractions(licenseCommit);
-  }
-
-  @Test
-  public void should_commit_uninstall() {
-    when(editionManagementState.getPendingInstallationStatus()).thenReturn(EditionManagementState.PendingStatus.UNINSTALL_IN_PROGRESS);
-
-    underTest.start();
-
-    verify(editionManagementState).getPendingInstallationStatus();
-    verify(editionManagementState).finalizeInstallation(null);
-    verifyNoMoreInteractions(editionManagementState);
-  }
-
-  @Test
-  public void should_fail_uninstall_if_license_commit_is_present() {
-    when(editionManagementState.getPendingInstallationStatus()).thenReturn(EditionManagementState.PendingStatus.UNINSTALL_IN_PROGRESS);
-
-    expectedException.expect(IllegalStateException.class);
-    expectedException.expectMessage("License Manager plugin is still present");
-
-    underTestWithLicenseCommit.start();
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/edition/FinalizeEditionChangeTest.java b/server/sonar-server/src/test/java/org/sonar/server/edition/FinalizeEditionChangeTest.java
new file mode 100644 (file)
index 0000000..43053ab
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.edition;
+
+import java.util.Optional;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.server.license.LicenseCommit;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.edition.EditionManagementState.PendingStatus.AUTOMATIC_IN_PROGRESS;
+import static org.sonar.server.edition.EditionManagementState.PendingStatus.AUTOMATIC_READY;
+import static org.sonar.server.edition.EditionManagementState.PendingStatus.MANUAL_IN_PROGRESS;
+import static org.sonar.server.edition.EditionManagementState.PendingStatus.NONE;
+import static org.sonar.server.edition.EditionManagementState.PendingStatus.UNINSTALL_IN_PROGRESS;
+
+public class FinalizeEditionChangeTest {
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+  @Rule
+  public LogTester logTester = new LogTester();
+
+  private MutableEditionManagementState editionManagementState = mock(MutableEditionManagementState.class);
+  private LicenseCommit licenseCommit = mock(LicenseCommit.class);
+  private FinalizeEditionChange underTest = new FinalizeEditionChange(editionManagementState);
+  private FinalizeEditionChange underTestWithLicenseCommit = new FinalizeEditionChange(editionManagementState, licenseCommit);
+
+  @Test
+  public void start_clears_error_message_when_status_is_NONE_without_LicenseCommit() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE);
+
+    underTest.start();
+
+    verify(editionManagementState).getPendingInstallationStatus();
+    verify(editionManagementState).clearInstallErrorMessage();
+    verifyNoMoreInteractions(editionManagementState);
+  }
+
+  @Test
+  public void start_clears_error_message_when_status_is_NONE_with_LicenseCommit() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE);
+
+    underTestWithLicenseCommit.start();
+
+    verify(editionManagementState).getPendingInstallationStatus();
+    verify(editionManagementState).clearInstallErrorMessage();
+    verifyNoMoreInteractions(editionManagementState);
+    verifyZeroInteractions(licenseCommit);
+  }
+
+  @Test
+  public void start_clears_status_when_status_is_AUTOMATIC_READY_and_no_LicenseCommit_is_available() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(AUTOMATIC_READY);
+
+    underTest.start();
+
+    verify(editionManagementState).getPendingInstallationStatus();
+    verify(editionManagementState).finalizeInstallation("Edition installation didn't complete. Some plugins were not installed.");
+
+    verifyNoMoreInteractions(editionManagementState);
+    verifyZeroInteractions(licenseCommit);
+    assertThat(logTester.logs()).hasSize(1);
+    assertThat(logTester.logs(LoggerLevel.WARN))
+      .containsOnly("Edition installation didn't complete. Some plugins were not installed.");
+  }
+
+  @Test
+  public void start_clears_status_when_status_is_AUTOMATIC_READY_and_license_is_not_available() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(AUTOMATIC_READY);
+    when(editionManagementState.getPendingLicense()).thenReturn(Optional.empty());
+
+    underTestWithLicenseCommit.start();
+
+    verify(editionManagementState).getPendingInstallationStatus();
+    verify(editionManagementState).finalizeInstallation("Edition installation didn't complete. License was not found.");
+    verify(editionManagementState).getPendingLicense();
+
+    verifyNoMoreInteractions(editionManagementState);
+    verifyZeroInteractions(licenseCommit);
+    assertThat(logTester.logs()).hasSize(1);
+    assertThat(logTester.logs(LoggerLevel.WARN))
+      .containsOnly("Edition installation didn't complete. License was not found.");
+  }
+
+  @Test
+  public void start_commit_license_and_finalizeInstallation_in_editionManagementState_when_status_is_AUTOMATIC_READY_and_LicenseCommit_is_available() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(AUTOMATIC_READY);
+    String license = RandomStringUtils.randomAlphanumeric(20);
+    when(editionManagementState.getPendingLicense()).thenReturn(Optional.of(license));
+
+    underTestWithLicenseCommit.start();
+
+    verify(editionManagementState).getPendingInstallationStatus();
+    verify(editionManagementState).getPendingLicense();
+    verify(editionManagementState).finalizeInstallation(null);
+    verifyNoMoreInteractions(editionManagementState);
+    verify(licenseCommit).update(license);
+    verifyNoMoreInteractions(licenseCommit);
+  }
+
+  @Test
+  public void start_commit_license_and_finalizeInstallation_with_error_in_editionManagementState_when_status_is_AUTOMATIC_READY_and_license_is_invalid() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(AUTOMATIC_READY);
+    String license = RandomStringUtils.randomAlphanumeric(20);
+    when(editionManagementState.getPendingLicense()).thenReturn(Optional.of(license));
+    doThrow(new IllegalArgumentException("Faking an IAE because of an invalid license"))
+      .when(licenseCommit)
+      .update(license);
+
+    underTestWithLicenseCommit.start();
+
+    verify(editionManagementState).getPendingInstallationStatus();
+    verify(editionManagementState).getPendingLicense();
+    verify(editionManagementState).finalizeInstallation("Edition installation didn't complete. License is not valid. Please set a new license.");
+    verifyNoMoreInteractions(editionManagementState);
+    verify(licenseCommit).update(license);
+    verifyNoMoreInteractions(licenseCommit);
+    assertThat(logTester.logs()).hasSize(1);
+    assertThat(logTester.logs(LoggerLevel.WARN))
+      .containsOnly("Edition installation didn't complete. License is not valid. Please set a new license.");
+  }
+
+  @Test
+  public void start_clears_status_when_status_is_MANUAL_IN_PROGRESS_and_no_LicenseCommit_is_available() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(MANUAL_IN_PROGRESS);
+
+    underTest.start();
+
+    verify(editionManagementState).getPendingInstallationStatus();
+    verify(editionManagementState).finalizeInstallation("Edition installation didn't complete. Some plugins were not installed.");
+    verifyNoMoreInteractions(editionManagementState);
+    verifyZeroInteractions(licenseCommit);
+    assertThat(logTester.logs()).hasSize(1);
+    assertThat(logTester.logs(LoggerLevel.WARN))
+      .containsOnly("Edition installation didn't complete. Some plugins were not installed.");
+  }
+
+  @Test
+  public void start_commit_license_and_finalizeInstallation_in_editionManagementState_when_status_is_MANUAL_IN_PROGRESS_and_LicenseCommit_is_available() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(MANUAL_IN_PROGRESS);
+    String license = RandomStringUtils.randomAlphanumeric(20);
+    when(editionManagementState.getPendingLicense()).thenReturn(Optional.of(license));
+
+    underTestWithLicenseCommit.start();
+
+    verify(editionManagementState).getPendingInstallationStatus();
+    verify(editionManagementState).getPendingLicense();
+    verify(editionManagementState).finalizeInstallation(null);
+    verifyNoMoreInteractions(editionManagementState);
+    verify(licenseCommit).update(license);
+    verifyNoMoreInteractions(licenseCommit);
+  }
+
+  @Test
+  public void start_commit_license_and_finalizeInstallation_with_error_in_editionManagementState_when_status_is_MANUAL_IN_PROGRESS_and_license_is_invalid() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(MANUAL_IN_PROGRESS);
+    String license = RandomStringUtils.randomAlphanumeric(20);
+    when(editionManagementState.getPendingLicense()).thenReturn(Optional.of(license));
+    doThrow(new IllegalArgumentException("Faking an IAE because of an invalid license"))
+      .when(licenseCommit)
+      .update(license);
+
+    underTestWithLicenseCommit.start();
+
+    verify(editionManagementState).getPendingInstallationStatus();
+    verify(editionManagementState).getPendingLicense();
+    verify(editionManagementState).finalizeInstallation("Edition installation didn't complete. License is not valid. Please set a new license.");
+    verifyNoMoreInteractions(editionManagementState);
+    verify(licenseCommit).update(license);
+    verifyNoMoreInteractions(licenseCommit);
+    assertThat(logTester.logs()).hasSize(1);
+    assertThat(logTester.logs(LoggerLevel.WARN))
+      .containsOnly("Edition installation didn't complete. License is not valid. Please set a new license.");
+  }
+
+  @Test
+  public void start_put_editionManagement_set_in_automaticInstallError_when_status_is_AUTOMATIC_PROGRESS_and_no_LicenseCommit_is_available() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(AUTOMATIC_IN_PROGRESS);
+
+    underTest.start();
+
+    verify(editionManagementState).getPendingInstallationStatus();
+    verify(editionManagementState).installFailed("SonarQube was restarted before asynchronous installation of edition completed");
+    verifyNoMoreInteractions(editionManagementState);
+    verifyZeroInteractions(licenseCommit);
+  }
+
+  @Test
+  public void start_put_editionManagement_set_in_automaticInstallError_when_status_is_AUTOMATIC_PROGRESS_and_LicenseCommit_is_available() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(AUTOMATIC_IN_PROGRESS);
+
+    underTestWithLicenseCommit.start();
+
+    verify(editionManagementState).getPendingInstallationStatus();
+    verify(editionManagementState).installFailed("SonarQube was restarted before asynchronous installation of edition completed");
+    verifyNoMoreInteractions(editionManagementState);
+    verifyZeroInteractions(licenseCommit);
+  }
+
+  @Test
+  public void stop_should_remove_license_if_uninstalling_and_LicenseCommit_is_available() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(UNINSTALL_IN_PROGRESS);
+
+    underTestWithLicenseCommit.stop();
+
+    assertThat(logTester.logs()).hasSize(1);
+    assertThat(logTester.logs(LoggerLevel.DEBUG))
+      .containsOnly("Removing license");
+    verify(licenseCommit).delete();
+    verifyNoMoreInteractions(licenseCommit);
+    verify(editionManagementState).getPendingInstallationStatus();
+    verifyNoMoreInteractions(editionManagementState);
+  }
+
+  @Test
+  public void stop_should_log_if_uninstalling_and_LicenseCommit_is_not_available() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(UNINSTALL_IN_PROGRESS);
+
+    underTest.stop();
+
+    assertThat(logTester.logs()).hasSize(1);
+    assertThat(logTester.logs(LoggerLevel.WARN))
+      .containsOnly("License Manager plugin not found - cannot remove the license");
+    verify(editionManagementState).getPendingInstallationStatus();
+    verifyNoMoreInteractions(editionManagementState);
+  }
+
+  @Test
+  public void should_commit_uninstall() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(EditionManagementState.PendingStatus.UNINSTALL_IN_PROGRESS);
+
+    underTest.start();
+
+    verify(editionManagementState).getPendingInstallationStatus();
+    verify(editionManagementState).finalizeInstallation(null);
+    verifyNoMoreInteractions(editionManagementState);
+  }
+
+  @Test
+  public void should_fail_uninstall_if_license_commit_is_present() {
+    when(editionManagementState.getPendingInstallationStatus()).thenReturn(EditionManagementState.PendingStatus.UNINSTALL_IN_PROGRESS);
+
+    expectedException.expect(IllegalStateException.class);
+    expectedException.expectMessage("License Manager plugin is still present");
+
+    underTestWithLicenseCommit.start();
+  }
+}