From: Duarte Meneses Date: Tue, 17 Oct 2017 07:25:40 +0000 (+0200) Subject: SONAR-9940 implementation of api/editions/apply_license X-Git-Tag: 6.7-RC1~98 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0720d837bab32ecc80b9f54a404fbf72da68c73a;p=sonarqube.git SONAR-9940 implementation of api/editions/apply_license --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/edition/ws/ApplyLicenseAction.java b/server/sonar-server/src/main/java/org/sonar/server/edition/ws/ApplyLicenseAction.java index 0d5c3b831ad..abdc676593d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/edition/ws/ApplyLicenseAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/edition/ws/ApplyLicenseAction.java @@ -19,7 +19,6 @@ */ package org.sonar.server.edition.ws; -import java.util.Collections; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.api.server.ws.Request; @@ -30,6 +29,7 @@ import org.sonar.server.edition.License; import org.sonar.server.edition.MutableEditionManagementState; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.license.LicenseCommit; +import org.sonar.server.plugins.edition.EditionInstaller; import org.sonar.server.user.UserSession; import org.sonar.server.ws.WsUtils; import org.sonarqube.ws.WsEditions; @@ -41,17 +41,19 @@ public class ApplyLicenseAction implements EditionsWsAction { private final UserSession userSession; private final MutableEditionManagementState editionManagementState; + private final EditionInstaller editionInstaller; @CheckForNull private final LicenseCommit licenseCommit; - public ApplyLicenseAction(UserSession userSession, MutableEditionManagementState editionManagementState) { - this(userSession, editionManagementState, null); + public ApplyLicenseAction(UserSession userSession, MutableEditionManagementState editionManagementState, EditionInstaller editionInstaller) { + this(userSession, editionManagementState, editionInstaller, null); } - public ApplyLicenseAction(UserSession userSession, MutableEditionManagementState editionManagementState, + public ApplyLicenseAction(UserSession userSession, MutableEditionManagementState editionManagementState, EditionInstaller editionInstaller, @Nullable LicenseCommit licenseCommit) { this.userSession = userSession; this.editionManagementState = editionManagementState; + this.editionInstaller = editionInstaller; this.licenseCommit = licenseCommit; } @@ -78,18 +80,21 @@ public class ApplyLicenseAction implements EditionsWsAction { throw BadRequestException.create("Can't apply a license when applying one is already in progress"); } - String license = request.mandatoryParam(PARAM_LICENSE); - License newLicense = new License(license, Collections.emptyList(), license); - if (license.contains("manual")) { - editionManagementState.startManualInstall(newLicense); - } else if (license.contains("done")) { + String licenseParam = request.mandatoryParam(PARAM_LICENSE); + License newLicense = License.parse(licenseParam).orElseThrow(() -> BadRequestException.create("The license provided is invalid")); + + if (!editionInstaller.requiresInstallationChange(newLicense.getPluginKeys())) { + editionManagementState.newEditionWithoutInstall(newLicense.getEditionKey()); checkState(licenseCommit != null, "Can't decide edition does not require install if LicenseCommit instance is null. " + "License-manager plugin should be installed."); - licenseCommit.update(newLicense.getContent()); - editionManagementState.newEditionWithoutInstall(newLicense.getEditionKey()); - } else { - editionManagementState.startAutomaticInstall(newLicense); + licenseCommit.update(newLicense.getContent()); } else { + boolean online = editionInstaller.install(newLicense.getPluginKeys()); + if (online) { + editionManagementState.startAutomaticInstall(newLicense); + } else { + editionManagementState.startManualInstall(newLicense); + } } WsUtils.writeProtobuf(buildResponse(), request, response); diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/edition/EditionInstaller.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/edition/EditionInstaller.java index 966aa26c4cf..8b1e4240e4e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/edition/EditionInstaller.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/edition/EditionInstaller.java @@ -19,11 +19,17 @@ */ package org.sonar.server.plugins.edition; +import com.google.common.base.Optional; +import java.util.Collection; +import java.util.Map; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import org.sonar.core.platform.PluginInfo; +import org.sonar.server.edition.MutableEditionManagementState; import org.sonar.server.plugins.ServerPluginRepository; +import org.sonar.server.plugins.UpdateCenterMatrixFactory; +import org.sonar.updatecenter.common.UpdateCenter; public class EditionInstaller { private final ReentrantLock lock = new ReentrantLock(); @@ -31,23 +37,35 @@ public class EditionInstaller { private final EditionPluginDownloader editionPluginDownloader; private final EditionPluginUninstaller editionPluginUninstaller; private final ServerPluginRepository pluginRepository; + private final UpdateCenterMatrixFactory updateCenterMatrixFactory; + private final MutableEditionManagementState editionManagementState; public EditionInstaller(EditionPluginDownloader editionDownloader, EditionPluginUninstaller editionPluginUninstaller, - ServerPluginRepository pluginRepository, EditionInstallerExecutor executor) { + ServerPluginRepository pluginRepository, EditionInstallerExecutor executor, UpdateCenterMatrixFactory updateCenterMatrixFactory, + MutableEditionManagementState editionManagementState) { this.editionPluginDownloader = editionDownloader; this.editionPluginUninstaller = editionPluginUninstaller; this.pluginRepository = pluginRepository; this.executor = executor; + this.updateCenterMatrixFactory = updateCenterMatrixFactory; + this.editionManagementState = editionManagementState; } - public void install(Set editionPlugins) { + /** + * Refreshes the update center, and submits in a executor a task to download all the needed plugins (asynchronously). + * If the update center is disabled or if we are offline, the task is not submitted and false is returned. + * @return true if a task was submitted to perform the download, false if update center is unavailable. + * @throws IllegalStateException if an installation is already in progress + */ + public boolean install(Set editionPluginKeys) { if (lock.tryLock()) { try { - if (!requiresInstallationChange(editionPlugins)) { - return; + Optional updateCenter = updateCenterMatrixFactory.getUpdateCenter(true); + if (!updateCenter.isPresent()) { + return false; } - - executor.execute(() -> asyncInstall(editionPlugins)); + executor.execute(() -> asyncInstall(editionPluginKeys, updateCenter.get())); + return true; } catch (RuntimeException e) { lock.unlock(); throw e; @@ -57,33 +75,42 @@ public class EditionInstaller { } } + public boolean isOffline() { + return !updateCenterMatrixFactory.getUpdateCenter(true).isPresent(); + } + public boolean requiresInstallationChange(Set editionPluginKeys) { - return !pluginsToInstall(editionPluginKeys).isEmpty() || !pluginsToRemove(editionPluginKeys).isEmpty(); + Map pluginInfosByKeys = pluginRepository.getPluginInfosByKeys(); + + return !pluginsToInstall(editionPluginKeys, pluginInfosByKeys.keySet()).isEmpty() + || !pluginsToRemove(editionPluginKeys, pluginInfosByKeys.values()).isEmpty(); } - private void asyncInstall(Set editionPluginKeys) { + private void asyncInstall(Set editionPluginKeys, UpdateCenter updateCenter) { + Map pluginInfosByKeys = pluginRepository.getPluginInfosByKeys(); + Set pluginsToRemove = pluginsToRemove(editionPluginKeys, pluginInfosByKeys.values()); + Set pluginsToInstall = pluginsToInstall(editionPluginKeys, pluginInfosByKeys.keySet()); + try { - // TODO clean previously staged edition installations, or fail? - // editionPluginDownloader.cancelDownloads(); - // editionPluginUninstaller.cancelUninstalls(); - editionPluginDownloader.installEdition(pluginsToInstall(editionPluginKeys)); - for (String pluginKey : pluginsToRemove(editionPluginKeys)) { + editionPluginDownloader.downloadEditionPlugins(pluginsToInstall, updateCenter); + for (String pluginKey : pluginsToRemove) { editionPluginUninstaller.uninstall(pluginKey); } + editionManagementState.automaticInstallReady(); } finally { lock.unlock(); + // TODO: catch exceptions and set error status } } - private Set pluginsToInstall(Set editionPluginKeys) { - Set installedKeys = pluginRepository.getPluginInfosByKeys().keySet(); + private Set pluginsToInstall(Set editionPluginKeys, Set installedPluginKeys) { return editionPluginKeys.stream() - .filter(p -> !installedKeys.contains(p)) + .filter(p -> !installedPluginKeys.contains(p)) .collect(Collectors.toSet()); } - private Set pluginsToRemove(Set editionPluginKeys) { - Set installedCommercialPluginKeys = pluginRepository.getPluginInfos().stream() + private Set pluginsToRemove(Set editionPluginKeys, Collection installedPluginInfos) { + Set installedCommercialPluginKeys = installedPluginInfos.stream() .filter(EditionInstaller::isSonarSourceCommercialPlugin) .map(PluginInfo::getKey) .collect(Collectors.toSet()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/edition/EditionPluginDownloader.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/edition/EditionPluginDownloader.java index db9943f0050..dff59fe316b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/edition/EditionPluginDownloader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/edition/EditionPluginDownloader.java @@ -19,7 +19,6 @@ */ package org.sonar.server.plugins.edition; -import com.google.common.base.Optional; import java.io.File; import java.io.IOException; import java.net.URI; @@ -33,7 +32,6 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.core.util.FileUtils; import org.sonar.server.platform.ServerFileSystem; -import org.sonar.server.plugins.UpdateCenterMatrixFactory; import org.sonar.updatecenter.common.Release; import org.sonar.updatecenter.common.UpdateCenter; import org.sonar.updatecenter.common.Version; @@ -45,37 +43,32 @@ public class EditionPluginDownloader { private static final Logger LOG = Loggers.get(EditionPluginDownloader.class); private static final String PLUGIN_EXTENSION = "jar"; - private final UpdateCenterMatrixFactory updateCenterMatrixFactory; private final Path tmpDir; private final Path downloadDir; private final HttpDownloader downloader; - public EditionPluginDownloader(UpdateCenterMatrixFactory updateCenterMatrixFactory, HttpDownloader downloader, ServerFileSystem fileSystem) { + public EditionPluginDownloader(HttpDownloader downloader, ServerFileSystem fileSystem) { this.downloadDir = fileSystem.getEditionDownloadedPluginsDir().toPath(); - this.updateCenterMatrixFactory = updateCenterMatrixFactory; this.downloader = downloader; this.tmpDir = downloadDir.resolveSibling(downloadDir.getFileName() + "_tmp"); } - public void installEdition(Set pluginKeys) { + public void downloadEditionPlugins(Set pluginKeys, UpdateCenter updateCenter) { try { - Optional updateCenter = updateCenterMatrixFactory.getUpdateCenter(true); - if (updateCenter.isPresent()) { - Set pluginsToInstall = new HashSet<>(); - for (String pluginKey : pluginKeys) { - pluginsToInstall.addAll(updateCenter.get().findInstallablePlugins(pluginKey, Version.create(""))); - } - - FileUtils.deleteQuietly(tmpDir); - Files.createDirectories(tmpDir); + Set pluginsToInstall = new HashSet<>(); + for (String pluginKey : pluginKeys) { + pluginsToInstall.addAll(updateCenter.findInstallablePlugins(pluginKey, Version.create(""))); + } - for (Release r : pluginsToInstall) { - download(r); - } + FileUtils.deleteQuietly(tmpDir); + Files.createDirectories(tmpDir); - FileUtils.deleteQuietly(downloadDir); - Files.move(tmpDir, downloadDir); + for (Release r : pluginsToInstall) { + download(r); } + + FileUtils.deleteQuietly(downloadDir); + Files.move(tmpDir, downloadDir); } catch (Exception e) { FileUtils.deleteQuietly(tmpDir); throw new IllegalStateException("Failed to install edition", e); diff --git a/server/sonar-server/src/test/java/org/sonar/server/edition/LicenseTest.java b/server/sonar-server/src/test/java/org/sonar/server/edition/LicenseTest.java index d894b5975e5..4f155e97a7c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/edition/LicenseTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/edition/LicenseTest.java @@ -97,7 +97,7 @@ public class LicenseTest { } @Test - public void parse_is_empty_if_no_plugin() throws IOException { + public void parse_is_empty_if_license_has_no_plugin() throws IOException { Properties props = new Properties(); props.setProperty("Plugins", ""); props.setProperty("Edition", "dev"); @@ -110,6 +110,19 @@ public class LicenseTest { assertThat(license).isEmpty(); } + @Test + public void parse_is_empty_if_license_has_no_edition_key() throws IOException { + Properties props = new Properties(); + props.setProperty("Plugins", "plugin1,plugin2"); + StringWriter writer = new StringWriter(); + props.store(writer, ""); + + byte[] encoded = Base64.getEncoder().encode(writer.toString().getBytes()); + + Optional license = License.parse(new String(encoded)); + assertThat(license).isEmpty(); + } + @Test public void verify_getters() { ImmutableSet pluginKeys = ImmutableSet.of("a", "b", "c"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/edition/ws/ApplyLicenseActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/edition/ws/ApplyLicenseActionTest.java index f12b5fc7753..bb11461f447 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/edition/ws/ApplyLicenseActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/edition/ws/ApplyLicenseActionTest.java @@ -22,26 +22,41 @@ package org.sonar.server.edition.ws; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; +import java.io.IOException; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; import java.util.Optional; +import java.util.Properties; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.sonar.api.server.ws.WebService; import org.sonar.server.edition.EditionManagementState; +import org.sonar.server.edition.EditionManagementState.PendingStatus; +import org.sonar.server.edition.License; import org.sonar.server.edition.MutableEditionManagementState; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.license.LicenseCommit; +import org.sonar.server.plugins.edition.EditionInstaller; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; +import org.sonar.server.ws.TestResponse; import org.sonar.server.ws.WsActionTester; import org.sonar.test.JsonAssert; +import org.sonarqube.ws.MediaTypes; +import org.sonarqube.ws.WsEditions; +import org.sonarqube.ws.WsEditions.StatusResponse; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; 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.NONE; @@ -49,15 +64,17 @@ import static org.sonar.server.edition.EditionManagementState.PendingStatus.NONE @RunWith(DataProviderRunner.class) public class ApplyLicenseActionTest { private static final String PARAM_LICENSE = "license"; + private static final String PENDING_EDITION_NAME = "developer-edition"; @Rule public ExpectedException expectedException = ExpectedException.none(); @Rule public UserSessionRule userSessionRule = UserSessionRule.standalone(); + private EditionInstaller editionInstaller = mock(EditionInstaller.class); private MutableEditionManagementState mutableEditionManagementState = mock(MutableEditionManagementState.class); private LicenseCommit licenseCommit = mock(LicenseCommit.class); - private ApplyLicenseAction underTest = new ApplyLicenseAction(userSessionRule, mutableEditionManagementState, licenseCommit); + private ApplyLicenseAction underTest = new ApplyLicenseAction(userSessionRule, mutableEditionManagementState, editionInstaller, licenseCommit); private WsActionTester actionTester = new WsActionTester(underTest); @Test @@ -127,25 +144,109 @@ public class ApplyLicenseActionTest { } @Test - public void verify_example() { + public void request_fails_with_BadRequestException_if_license_is_invalid() { userSessionRule.logIn().setSystemAdministrator(); - when(mutableEditionManagementState.getCurrentEditionKey()).thenReturn(Optional.empty()); - when(mutableEditionManagementState.getPendingEditionKey()).thenReturn(Optional.of("developer-edition")); - when(mutableEditionManagementState.getPendingInstallationStatus()) - .thenReturn(NONE) - .thenReturn(AUTOMATIC_IN_PROGRESS); + TestRequest request = actionTester.newRequest() + .setParam(PARAM_LICENSE, "invalid"); + when(mutableEditionManagementState.getPendingInstallationStatus()).thenReturn(NONE); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("The license provided is invalid"); + request.execute(); + } + + @Test + public void verify_example() throws IOException { + userSessionRule.logIn().setSystemAdministrator(); + setPendingLicense(AUTOMATIC_IN_PROGRESS); TestRequest request = actionTester.newRequest() - .setParam(PARAM_LICENSE, "foo"); + .setParam(PARAM_LICENSE, createLicenseParam("dev", "plugin1")); JsonAssert.assertJson(request.execute().getInput()).isSimilarTo(actionTester.getDef().responseExampleAsString()); } + @Test + public void apply_without_need_to_install() throws IOException { + userSessionRule.logIn().setSystemAdministrator(); + setPendingLicense(NONE); + when(editionInstaller.requiresInstallationChange(Collections.singleton("plugin1"))).thenReturn(false); + + TestRequest request = actionTester.newRequest() + .setMediaType(MediaTypes.PROTOBUF) + .setParam(PARAM_LICENSE, createLicenseParam(PENDING_EDITION_NAME, "plugin1")); + + TestResponse response = request.execute(); + assertResponse(response, PENDING_EDITION_NAME, "", NONE); + verify(mutableEditionManagementState).newEditionWithoutInstall(PENDING_EDITION_NAME); + } + + @Test + public void apply_offline() throws IOException { + userSessionRule.logIn().setSystemAdministrator(); + setPendingLicense(PendingStatus.MANUAL_IN_PROGRESS); + when(editionInstaller.requiresInstallationChange(Collections.singleton("plugin1"))).thenReturn(true); + when(editionInstaller.install(Collections.singleton("plugin1"))).thenReturn(false); + + TestRequest request = actionTester.newRequest() + .setMediaType(MediaTypes.PROTOBUF) + .setParam(PARAM_LICENSE, createLicenseParam(PENDING_EDITION_NAME, "plugin1")); + + TestResponse response = request.execute(); + + assertResponse(response, PENDING_EDITION_NAME, "", PendingStatus.MANUAL_IN_PROGRESS); + verify(mutableEditionManagementState).startManualInstall(any(License.class)); + } + + @Test + public void apply_successfully_auto_installation() throws IOException { + userSessionRule.logIn().setSystemAdministrator(); + setPendingLicense(PendingStatus.AUTOMATIC_IN_PROGRESS); + when(editionInstaller.requiresInstallationChange(Collections.singleton("plugin1"))).thenReturn(true); + when(editionInstaller.install(Collections.singleton("plugin1"))).thenReturn(true); + + TestRequest request = actionTester.newRequest() + .setMediaType(MediaTypes.PROTOBUF) + .setParam(PARAM_LICENSE, createLicenseParam(PENDING_EDITION_NAME, "plugin1")); + + TestResponse response = request.execute(); + + assertResponse(response, PENDING_EDITION_NAME, "", PendingStatus.AUTOMATIC_IN_PROGRESS); + verify(mutableEditionManagementState).startAutomaticInstall(any(License.class)); + } + + private void assertResponse(TestResponse response, String expectedNextEditionKey, String expectedEditionKey, + PendingStatus expectedPendingStatus) throws IOException { + StatusResponse parsedResponse = WsEditions.StatusResponse.parseFrom(response.getInputStream()); + assertThat(parsedResponse.getCurrentEditionKey()).isEqualTo(expectedEditionKey); + assertThat(parsedResponse.getNextEditionKey()).isEqualTo(expectedNextEditionKey); + assertThat(parsedResponse.getInstallationStatus()).isEqualTo(WsEditions.InstallationStatus.valueOf(expectedPendingStatus.toString())); + } + + private void setPendingLicense(PendingStatus pendingStatus) { + when(mutableEditionManagementState.getCurrentEditionKey()).thenReturn(Optional.empty()); + when(mutableEditionManagementState.getPendingEditionKey()).thenReturn(Optional.of(PENDING_EDITION_NAME)); + when(mutableEditionManagementState.getPendingInstallationStatus()) + .thenReturn(NONE) + .thenReturn(pendingStatus); + } + + private static String createLicenseParam(String editionKey, String... pluginKeys) throws IOException { + Properties props = new Properties(); + props.setProperty("Plugins", String.join(",", pluginKeys)); + props.setProperty("Edition", editionKey); + StringWriter writer = new StringWriter(); + props.store(writer, ""); + + byte[] encoded = Base64.getEncoder().encode(writer.toString().getBytes()); + return new String(encoded, StandardCharsets.UTF_8); + } + @DataProvider public static Object[][] notNonePendingInstallationStatuses() { return Arrays.stream(EditionManagementState.PendingStatus.values()) - .filter(s -> s != NONE) - .map(s -> new Object[] {s}) - .toArray(Object[][]::new); + .filter(s -> s != NONE) + .map(s -> new Object[] {s}) + .toArray(Object[][]::new); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/edition/EditionInstallerTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/edition/EditionInstallerTest.java index 47a6a587c17..ff988300f94 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/edition/EditionInstallerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/edition/EditionInstallerTest.java @@ -19,6 +19,7 @@ */ package org.sonar.server.plugins.edition; +import com.google.common.base.Optional; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -27,23 +28,29 @@ import java.util.Set; import java.util.stream.Collectors; import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.sonar.core.platform.PluginInfo; +import org.sonar.server.edition.MutableEditionManagementState; import org.sonar.server.plugins.ServerPluginRepository; +import org.sonar.server.plugins.UpdateCenterMatrixFactory; +import org.sonar.updatecenter.common.UpdateCenter; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyBoolean; +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; public class EditionInstallerTest { - private static final String pluginKey = "key"; - @Mock - private EditionPluginDownloader downloader; - @Mock - private EditionPluginUninstaller uninstaller; - @Mock - private ServerPluginRepository pluginRepository; + private static final String PLUGIN_KEY = "key"; + + private EditionPluginDownloader downloader = mock(EditionPluginDownloader.class); + private EditionPluginUninstaller uninstaller = mock(EditionPluginUninstaller.class); + private UpdateCenterMatrixFactory updateCenterMatrixFactory = mock(UpdateCenterMatrixFactory.class); + private ServerPluginRepository pluginRepository = mock(ServerPluginRepository.class); + private UpdateCenter updateCenter = mock(UpdateCenter.class); + private MutableEditionManagementState editionManagementState = mock(MutableEditionManagementState.class); private EditionInstallerExecutor executor = new EditionInstallerExecutor() { public void execute(Runnable r) { @@ -51,18 +58,17 @@ public class EditionInstallerTest { } }; - private EditionInstaller installer; + private EditionInstaller installer = new EditionInstaller(downloader, uninstaller, pluginRepository, executor, updateCenterMatrixFactory, editionManagementState); @Before - public void before() { - MockitoAnnotations.initMocks(this); - installer = new EditionInstaller(downloader, uninstaller, pluginRepository, executor); + public void setUp() { + when(updateCenterMatrixFactory.getUpdateCenter(anyBoolean())).thenReturn(Optional.of(updateCenter)); } @Test - public void install() { - installer.install(Collections.singleton(pluginKey)); - verify(downloader).installEdition(Collections.singleton(pluginKey)); + public void launch_task_download_plugins() { + assertThat(installer.install(Collections.singleton(PLUGIN_KEY))).isTrue(); + verify(downloader).downloadEditionPlugins(Collections.singleton(PLUGIN_KEY), updateCenter); } @Test @@ -77,11 +83,70 @@ public class EditionInstallerTest { editionPlugins.add("p4"); installer.install(editionPlugins); - verify(downloader).installEdition(Collections.singleton("p4")); + verify(editionManagementState).automaticInstallReady(); + verify(downloader).downloadEditionPlugins(Collections.singleton("p4"), updateCenter); verify(uninstaller).uninstall("p2"); verifyNoMoreInteractions(uninstaller); verifyNoMoreInteractions(downloader); + } + + @Test + public void do_nothing_if_offline() { + mockPluginRepository(createPluginInfo("p1", true)); + executor = mock(EditionInstallerExecutor.class); + when(updateCenterMatrixFactory.getUpdateCenter(true)).thenReturn(Optional.absent()); + installer = new EditionInstaller(downloader, uninstaller, pluginRepository, executor, updateCenterMatrixFactory, editionManagementState); + assertThat(installer.install(Collections.singleton("p1"))).isFalse(); + + verifyZeroInteractions(executor); + verifyZeroInteractions(uninstaller); + verifyZeroInteractions(downloader); + verifyZeroInteractions(editionManagementState); + } + + @Test + public void is_offline() { + when(updateCenterMatrixFactory.getUpdateCenter(true)).thenReturn(Optional.absent()); + assertThat(installer.isOffline()).isTrue(); + } + + @Test + public void is_not_offline() { + assertThat(installer.isOffline()).isFalse(); + } + + @Test + public void requires_installation_change() { + PluginInfo commercial1 = createPluginInfo("p1", true); + PluginInfo commercial2 = createPluginInfo("p2", true); + PluginInfo open1 = createPluginInfo("p3", false); + mockPluginRepository(commercial1, commercial2, open1); + + Set editionPlugins = new HashSet<>(); + editionPlugins.add("p1"); + editionPlugins.add("p4"); + + assertThat(installer.requiresInstallationChange(editionPlugins)).isTrue(); + verifyZeroInteractions(downloader); + verifyZeroInteractions(uninstaller); + verifyZeroInteractions(editionManagementState); + } + + @Test + public void does_not_require_installation_change() { + PluginInfo commercial1 = createPluginInfo("p1", true); + PluginInfo commercial2 = createPluginInfo("p2", true); + PluginInfo open1 = createPluginInfo("p3", false); + mockPluginRepository(commercial1, commercial2, open1); + + Set editionPlugins = new HashSet<>(); + editionPlugins.add("p1"); + editionPlugins.add("p2"); + assertThat(installer.requiresInstallationChange(editionPlugins)).isFalse(); + verifyZeroInteractions(downloader); + verifyZeroInteractions(uninstaller); + verifyZeroInteractions(editionManagementState); } private void mockPluginRepository(PluginInfo... installedPlugins) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/edition/EditionPluginDownloaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/edition/EditionPluginDownloaderTest.java index 6f3545e272d..c2659beacfe 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/edition/EditionPluginDownloaderTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/edition/EditionPluginDownloaderTest.java @@ -19,7 +19,6 @@ */ package org.sonar.server.plugins.edition; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import java.io.File; import java.io.IOException; @@ -31,11 +30,8 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.sonar.api.utils.HttpDownloader; import org.sonar.server.platform.ServerFileSystem; -import org.sonar.server.plugins.UpdateCenterMatrixFactory; import org.sonar.updatecenter.common.Plugin; import org.sonar.updatecenter.common.Release; import org.sonar.updatecenter.common.UpdateCenter; @@ -43,7 +39,6 @@ import org.sonar.updatecenter.common.Version; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; -import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -52,14 +47,10 @@ import static org.mockito.Mockito.when; public class EditionPluginDownloaderTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); - @Mock - private UpdateCenterMatrixFactory updateCenterMatrixFactory; - @Mock - private UpdateCenter updateCenter; - @Mock - private ServerFileSystem fs; - @Mock - private HttpDownloader httpDownloader; + + private ServerFileSystem fs = mock(ServerFileSystem.class); + private HttpDownloader httpDownloader = mock(HttpDownloader.class); + private UpdateCenter updateCenter = mock(UpdateCenter.class); private File downloadDir; private File tmpDir; @@ -67,12 +58,10 @@ public class EditionPluginDownloaderTest { @Before public void setUp() throws IOException { - MockitoAnnotations.initMocks(this); - downloadDir = temp.newFolder(); + downloadDir = temp.newFolder("download"); tmpDir = new File(downloadDir.getParentFile(), downloadDir.getName() + "_tmp"); - when(updateCenterMatrixFactory.getUpdateCenter(anyBoolean())).thenReturn(Optional.of(updateCenter)); when(fs.getEditionDownloadedPluginsDir()).thenReturn(downloadDir); - downloader = new EditionPluginDownloader(updateCenterMatrixFactory, httpDownloader, fs); + downloader = new EditionPluginDownloader(httpDownloader, fs); } @Test @@ -81,7 +70,7 @@ public class EditionPluginDownloaderTest { createRelease("plugin2", "1.0", "http://host/plugin2.jar")); when(updateCenter.findInstallablePlugins("plugins", Version.create(""))).thenReturn(releases); - downloader.installEdition(Collections.singleton("plugins")); + downloader.downloadEditionPlugins(Collections.singleton("plugins"), updateCenter); verify(httpDownloader).download(new URI("http://host/plugin1.jar"), new File(tmpDir, "plugin1.jar")); verify(httpDownloader).download(new URI("http://host/plugin2.jar"), new File(tmpDir, "plugin2.jar")); @@ -100,14 +89,13 @@ public class EditionPluginDownloaderTest { when(updateCenter.findInstallablePlugins("plugins", Version.create(""))).thenReturn(releases); try { - downloader.installEdition(Collections.singleton("plugins")); + downloader.downloadEditionPlugins(Collections.singleton("plugins"), updateCenter); fail("expecting exception"); } catch (IllegalStateException e) { } verify(httpDownloader).download(new URI("http://host/plugin1.jar"), new File(tmpDir, "plugin1.jar")); - verify(httpDownloader).download(new URI("http://host/plugin2.jar"), new File(tmpDir, "plugin2.jar")); assertThat(downloadDir.list()).isEmpty(); assertThat(tmpDir).doesNotExist();