@@ -24,9 +24,9 @@ import java.io.IOException; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.server.platform.ServerFileSystem; | |||
import org.sonar.core.platform.ExplodedPlugin; | |||
import org.sonar.core.platform.PluginInfo; | |||
import org.sonar.server.platform.ServerFileSystem; | |||
import static org.apache.commons.io.FileUtils.sizeOfDirectory; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@@ -97,11 +97,6 @@ public class CePluginJarExploderTest { | |||
this.temp = temp; | |||
} | |||
@Override | |||
public File getDataDir() { | |||
throw new UnsupportedOperationException(); | |||
} | |||
@Override | |||
public File getHomeDir() { | |||
throw new UnsupportedOperationException(); | |||
@@ -139,20 +134,10 @@ public class CePluginJarExploderTest { | |||
throw new UnsupportedOperationException(); | |||
} | |||
@Override | |||
public File getEditionDownloadedPluginsDir() { | |||
throw new UnsupportedOperationException(); | |||
} | |||
@Override | |||
public File getUninstalledPluginsDir() { | |||
throw new UnsupportedOperationException(); | |||
} | |||
@Override | |||
public File getEditionUninstalledPluginsDir() { | |||
throw new UnsupportedOperationException(); | |||
} | |||
} | |||
} |
@@ -1,64 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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; | |||
public interface EditionManagementState { | |||
/** | |||
* @return {@link Optional#empty() empty} if there is no edition installed | |||
*/ | |||
Optional<String> getCurrentEditionKey(); | |||
/** | |||
* @return the pending installation status. | |||
*/ | |||
PendingStatus getPendingInstallationStatus(); | |||
/** | |||
* @return {@link Optional#empty() empty} when {@link #getPendingInstallationStatus()} returns {@link PendingStatus#NONE}, | |||
* otherwise a {@link String} | |||
*/ | |||
Optional<String> getPendingEditionKey(); | |||
/** | |||
* The license string. | |||
* | |||
* @return {@link Optional#empty() empty} when {@link #getPendingInstallationStatus()} returns {@link PendingStatus#NONE}, | |||
* otherwise a {@link String} | |||
*/ | |||
Optional<String> getPendingLicense(); | |||
/** | |||
* The message explaining the error that made the install fail (if any). | |||
* | |||
* @return a {@link String} if {@link #getPendingInstallationStatus()} returns {@link PendingStatus#NONE} and an error | |||
* occurred during install, otherwise {@link Optional#empty() empty} | |||
*/ | |||
Optional<String> getInstallErrorMessage(); | |||
enum PendingStatus { | |||
NONE, | |||
AUTOMATIC_IN_PROGRESS, | |||
AUTOMATIC_READY, | |||
MANUAL_IN_PROGRESS, | |||
UNINSTALL_IN_PROGRESS | |||
} | |||
} |
@@ -1,44 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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 org.sonar.core.platform.Module; | |||
import org.sonar.server.edition.ws.ApplyLicenseAction; | |||
import org.sonar.server.edition.ws.ClearErrorMessageAction; | |||
import org.sonar.server.edition.ws.EditionsWs; | |||
import org.sonar.server.edition.ws.FormDataAction; | |||
import org.sonar.server.edition.ws.PreviewAction; | |||
import org.sonar.server.edition.ws.StatusAction; | |||
import org.sonar.server.edition.ws.UninstallAction; | |||
public class EditionsWsModule extends Module { | |||
@Override | |||
protected void configureModule() { | |||
add( | |||
StandaloneEditionManagementStateImpl.class, | |||
StatusAction.class, | |||
ApplyLicenseAction.class, | |||
PreviewAction.class, | |||
FormDataAction.class, | |||
ClearErrorMessageAction.class, | |||
UninstallAction.class, | |||
EditionsWs.class); | |||
} | |||
} |
@@ -1,120 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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"); | |||
} | |||
} | |||
} | |||
} |
@@ -1,94 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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 com.google.common.collect.ImmutableSet; | |||
import java.io.StringReader; | |||
import java.nio.charset.StandardCharsets; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.Optional; | |||
import java.util.Properties; | |||
import java.util.Set; | |||
import javax.annotation.concurrent.Immutable; | |||
import org.apache.commons.codec.binary.Base64; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static com.google.common.base.Preconditions.checkNotNull; | |||
@Immutable | |||
public class License { | |||
private static final Logger LOG = Loggers.get(License.class); | |||
private static final String EDITION_KEY = "Edition"; | |||
private static final String PLUGINS_KEY = "Plugins"; | |||
private final String editionKey; | |||
private final Set<String> pluginKeys; | |||
private final String content; | |||
public License(String editionKey, Collection<String> pluginKeys, String content) { | |||
this.editionKey = enforceNotNullNorEmpty(editionKey, "editionKey"); | |||
this.pluginKeys = ImmutableSet.copyOf(pluginKeys); | |||
this.content = enforceNotNullNorEmpty(content, "content"); | |||
} | |||
private static String enforceNotNullNorEmpty(String str, String propertyName) { | |||
checkNotNull(str, "%s can't be null", propertyName); | |||
checkArgument(!str.isEmpty(), "%s can't be empty", propertyName); | |||
return str; | |||
} | |||
public String getEditionKey() { | |||
return editionKey; | |||
} | |||
public Set<String> getPluginKeys() { | |||
return pluginKeys; | |||
} | |||
public String getContent() { | |||
return content; | |||
} | |||
public static Optional<License> parse(String base64) { | |||
try { | |||
String data = new String(Base64.decodeBase64(base64.trim().getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); | |||
Properties props = new Properties(); | |||
props.load(new StringReader(data)); | |||
String[] plugins = StringUtils.split(props.getProperty(PLUGINS_KEY), ','); | |||
String editionKey = props.getProperty(EDITION_KEY); | |||
if (StringUtils.isNotBlank(editionKey) && plugins.length > 0) { | |||
return Optional.of(new License(editionKey, Arrays.asList(plugins), base64)); | |||
} else { | |||
LOG.debug("Failed to parse license: no edition key and/or no plugin found"); | |||
} | |||
} catch (Exception e) { | |||
LOG.debug("Failed to parse license", e); | |||
} | |||
return Optional.empty(); | |||
} | |||
} |
@@ -1,104 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.Nullable; | |||
/** | |||
* Provides access to operations which will alter the Edition management state. | |||
*/ | |||
public interface MutableEditionManagementState extends EditionManagementState { | |||
/** | |||
* Stage the specified license and records that an automatic install of the plugins will be performed. | |||
* | |||
* @return the new {@link PendingStatus}, always {@link PendingStatus#AUTOMATIC_IN_PROGRESS} | |||
* an exception. | |||
* | |||
* @throws IllegalStateException if current status is not {@link PendingStatus#NONE} | |||
*/ | |||
PendingStatus startAutomaticInstall(License license); | |||
/** | |||
* Stage the specified license and records that a manual install of the plugins will be performed. | |||
* | |||
* @return the new {@link PendingStatus}, always {@link PendingStatus#MANUAL_IN_PROGRESS} | |||
* | |||
* @throws IllegalStateException if current status is not {@link PendingStatus#NONE} | |||
*/ | |||
PendingStatus startManualInstall(License license); | |||
/** | |||
* Records that the specified edition has been installed without plugin changes. | |||
* | |||
* @param newEditionKey can't be {@code null} nor empty | |||
* | |||
* @return the new {@link PendingStatus}, always {@link PendingStatus#NONE} | |||
* | |||
* @throws IllegalStateException if current status is not {@link PendingStatus#NONE} | |||
*/ | |||
PendingStatus newEditionWithoutInstall(String newEditionKey); | |||
/** | |||
* Records that automatic install is ready to be finalized. | |||
* | |||
* @return the new pending status, always {@link PendingStatus#AUTOMATIC_READY} | |||
* | |||
* @throws IllegalStateException if current status is not {@link PendingStatus#AUTOMATIC_IN_PROGRESS} | |||
*/ | |||
PendingStatus automaticInstallReady(); | |||
/** | |||
* Records that install failed with the specified (optional) error message to explain the cause of the | |||
* failure. | |||
* | |||
* @return the new pending status, always {@link PendingStatus#NONE} | |||
* | |||
* @throws IllegalStateException if current status is neither {@link PendingStatus#AUTOMATIC_IN_PROGRESS} nor | |||
* {@link PendingStatus#MANUAL_IN_PROGRESS} | |||
*/ | |||
PendingStatus installFailed(@Nullable String errorMessage); | |||
/** | |||
* Clears the error message set by {@link #installFailed(String)} (String)} if there is any and which ever the current | |||
* status. | |||
*/ | |||
void clearInstallErrorMessage(); | |||
/** | |||
* Uninstalls the currently installed edition | |||
* | |||
* @return the new pending status, always {@link PendingStatus#UNINSTALL_IN_PROGRESS} | |||
* | |||
* @throws IllegalStateException if current status is not {@link PendingStatus#NONE} or if there is | |||
* no edition currently installed. | |||
*/ | |||
PendingStatus uninstall(); | |||
/** | |||
* Finalize an automatic or manual install with the specified (optional) error message to explain a partially | |||
* finalized install. | |||
* | |||
* @return the new pending status, always {@link PendingStatus#NONE} | |||
* | |||
* @throws IllegalStateException if current status is neither {@link PendingStatus#AUTOMATIC_READY} nor | |||
* {@link PendingStatus#MANUAL_IN_PROGRESS} | |||
*/ | |||
PendingStatus finalizeInstallation(@Nullable String errorMessage); | |||
} |
@@ -1,381 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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 com.google.common.collect.ImmutableSet; | |||
import java.util.Arrays; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import javax.annotation.concurrent.Immutable; | |||
import org.picocontainer.Startable; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.property.InternalPropertiesDao; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static com.google.common.base.Preconditions.checkState; | |||
import static java.util.Objects.requireNonNull; | |||
import static java.util.Optional.empty; | |||
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 StandaloneEditionManagementStateImpl implements MutableEditionManagementState, Startable { | |||
private static final String CURRENT_EDITION_KEY = "currentEditionKey"; | |||
private static final String PENDING_INSTALLATION_STATUS = "pendingInstallStatus"; | |||
private static final String PENDING_EDITION_KEY = "pendingEditionKey"; | |||
private static final String PENDING_LICENSE = "pendingLicense"; | |||
private static final String INSTALL_ERROR_MESSAGE = "installError"; | |||
private final DbClient dbClient; | |||
@CheckForNull | |||
private State state; | |||
public StandaloneEditionManagementStateImpl(DbClient dbClient) { | |||
this.dbClient = dbClient; | |||
} | |||
@Override | |||
public void start() { | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
// load current state value | |||
Map<String, Optional<String>> internalPropertyValues = dbClient.internalPropertiesDao().selectByKeys(dbSession, | |||
ImmutableSet.of(CURRENT_EDITION_KEY, PENDING_INSTALLATION_STATUS, PENDING_EDITION_KEY, PENDING_LICENSE, INSTALL_ERROR_MESSAGE)); | |||
PendingStatus pendingInstallationStatus = internalPropertyValues.getOrDefault(PENDING_INSTALLATION_STATUS, empty()) | |||
.map(StandaloneEditionManagementStateImpl::emptyToNull) | |||
.map(PendingStatus::valueOf) | |||
.orElse(NONE); | |||
State.Builder builder = State.newBuilder(pendingInstallationStatus); | |||
builder | |||
.setCurrentEditionKey(internalPropertyValues.getOrDefault(CURRENT_EDITION_KEY, empty()) | |||
.map(StandaloneEditionManagementStateImpl::emptyToNull) | |||
.orElse(null)) | |||
.setPendingEditionKey(internalPropertyValues.getOrDefault(PENDING_EDITION_KEY, empty()) | |||
.map(StandaloneEditionManagementStateImpl::emptyToNull) | |||
.orElse(null)) | |||
.setPendingLicense(internalPropertyValues.getOrDefault(PENDING_LICENSE, empty()) | |||
.map(StandaloneEditionManagementStateImpl::emptyToNull) | |||
.orElse(null)) | |||
.setInstallErrorMessage(internalPropertyValues.getOrDefault(INSTALL_ERROR_MESSAGE, empty()) | |||
.map(StandaloneEditionManagementStateImpl::emptyToNull) | |||
.orElse(null)); | |||
state = builder.build(); | |||
} | |||
} | |||
@Override | |||
public void stop() { | |||
// nothing to do | |||
} | |||
@Override | |||
public Optional<String> getCurrentEditionKey() { | |||
ensureStarted(); | |||
return Optional.ofNullable(state.getCurrentEditionKey()); | |||
} | |||
@Override | |||
public PendingStatus getPendingInstallationStatus() { | |||
ensureStarted(); | |||
return state.getPendingInstallationStatus(); | |||
} | |||
@Override | |||
public Optional<String> getPendingEditionKey() { | |||
ensureStarted(); | |||
return Optional.ofNullable(state.getPendingEditionKey()); | |||
} | |||
@Override | |||
public Optional<String> getPendingLicense() { | |||
ensureStarted(); | |||
return Optional.ofNullable(state.getPendingLicense()); | |||
} | |||
@Override | |||
public Optional<String> getInstallErrorMessage() { | |||
ensureStarted(); | |||
return Optional.ofNullable(state.getInstallErrorMessage()); | |||
} | |||
@Override | |||
public synchronized PendingStatus startAutomaticInstall(License license) { | |||
ensureStarted(); | |||
checkLicense(license); | |||
State newState = changeStatusToFrom(AUTOMATIC_IN_PROGRESS, NONE) | |||
.setPendingLicense(license.getContent()) | |||
.setPendingEditionKey(license.getEditionKey()) | |||
.clearAutomaticInstallErrorMessage() | |||
.build(); | |||
persistProperties(newState); | |||
return newState.getPendingInstallationStatus(); | |||
} | |||
@Override | |||
public synchronized PendingStatus startManualInstall(License license) { | |||
ensureStarted(); | |||
checkLicense(license); | |||
State newState = changeStatusToFrom(MANUAL_IN_PROGRESS, NONE) | |||
.setPendingLicense(license.getContent()) | |||
.setPendingEditionKey(license.getEditionKey()) | |||
.clearAutomaticInstallErrorMessage() | |||
.build(); | |||
persistProperties(newState); | |||
return newState.getPendingInstallationStatus(); | |||
} | |||
@Override | |||
public synchronized PendingStatus newEditionWithoutInstall(String newEditionKey) { | |||
ensureStarted(); | |||
requireNonNull(newEditionKey, "newEditionKey can't be null"); | |||
checkArgument(!newEditionKey.isEmpty(), "newEditionKey can't be empty"); | |||
State newState = changeStatusToFrom(NONE, NONE) | |||
.setCurrentEditionKey(newEditionKey) | |||
.clearAutomaticInstallErrorMessage() | |||
.build(); | |||
persistProperties(newState); | |||
return newState.getPendingInstallationStatus(); | |||
} | |||
@Override | |||
public synchronized PendingStatus automaticInstallReady() { | |||
ensureStarted(); | |||
State newState = changeStatusToFrom(AUTOMATIC_READY, AUTOMATIC_IN_PROGRESS) | |||
.clearAutomaticInstallErrorMessage() | |||
.build(); | |||
persistProperties(newState); | |||
return newState.getPendingInstallationStatus(); | |||
} | |||
@Override | |||
public synchronized PendingStatus installFailed(@Nullable String errorMessage) { | |||
ensureStarted(); | |||
State newState = changeStatusToFrom(NONE, AUTOMATIC_IN_PROGRESS, MANUAL_IN_PROGRESS) | |||
.setInstallErrorMessage(nullableTrimmedEmptyToNull(errorMessage)) | |||
.clearPendingFields() | |||
.build(); | |||
persistProperties(newState); | |||
return newState.getPendingInstallationStatus(); | |||
} | |||
@Override | |||
public synchronized void clearInstallErrorMessage() { | |||
ensureStarted(); | |||
State currentState = this.state; | |||
if (currentState.getInstallErrorMessage() != null) { | |||
State newState = State.newBuilder(currentState) | |||
.clearAutomaticInstallErrorMessage() | |||
.build(); | |||
persistProperties(newState); | |||
} | |||
} | |||
@Override | |||
public synchronized PendingStatus finalizeInstallation(@Nullable String errorMessage) { | |||
ensureStarted(); | |||
State newState = changeStatusToFrom(NONE, AUTOMATIC_READY, MANUAL_IN_PROGRESS, UNINSTALL_IN_PROGRESS) | |||
.commitPendingEditionKey() | |||
.clearPendingFields() | |||
.setInstallErrorMessage(nullableTrimmedEmptyToNull(errorMessage)) | |||
.build(); | |||
persistProperties(newState); | |||
return newState.getPendingInstallationStatus(); | |||
} | |||
@Override | |||
public synchronized PendingStatus uninstall() { | |||
ensureStarted(); | |||
State.Builder builder = changeStatusToFrom(UNINSTALL_IN_PROGRESS, NONE); | |||
checkState(state.currentEditionKey != null, "There is no edition currently installed"); | |||
State newState = builder | |||
.clearPendingFields() | |||
.clearCurrentEditionKey() | |||
.clearAutomaticInstallErrorMessage() | |||
.build(); | |||
persistProperties(newState); | |||
return newState.getPendingInstallationStatus(); | |||
} | |||
private void ensureStarted() { | |||
checkState(state != null, "%s is not started", getClass().getSimpleName()); | |||
} | |||
private State.Builder changeStatusToFrom(PendingStatus newStatus, PendingStatus... validPendingStatuses) { | |||
State currentState = this.state; | |||
if (Arrays.stream(validPendingStatuses).noneMatch(s -> s == currentState.getPendingInstallationStatus())) { | |||
throw new IllegalStateException(String.format("Can't move to %s when status is %s (should be any of %s)", | |||
newStatus, currentState.getPendingInstallationStatus(), Arrays.toString(validPendingStatuses))); | |||
} | |||
return State.newBuilder(currentState, newStatus); | |||
} | |||
private void persistProperties(State newState) { | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
InternalPropertiesDao internalPropertiesDao = dbClient.internalPropertiesDao(); | |||
saveInternalProperty(internalPropertiesDao, dbSession, PENDING_EDITION_KEY, newState.getPendingEditionKey()); | |||
saveInternalProperty(internalPropertiesDao, dbSession, PENDING_LICENSE, newState.getPendingLicense()); | |||
saveInternalProperty(internalPropertiesDao, dbSession, INSTALL_ERROR_MESSAGE, newState.getInstallErrorMessage()); | |||
saveInternalProperty(internalPropertiesDao, dbSession, CURRENT_EDITION_KEY, newState.getCurrentEditionKey()); | |||
saveInternalProperty(internalPropertiesDao, dbSession, PENDING_INSTALLATION_STATUS, newState.getPendingInstallationStatus().name()); | |||
dbSession.commit(); | |||
this.state = newState; | |||
} | |||
} | |||
private static void saveInternalProperty(InternalPropertiesDao dao, DbSession dbSession, String key, @Nullable String value) { | |||
if (value == null) { | |||
dao.saveAsEmpty(dbSession, key); | |||
} else { | |||
dao.save(dbSession, key, value); | |||
} | |||
} | |||
private static void checkLicense(License license) { | |||
requireNonNull(license, "license can't be null"); | |||
} | |||
private static String nullableTrimmedEmptyToNull(@Nullable String s) { | |||
if (s == null) { | |||
return null; | |||
} | |||
String v = s.trim(); | |||
return v.isEmpty() ? null : v; | |||
} | |||
private static String emptyToNull(String s) { | |||
return s.isEmpty() ? null : s; | |||
} | |||
@Immutable | |||
private static final class State { | |||
private final String currentEditionKey; | |||
private final PendingStatus pendingInstallationStatus; | |||
private final String pendingEditionKey; | |||
private final String pendingLicense; | |||
private final String installErrorMessage; | |||
public State(Builder builder) { | |||
this.currentEditionKey = builder.currentEditionKey; | |||
this.pendingInstallationStatus = builder.pendingInstallationStatus; | |||
this.pendingEditionKey = builder.pendingEditionKey; | |||
this.pendingLicense = builder.pendingLicense; | |||
this.installErrorMessage = builder.installErrorMessage; | |||
} | |||
public String getCurrentEditionKey() { | |||
return currentEditionKey; | |||
} | |||
public PendingStatus getPendingInstallationStatus() { | |||
return pendingInstallationStatus; | |||
} | |||
public String getPendingEditionKey() { | |||
return pendingEditionKey; | |||
} | |||
public String getPendingLicense() { | |||
return pendingLicense; | |||
} | |||
public String getInstallErrorMessage() { | |||
return installErrorMessage; | |||
} | |||
public static Builder newBuilder(PendingStatus pendingInstallationStatus) { | |||
return new Builder(pendingInstallationStatus); | |||
} | |||
public static Builder newBuilder(State from) { | |||
return newBuilder(from, from.getPendingInstallationStatus()); | |||
} | |||
public static Builder newBuilder(State from, PendingStatus newStatus) { | |||
return new Builder(newStatus) | |||
.setCurrentEditionKey(from.currentEditionKey) | |||
.setPendingEditionKey(from.pendingEditionKey) | |||
.setPendingLicense(from.pendingLicense) | |||
.setInstallErrorMessage(from.installErrorMessage); | |||
} | |||
private static class Builder { | |||
private PendingStatus pendingInstallationStatus; | |||
private String currentEditionKey; | |||
private String pendingEditionKey; | |||
private String pendingLicense; | |||
private String installErrorMessage; | |||
private Builder(PendingStatus pendingInstallationStatus) { | |||
this.pendingInstallationStatus = requireNonNull(pendingInstallationStatus); | |||
} | |||
public Builder setCurrentEditionKey(@Nullable String currentEditionKey) { | |||
this.currentEditionKey = currentEditionKey; | |||
return this; | |||
} | |||
public Builder setPendingEditionKey(@Nullable String pendingEditionKey) { | |||
this.pendingEditionKey = pendingEditionKey; | |||
return this; | |||
} | |||
public Builder setPendingLicense(@Nullable String pendingLicense) { | |||
this.pendingLicense = pendingLicense; | |||
return this; | |||
} | |||
public Builder setInstallErrorMessage(@Nullable String installErrorMessage) { | |||
this.installErrorMessage = installErrorMessage; | |||
return this; | |||
} | |||
public Builder commitPendingEditionKey() { | |||
this.currentEditionKey = pendingEditionKey; | |||
return this; | |||
} | |||
public Builder clearCurrentEditionKey() { | |||
this.currentEditionKey = null; | |||
return this; | |||
} | |||
public Builder clearPendingFields() { | |||
this.pendingEditionKey = null; | |||
this.pendingLicense = null; | |||
return this; | |||
} | |||
public Builder clearAutomaticInstallErrorMessage() { | |||
this.installErrorMessage = null; | |||
return this; | |||
} | |||
public State build() { | |||
return new State(this); | |||
} | |||
} | |||
} | |||
} |
@@ -1,23 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.server.edition; | |||
import javax.annotation.ParametersAreNonnullByDefault; |
@@ -1,126 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.ws; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.server.ws.Request; | |||
import org.sonar.api.server.ws.Response; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.server.edition.EditionManagementState; | |||
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.platform.WebServer; | |||
import org.sonar.server.plugins.edition.EditionInstaller; | |||
import org.sonar.server.user.UserSession; | |||
import org.sonar.server.ws.WsUtils; | |||
import org.sonarqube.ws.Editions; | |||
import static com.google.common.base.Preconditions.checkState; | |||
public class ApplyLicenseAction implements EditionsWsAction { | |||
private static final String PARAM_LICENSE = "license"; | |||
private final UserSession userSession; | |||
private final MutableEditionManagementState editionManagementState; | |||
private final EditionInstaller editionInstaller; | |||
private final WebServer webServer; | |||
@CheckForNull | |||
private final LicenseCommit licenseCommit; | |||
public ApplyLicenseAction(UserSession userSession, MutableEditionManagementState editionManagementState, | |||
EditionInstaller editionInstaller, WebServer webServer) { | |||
this(userSession, editionManagementState, editionInstaller, webServer, null); | |||
} | |||
public ApplyLicenseAction(UserSession userSession, MutableEditionManagementState editionManagementState, | |||
EditionInstaller editionInstaller, WebServer webServer, @Nullable LicenseCommit licenseCommit) { | |||
this.userSession = userSession; | |||
this.editionManagementState = editionManagementState; | |||
this.editionInstaller = editionInstaller; | |||
this.webServer = webServer; | |||
this.licenseCommit = licenseCommit; | |||
} | |||
@Override | |||
public void define(WebService.NewController controller) { | |||
WebService.NewAction action = controller.createAction("apply_license") | |||
.setSince("6.7") | |||
.setPost(true) | |||
.setDescription("Apply changes to SonarQube to match the specified license." + | |||
" Clear error message of previous automatic install of an edition, if there is any." + | |||
" Require 'Administer System' permission.") | |||
.setResponseExample(getClass().getResource("example-edition-apply_license.json")) | |||
.setHandler(this); | |||
action.createParam(PARAM_LICENSE) | |||
.setRequired(true) | |||
.setSince("6.7") | |||
.setDescription("the license"); | |||
} | |||
@Override | |||
public void handle(Request request, Response response) throws Exception { | |||
userSession.checkLoggedIn().checkIsSystemAdministrator(); | |||
if (editionManagementState.getPendingInstallationStatus() != EditionManagementState.PendingStatus.NONE) { | |||
throw BadRequestException.create("Can't apply a license when applying one is already in progress"); | |||
} | |||
String licenseParam = request.mandatoryParam(PARAM_LICENSE); | |||
License newLicense = License.parse(licenseParam).orElseThrow(() -> BadRequestException.create("The license provided is invalid")); | |||
if (!webServer.isStandalone()) { | |||
checkState(licenseCommit != null, "LicenseCommit instance not found. License-manager plugin should be installed."); | |||
setLicenseWithoutInstall(newLicense); | |||
} else if (editionInstaller.requiresInstallationChange(newLicense.getPluginKeys())) { | |||
editionInstaller.install(newLicense); | |||
} else { | |||
checkState(licenseCommit != null, | |||
"Can't decide edition does not require install if LicenseCommit instance is null. " + | |||
"License-manager plugin should be installed."); | |||
setLicenseWithoutInstall(newLicense); | |||
} | |||
WsUtils.writeProtobuf(buildResponse(), request, response); | |||
} | |||
private void setLicenseWithoutInstall(License newLicense) { | |||
try { | |||
licenseCommit.update(newLicense.getContent()); | |||
editionManagementState.newEditionWithoutInstall(newLicense.getEditionKey()); | |||
} catch (IllegalArgumentException e) { | |||
Loggers.get(ApplyLicenseAction.class).error("Failed to commit license", e); | |||
throw BadRequestException.create(e.getMessage()); | |||
} | |||
} | |||
private Editions.StatusResponse buildResponse() { | |||
Editions.StatusResponse.Builder builder = Editions.StatusResponse.newBuilder() | |||
.setNextEditionKey(editionManagementState.getPendingEditionKey().orElse("")) | |||
.setCurrentEditionKey(editionManagementState.getCurrentEditionKey().orElse("")) | |||
.setInstallationStatus(Editions.InstallationStatus.valueOf(editionManagementState.getPendingInstallationStatus().name())); | |||
editionManagementState.getInstallErrorMessage().ifPresent(builder::setInstallError); | |||
return builder.build(); | |||
} | |||
} |
@@ -1,56 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.ws; | |||
import org.sonar.api.server.ws.Request; | |||
import org.sonar.api.server.ws.Response; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.server.edition.MutableEditionManagementState; | |||
import org.sonar.server.user.UserSession; | |||
public class ClearErrorMessageAction implements EditionsWsAction { | |||
private final UserSession userSession; | |||
private final MutableEditionManagementState editionManagementState; | |||
public ClearErrorMessageAction(UserSession userSession, MutableEditionManagementState editionManagementState) { | |||
this.userSession = userSession; | |||
this.editionManagementState = editionManagementState; | |||
} | |||
@Override | |||
public void define(WebService.NewController controller) { | |||
controller.createAction("clear_error_message") | |||
.setSince("6.7") | |||
.setPost(true) | |||
.setDescription("Clear error message of last install of an edition (if any). Require 'Administer System' permission.") | |||
.setHandler(this); | |||
} | |||
@Override | |||
public void handle(Request request, Response response) throws Exception { | |||
userSession | |||
.checkLoggedIn() | |||
.checkIsSystemAdministrator(); | |||
editionManagementState.clearInstallErrorMessage(); | |||
response.noContent(); | |||
} | |||
} |
@@ -1,40 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.ws; | |||
import java.util.Arrays; | |||
import org.sonar.api.server.ws.WebService; | |||
public class EditionsWs implements WebService { | |||
private final EditionsWsAction[] actions; | |||
public EditionsWs(EditionsWsAction[] actions) { | |||
this.actions = actions; | |||
} | |||
@Override | |||
public void define(Context context) { | |||
NewController controller = context.createController("api/editions") | |||
.setSince("6.7") | |||
.setDescription("Manage SonarSource commercial editions."); | |||
Arrays.stream(actions).forEach(action -> action.define(controller)); | |||
controller.done(); | |||
} | |||
} |
@@ -1,28 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.ws; | |||
import org.sonar.server.ws.WsAction; | |||
/** | |||
* Marker interface for actions of {@link EditionsWs}. | |||
*/ | |||
public interface EditionsWsAction extends WsAction { | |||
} |
@@ -1,73 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.ws; | |||
import org.sonar.api.platform.Server; | |||
import org.sonar.api.server.ws.Request; | |||
import org.sonar.api.server.ws.Response; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.server.user.UserSession; | |||
import org.sonarqube.ws.Editions.FormDataResponse; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
public class FormDataAction implements EditionsWsAction { | |||
private final UserSession userSession; | |||
private final Server server; | |||
private final DbClient dbClient; | |||
public FormDataAction(UserSession userSession, Server server, DbClient dbClient) { | |||
this.userSession = userSession; | |||
this.server = server; | |||
this.dbClient = dbClient; | |||
} | |||
@Override | |||
public void define(WebService.NewController controller) { | |||
controller.createAction("form_data") | |||
.setSince("6.7") | |||
.setPost(false) | |||
.setDescription("Provide data to prefill license request forms: the server ID and the total number of lines of code.") | |||
.setResponseExample(getClass().getResource("example-edition-form_data.json")) | |||
.setInternal(true) | |||
.setHandler(this); | |||
} | |||
@Override | |||
public void handle(Request request, Response response) throws Exception { | |||
userSession | |||
.checkLoggedIn() | |||
.checkIsSystemAdministrator(); | |||
FormDataResponse responsePayload = FormDataResponse.newBuilder() | |||
.setNcloc(computeNcloc()) | |||
.setServerId(server.getId()) | |||
.build(); | |||
writeProtobuf(responsePayload, request, response); | |||
} | |||
private long computeNcloc() { | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
return dbClient.liveMeasureDao().sumNclocOfBiggestLongLivingBranch(dbSession); | |||
} | |||
} | |||
} |
@@ -1,127 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.ws; | |||
import java.util.Optional; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.server.ws.Request; | |||
import org.sonar.api.server.ws.Response; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.server.edition.EditionManagementState; | |||
import org.sonar.server.edition.License; | |||
import org.sonar.server.exceptions.BadRequestException; | |||
import org.sonar.server.platform.WebServer; | |||
import org.sonar.server.plugins.edition.EditionInstaller; | |||
import org.sonar.server.user.UserSession; | |||
import org.sonar.server.ws.WsUtils; | |||
import org.sonarqube.ws.Editions; | |||
import static java.util.Optional.ofNullable; | |||
import static org.sonarqube.ws.Editions.PreviewStatus.AUTOMATIC_INSTALL; | |||
import static org.sonarqube.ws.Editions.PreviewStatus.MANUAL_INSTALL; | |||
import static org.sonarqube.ws.Editions.PreviewStatus.NO_INSTALL; | |||
public class PreviewAction implements EditionsWsAction { | |||
private static final String PARAM_LICENSE = "license"; | |||
private final UserSession userSession; | |||
private final EditionManagementState editionManagementState; | |||
private final EditionInstaller editionInstaller; | |||
private final WebServer webServer; | |||
public PreviewAction(UserSession userSession, EditionManagementState editionManagementState, EditionInstaller editionInstaller, | |||
WebServer webServer) { | |||
this.userSession = userSession; | |||
this.editionManagementState = editionManagementState; | |||
this.editionInstaller = editionInstaller; | |||
this.webServer = webServer; | |||
} | |||
@Override | |||
public void define(WebService.NewController controller) { | |||
WebService.NewAction action = controller.createAction("preview") | |||
.setSince("6.7") | |||
.setPost(true) | |||
.setDescription("Preview the changes to SonarQube to match the specified license. Requires 'Administer System' permission.") | |||
.setResponseExample(getClass().getResource("example-edition-preview.json")) | |||
.setHandler(this); | |||
action.createParam(PARAM_LICENSE) | |||
.setRequired(true) | |||
.setSince("6.7") | |||
.setDescription("the license"); | |||
} | |||
@Override | |||
public void handle(Request request, Response response) throws Exception { | |||
userSession.checkLoggedIn().checkIsSystemAdministrator(); | |||
if (editionManagementState.getPendingInstallationStatus() != EditionManagementState.PendingStatus.NONE) { | |||
throw BadRequestException.create("Can't apply a license when applying one is already in progress"); | |||
} | |||
String licenseParam = request.mandatoryParam(PARAM_LICENSE); | |||
if (licenseParam.isEmpty()) { | |||
throw new IllegalArgumentException(String.format("The '%s' parameter is empty", PARAM_LICENSE)); | |||
} | |||
License newLicense = License.parse(licenseParam).orElseThrow(() -> BadRequestException.create("The license provided is invalid")); | |||
NextState nextState = computeNextState(newLicense); | |||
WsUtils.writeProtobuf(buildResponse(nextState), request, response); | |||
} | |||
private static Editions.PreviewResponse buildResponse(NextState nextState) { | |||
return Editions.PreviewResponse.newBuilder() | |||
.setNextEditionKey(nextState.getPendingEditionKey().orElse("")) | |||
.setPreviewStatus(nextState.getPreviewStatus()) | |||
.build(); | |||
} | |||
private NextState computeNextState(License newLicense) { | |||
if (!webServer.isStandalone() || !editionInstaller.requiresInstallationChange(newLicense.getPluginKeys())) { | |||
return new NextState(newLicense.getEditionKey(), NO_INSTALL); | |||
// this won't refresh the update center (uses cached state). Preview is called while typing (must be fast) | |||
// and anyway the status is refreshed when arriving at the marketplace page. | |||
} else if (editionInstaller.isOffline()) { | |||
return new NextState(newLicense.getEditionKey(), MANUAL_INSTALL); | |||
} else { | |||
return new NextState(newLicense.getEditionKey(), AUTOMATIC_INSTALL); | |||
} | |||
} | |||
private static final class NextState { | |||
private final String pendingEditionKey; | |||
private final Editions.PreviewStatus previewStatus; | |||
private NextState(@Nullable String pendingEditionKey, Editions.PreviewStatus previewStatus) { | |||
this.pendingEditionKey = pendingEditionKey; | |||
this.previewStatus = previewStatus; | |||
} | |||
Optional<String> getPendingEditionKey() { | |||
return ofNullable(pendingEditionKey); | |||
} | |||
Editions.PreviewStatus getPreviewStatus() { | |||
return previewStatus; | |||
} | |||
} | |||
} |
@@ -1,63 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.ws; | |||
import org.sonar.api.server.ws.Request; | |||
import org.sonar.api.server.ws.Response; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.server.edition.EditionManagementState; | |||
import org.sonar.server.user.UserSession; | |||
import org.sonar.server.ws.WsUtils; | |||
import org.sonarqube.ws.Editions; | |||
public class StatusAction implements EditionsWsAction { | |||
private final UserSession userSession; | |||
private final EditionManagementState editionManagementState; | |||
public StatusAction(UserSession userSession, EditionManagementState editionManagementState) { | |||
this.userSession = userSession; | |||
this.editionManagementState = editionManagementState; | |||
} | |||
@Override | |||
public void define(WebService.NewController controller) { | |||
controller.createAction("status") | |||
.setSince("6.7") | |||
.setPost(false) | |||
.setDescription("Provide status of SonarSource commercial edition of the current SonarQube. Requires 'Administer System' permission.") | |||
.setResponseExample(getClass().getResource("example-edition-status.json")) | |||
.setHandler(this); | |||
} | |||
@Override | |||
public void handle(Request request, Response response) throws Exception { | |||
userSession | |||
.checkLoggedIn() | |||
.checkIsSystemAdministrator(); | |||
Editions.StatusResponse.Builder responseBuilder = Editions.StatusResponse.newBuilder() | |||
.setCurrentEditionKey(editionManagementState.getCurrentEditionKey().orElse("")) | |||
.setNextEditionKey(editionManagementState.getPendingEditionKey().orElse("")) | |||
.setInstallationStatus(Editions.InstallationStatus.valueOf(editionManagementState.getPendingInstallationStatus().name())); | |||
editionManagementState.getInstallErrorMessage().ifPresent(responseBuilder::setInstallError); | |||
WsUtils.writeProtobuf(responseBuilder.build(), request, response); | |||
} | |||
} |
@@ -1,66 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.ws; | |||
import org.sonar.api.server.ws.Request; | |||
import org.sonar.api.server.ws.Response; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.server.edition.EditionManagementState.PendingStatus; | |||
import org.sonar.server.edition.MutableEditionManagementState; | |||
import org.sonar.server.exceptions.BadRequestException; | |||
import org.sonar.server.plugins.edition.EditionInstaller; | |||
import org.sonar.server.user.UserSession; | |||
import static org.sonar.server.edition.EditionManagementState.PendingStatus.NONE; | |||
import static org.sonar.server.edition.EditionManagementState.PendingStatus.UNINSTALL_IN_PROGRESS; | |||
public class UninstallAction implements EditionsWsAction { | |||
private final UserSession userSession; | |||
private final MutableEditionManagementState mutableEditionManagementState; | |||
private final EditionInstaller editionInstaller; | |||
public UninstallAction(UserSession userSession, MutableEditionManagementState mutableEditionManagementState, EditionInstaller editionInstaller) { | |||
this.userSession = userSession; | |||
this.mutableEditionManagementState = mutableEditionManagementState; | |||
this.editionInstaller = editionInstaller; | |||
} | |||
@Override | |||
public void define(WebService.NewController controller) { | |||
controller.createAction("uninstall") | |||
.setSince("6.7") | |||
.setPost(true) | |||
.setDescription("Uninstall the currently installed edition. Requires 'Administer System' permission.") | |||
.setHandler(this); | |||
} | |||
@Override | |||
public void handle(Request request, Response response) throws Exception { | |||
userSession.checkLoggedIn().checkIsSystemAdministrator(); | |||
PendingStatus status = mutableEditionManagementState.getPendingInstallationStatus(); | |||
if (status != NONE && status != UNINSTALL_IN_PROGRESS) { | |||
throw BadRequestException.create("Uninstall of the current edition is not allowed when install of an edition is in progress"); | |||
} | |||
editionInstaller.uninstall(); | |||
mutableEditionManagementState.uninstall(); | |||
response.noContent(); | |||
} | |||
} |
@@ -1,23 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.server.edition.ws; | |||
import javax.annotation.ParametersAreNonnullByDefault; |
@@ -1,37 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.license; | |||
import org.sonar.api.ExtensionPoint; | |||
@ExtensionPoint | |||
public interface LicenseCommit { | |||
/** | |||
* Installs the specified license on SonarQube. | |||
* | |||
* @throws IllegalArgumentException if new license is invalid or cannot be read | |||
*/ | |||
void update(String newLicense); | |||
/** | |||
* Remove any license installed on SonarQube. | |||
*/ | |||
void delete(); | |||
} |
@@ -1,23 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.server.license; | |||
import javax.annotation.ParametersAreNonnullByDefault; |
@@ -27,12 +27,6 @@ import java.io.File; | |||
*/ | |||
public interface ServerFileSystem { | |||
/** | |||
* Directory that contains the persisted data to be kept on restarts and upgrades. | |||
* @return an existing directory | |||
*/ | |||
File getDataDir(); | |||
/** | |||
* Root directory of the server installation | |||
* @return an existing directory | |||
@@ -58,13 +52,6 @@ public interface ServerFileSystem { | |||
*/ | |||
File getDownloadedPluginsDir(); | |||
/** | |||
* Directory of commercial plugins downloaded as part of the installation of an edition. Files | |||
* will be moved to {@link #getInstalledPluginsDir()} on startup. | |||
* @return a directory which may or not exist | |||
*/ | |||
File getEditionDownloadedPluginsDir(); | |||
/** | |||
* Directory of currently installed plugins. Used at startup. | |||
* @return a directory which may or not exist | |||
@@ -85,9 +72,4 @@ public interface ServerFileSystem { | |||
*/ | |||
File getUninstalledPluginsDir(); | |||
/** | |||
* Directory where plugins that are part of an edition and are to be uninstalled are moved to. | |||
* @return a directory which may or not exist | |||
*/ | |||
File getEditionUninstalledPluginsDir(); | |||
} |
@@ -36,18 +36,15 @@ public class ServerFileSystemImpl implements ServerFileSystem, org.sonar.api.pla | |||
private final File homeDir; | |||
private final File tempDir; | |||
private final File dataDir; | |||
private final File deployDir; | |||
private final File uninstallDir; | |||
private final File editionUninstallDir; | |||
public ServerFileSystemImpl(Configuration config) { | |||
this.homeDir = new File(config.get(PATH_HOME.getKey()).get()); | |||
this.tempDir = new File(config.get(PATH_TEMP.getKey()).get()); | |||
this.dataDir = new File(config.get(PATH_DATA.getKey()).get()); | |||
this.deployDir = new File(this.dataDir, TomcatContexts.WEB_DEPLOY_PATH_RELATIVE_TO_DATA_DIR); | |||
File dataDir = new File(config.get(PATH_DATA.getKey()).get()); | |||
this.deployDir = new File(dataDir, TomcatContexts.WEB_DEPLOY_PATH_RELATIVE_TO_DATA_DIR); | |||
this.uninstallDir = new File(getTempDir(), "uninstalled-plugins"); | |||
this.editionUninstallDir = new File(getTempDir(), "uninstalled-edition-plugins"); | |||
} | |||
@Override | |||
@@ -70,11 +67,6 @@ public class ServerFileSystemImpl implements ServerFileSystem, org.sonar.api.pla | |||
return tempDir; | |||
} | |||
@Override | |||
public File getDataDir() { | |||
return dataDir; | |||
} | |||
@Override | |||
public File getDeployedPluginsDir() { | |||
return new File(deployDir, "plugins"); | |||
@@ -85,11 +77,6 @@ public class ServerFileSystemImpl implements ServerFileSystem, org.sonar.api.pla | |||
return new File(getHomeDir(), "extensions/downloads"); | |||
} | |||
@Override | |||
public File getEditionDownloadedPluginsDir() { | |||
return new File(getHomeDir(), "extensions/new-edition"); | |||
} | |||
@Override | |||
public File getInstalledPluginsDir() { | |||
return new File(getHomeDir(), "extensions/plugins"); | |||
@@ -105,9 +92,4 @@ public class ServerFileSystemImpl implements ServerFileSystem, org.sonar.api.pla | |||
return uninstallDir; | |||
} | |||
@Override | |||
public File getEditionUninstalledPluginsDir() { | |||
return editionUninstallDir; | |||
} | |||
} |
@@ -32,6 +32,7 @@ import org.sonar.ce.CeModule; | |||
import org.sonar.ce.notification.ReportAnalysisFailureNotificationModule; | |||
import org.sonar.ce.settings.ProjectConfigurationFactory; | |||
import org.sonar.core.component.DefaultResourceTypes; | |||
import org.sonar.core.extension.CoreExtensionsInstaller; | |||
import org.sonar.core.platform.ComponentContainer; | |||
import org.sonar.core.timemachine.Periods; | |||
import org.sonar.server.authentication.AuthenticationModule; | |||
@@ -56,8 +57,6 @@ import org.sonar.server.debt.DebtRulesXMLImporter; | |||
import org.sonar.server.duplication.ws.DuplicationsParser; | |||
import org.sonar.server.duplication.ws.DuplicationsWs; | |||
import org.sonar.server.duplication.ws.ShowResponseBuilder; | |||
import org.sonar.server.edition.EditionsWsModule; | |||
import org.sonar.server.edition.FinalizeEditionChange; | |||
import org.sonar.server.email.ws.EmailsWsModule; | |||
import org.sonar.server.es.IndexCreator; | |||
import org.sonar.server.es.IndexDefinitions; | |||
@@ -67,7 +66,6 @@ import org.sonar.server.es.metadata.EsDbCompatibilityImpl; | |||
import org.sonar.server.es.metadata.MetadataIndex; | |||
import org.sonar.server.es.metadata.MetadataIndexDefinition; | |||
import org.sonar.server.event.NewAlerts; | |||
import org.sonar.core.extension.CoreExtensionsInstaller; | |||
import org.sonar.server.favorite.FavoriteModule; | |||
import org.sonar.server.health.NodeHealthModule; | |||
import org.sonar.server.issue.AddTagsAction; | |||
@@ -138,10 +136,6 @@ import org.sonar.server.platform.ws.UpgradesAction; | |||
import org.sonar.server.plugins.PluginDownloader; | |||
import org.sonar.server.plugins.PluginUninstaller; | |||
import org.sonar.server.plugins.ServerExtensionInstaller; | |||
import org.sonar.server.plugins.edition.EditionInstaller; | |||
import org.sonar.server.plugins.edition.EditionInstallerExecutor; | |||
import org.sonar.server.plugins.edition.EditionPluginDownloader; | |||
import org.sonar.server.plugins.edition.EditionPluginUninstaller; | |||
import org.sonar.server.plugins.privileged.PrivilegedPluginsBootstraper; | |||
import org.sonar.server.plugins.privileged.PrivilegedPluginsStopper; | |||
import org.sonar.server.plugins.ws.AvailableAction; | |||
@@ -284,12 +278,6 @@ public class PlatformLevel4 extends PlatformLevel { | |||
IndexDefinitions.class, | |||
WebPagesFilter.class, | |||
// edition | |||
EditionInstaller.class, | |||
EditionPluginDownloader.class, | |||
EditionInstallerExecutor.class, | |||
EditionPluginUninstaller.class, | |||
// batch | |||
BatchWsModule.class, | |||
@@ -556,9 +544,6 @@ public class PlatformLevel4 extends PlatformLevel { | |||
CeModule.class, | |||
CeWsModule.class, | |||
// SonarSource editions | |||
EditionsWsModule.class, | |||
InternalPropertiesImpl.class, | |||
ProjectConfigurationFactory.class, | |||
@@ -582,9 +567,6 @@ public class PlatformLevel4 extends PlatformLevel { | |||
add(TelemetryDataLoader.class); | |||
addIfStartupLeader(TelemetryDaemon.class, TelemetryClient.class); | |||
// edition | |||
addIfStartupLeader(FinalizeEditionChange.class); | |||
// system info | |||
addIfCluster(WebSystemInfoModule.forClusterMode()).otherwiseAdd(WebSystemInfoModule.forStandaloneMode()); | |||
@@ -1,91 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.plugins; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import org.apache.commons.io.FileUtils; | |||
import org.picocontainer.Startable; | |||
import org.sonar.core.platform.PluginInfo; | |||
import org.sonar.core.util.stream.MoreCollectors; | |||
import static java.lang.String.format; | |||
import static org.apache.commons.io.FileUtils.forceMkdir; | |||
public abstract class AbstractPluginUninstaller implements Startable { | |||
private static final String PLUGIN_EXTENSION = "jar"; | |||
private final ServerPluginRepository serverPluginRepository; | |||
private final File uninstallDir; | |||
protected AbstractPluginUninstaller(ServerPluginRepository serverPluginRepository, File uninstallDir) { | |||
this.serverPluginRepository = serverPluginRepository; | |||
this.uninstallDir = uninstallDir; | |||
} | |||
@Override | |||
public void start() { | |||
try { | |||
forceMkdir(uninstallDir); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Fail to create the directory: " + uninstallDir, e); | |||
} | |||
} | |||
@Override | |||
public void stop() { | |||
// Nothing to do | |||
} | |||
public void uninstall(String pluginKey) { | |||
ensurePluginIsInstalled(pluginKey); | |||
serverPluginRepository.uninstall(pluginKey, uninstallDir); | |||
} | |||
public void cancelUninstalls() { | |||
serverPluginRepository.cancelUninstalls(uninstallDir); | |||
} | |||
/** | |||
* @return the list of plugins to be uninstalled as {@link PluginInfo} instances | |||
*/ | |||
public Collection<PluginInfo> getUninstalledPlugins() { | |||
return listJarFiles(uninstallDir) | |||
.stream() | |||
.map(PluginInfo::create) | |||
.collect(MoreCollectors.toList()); | |||
} | |||
private static Collection<File> listJarFiles(File dir) { | |||
if (dir.exists()) { | |||
return FileUtils.listFiles(dir, new String[] {PLUGIN_EXTENSION}, false); | |||
} | |||
return Collections.emptyList(); | |||
} | |||
private void ensurePluginIsInstalled(String key) { | |||
if (!serverPluginRepository.hasPlugin(key)) { | |||
throw new IllegalArgumentException(format("Plugin [%s] is not installed", key)); | |||
} | |||
} | |||
} |
@@ -19,10 +19,72 @@ | |||
*/ | |||
package org.sonar.server.plugins; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import org.apache.commons.io.FileUtils; | |||
import org.picocontainer.Startable; | |||
import org.sonar.core.platform.PluginInfo; | |||
import org.sonar.core.util.stream.MoreCollectors; | |||
import org.sonar.server.platform.ServerFileSystem; | |||
public class PluginUninstaller extends AbstractPluginUninstaller { | |||
import static java.lang.String.format; | |||
import static org.apache.commons.io.FileUtils.forceMkdir; | |||
public class PluginUninstaller implements Startable { | |||
private static final String PLUGIN_EXTENSION = "jar"; | |||
private final ServerPluginRepository serverPluginRepository; | |||
private final File uninstallDir; | |||
public PluginUninstaller(ServerPluginRepository serverPluginRepository, ServerFileSystem fs) { | |||
super(serverPluginRepository, fs.getUninstalledPluginsDir()); | |||
this.serverPluginRepository = serverPluginRepository; | |||
this.uninstallDir = fs.getUninstalledPluginsDir(); | |||
} | |||
private static Collection<File> listJarFiles(File dir) { | |||
if (dir.exists()) { | |||
return FileUtils.listFiles(dir, new String[] {PLUGIN_EXTENSION}, false); | |||
} | |||
return Collections.emptyList(); | |||
} | |||
@Override | |||
public void start() { | |||
try { | |||
forceMkdir(uninstallDir); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Fail to create the directory: " + uninstallDir, e); | |||
} | |||
} | |||
@Override | |||
public void stop() { | |||
// Nothing to do | |||
} | |||
public void uninstall(String pluginKey) { | |||
ensurePluginIsInstalled(pluginKey); | |||
serverPluginRepository.uninstall(pluginKey, uninstallDir); | |||
} | |||
public void cancelUninstalls() { | |||
serverPluginRepository.cancelUninstalls(uninstallDir); | |||
} | |||
/** | |||
* @return the list of plugins to be uninstalled as {@link PluginInfo} instances | |||
*/ | |||
public Collection<PluginInfo> getUninstalledPlugins() { | |||
return listJarFiles(uninstallDir) | |||
.stream() | |||
.map(PluginInfo::create) | |||
.collect(MoreCollectors.toList()); | |||
} | |||
private void ensurePluginIsInstalled(String key) { | |||
if (!serverPluginRepository.hasPlugin(key)) { | |||
throw new IllegalArgumentException(format("Plugin [%s] is not installed", key)); | |||
} | |||
} | |||
} |
@@ -102,7 +102,6 @@ public class ServerPluginRepository implements PluginRepository, Startable { | |||
public void start() { | |||
loadPreInstalledPlugins(); | |||
moveDownloadedPlugins(); | |||
moveDownloadedEditionPlugins(); | |||
unloadIncompatiblePlugins(); | |||
logInstalledPlugins(); | |||
loadInstances(); | |||
@@ -149,14 +148,6 @@ public class ServerPluginRepository implements PluginRepository, Startable { | |||
} | |||
} | |||
private void moveDownloadedEditionPlugins() { | |||
if (fs.getEditionDownloadedPluginsDir().exists()) { | |||
for (File sourceFile : listJarFiles(fs.getEditionDownloadedPluginsDir())) { | |||
overrideAndRegisterPlugin(sourceFile); | |||
} | |||
} | |||
} | |||
private void registerPluginInfo(PluginInfo info) { | |||
String pluginKey = info.getKey(); | |||
if (blacklistedPluginKeys.contains(pluginKey)) { |
@@ -1,149 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.plugins.edition; | |||
import com.google.common.base.Optional; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.concurrent.Semaphore; | |||
import java.util.stream.Collectors; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.core.platform.PluginInfo; | |||
import org.sonar.server.edition.License; | |||
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 static final Logger LOG = Loggers.get(EditionInstaller.class); | |||
private final Semaphore semaphore = new Semaphore(1); | |||
private final EditionInstallerExecutor executor; | |||
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, UpdateCenterMatrixFactory updateCenterMatrixFactory, | |||
MutableEditionManagementState editionManagementState) { | |||
this.editionPluginDownloader = editionDownloader; | |||
this.editionPluginUninstaller = editionPluginUninstaller; | |||
this.pluginRepository = pluginRepository; | |||
this.executor = executor; | |||
this.updateCenterMatrixFactory = updateCenterMatrixFactory; | |||
this.editionManagementState = editionManagementState; | |||
} | |||
/** | |||
* 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. | |||
* | |||
* @throws IllegalStateException if an installation is already in progress | |||
*/ | |||
public void install(License newLicense) { | |||
if (semaphore.tryAcquire()) { | |||
try { | |||
Optional<UpdateCenter> updateCenter = updateCenterMatrixFactory.getUpdateCenter(true); | |||
if (!updateCenter.isPresent()) { | |||
LOG.info("Installation of edition '{}' needs to be done manually", newLicense.getEditionKey()); | |||
editionManagementState.startManualInstall(newLicense); | |||
return; | |||
} | |||
editionManagementState.startAutomaticInstall(newLicense); | |||
executor.execute(() -> asyncInstall(newLicense, updateCenter.get())); | |||
} catch (RuntimeException e) { | |||
semaphore.release(); | |||
throw e; | |||
} | |||
} else { | |||
throw new IllegalStateException("Another installation of an edition is already running"); | |||
} | |||
} | |||
public void uninstall() { | |||
Map<String, PluginInfo> pluginInfosByKeys = pluginRepository.getPluginInfosByKeys(); | |||
Set<String> pluginsToRemove = pluginsToRemove(Collections.emptySet(), pluginInfosByKeys.values()); | |||
uninstallPlugins(pluginsToRemove); | |||
} | |||
/** | |||
* Check if the update center is disabled or unreachable. It uses the cached status (it doesn't refresh), | |||
* to be a cost-free check. | |||
*/ | |||
public boolean isOffline() { | |||
return !updateCenterMatrixFactory.getUpdateCenter(false).isPresent(); | |||
} | |||
public boolean requiresInstallationChange(Set<String> editionPluginKeys) { | |||
Map<String, PluginInfo> pluginInfosByKeys = pluginRepository.getPluginInfosByKeys(); | |||
return !pluginsToInstall(editionPluginKeys, pluginInfosByKeys.keySet()).isEmpty() | |||
|| !pluginsToRemove(editionPluginKeys, pluginInfosByKeys.values()).isEmpty(); | |||
} | |||
private void asyncInstall(License newLicense, UpdateCenter updateCenter) { | |||
try { | |||
Set<String> editionPluginKeys = newLicense.getPluginKeys(); | |||
Map<String, PluginInfo> pluginInfosByKeys = pluginRepository.getPluginInfosByKeys(); | |||
Set<String> pluginsToRemove = pluginsToRemove(editionPluginKeys, pluginInfosByKeys.values()); | |||
Set<String> pluginsToInstall = pluginsToInstall(editionPluginKeys, pluginInfosByKeys.keySet()); | |||
LOG.info("Installing edition '{}', download: {}, remove: {}", | |||
newLicense.getEditionKey(), pluginsToInstall, pluginsToRemove); | |||
editionPluginDownloader.downloadEditionPlugins(pluginsToInstall, updateCenter); | |||
uninstallPlugins(pluginsToRemove); | |||
editionManagementState.automaticInstallReady(); | |||
} catch (Throwable t) { | |||
LOG.error("Failed to install edition {} with plugins {}", newLicense.getEditionKey(), newLicense.getPluginKeys(), t); | |||
editionManagementState.installFailed(t.getMessage()); | |||
} finally { | |||
semaphore.release(); | |||
} | |||
} | |||
private void uninstallPlugins(Set<String> pluginsToRemove) { | |||
pluginsToRemove.stream().forEach(editionPluginUninstaller::uninstall); | |||
} | |||
private static Set<String> pluginsToInstall(Set<String> editionPluginKeys, Set<String> installedPluginKeys) { | |||
return editionPluginKeys.stream() | |||
.filter(p -> !installedPluginKeys.contains(p)) | |||
.collect(Collectors.toSet()); | |||
} | |||
private static Set<String> pluginsToRemove(Set<String> editionPluginKeys, Collection<PluginInfo> installedPluginInfos) { | |||
Set<String> installedCommercialPluginKeys = installedPluginInfos.stream() | |||
.filter(EditionBundledPlugins::isEditionBundled) | |||
.map(PluginInfo::getKey) | |||
.collect(Collectors.toSet()); | |||
return installedCommercialPluginKeys.stream() | |||
.filter(p -> !editionPluginKeys.contains(p)) | |||
.collect(Collectors.toSet()); | |||
} | |||
} |
@@ -1,45 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.plugins.edition; | |||
import com.google.common.util.concurrent.ThreadFactoryBuilder; | |||
import java.util.concurrent.ExecutorService; | |||
import java.util.concurrent.Executors; | |||
import java.util.concurrent.ThreadFactory; | |||
import org.sonar.server.util.AbstractStoppableExecutorService; | |||
public class EditionInstallerExecutor extends AbstractStoppableExecutorService<ExecutorService> { | |||
public EditionInstallerExecutor() { | |||
super(createExecutor()); | |||
} | |||
@Override | |||
public void execute(Runnable r) { | |||
delegate.submit(r); | |||
} | |||
private static ExecutorService createExecutor() { | |||
ThreadFactory threadFactory = new ThreadFactoryBuilder() | |||
.setDaemon(true) | |||
.setNameFormat("edition-installation-%d") | |||
.build(); | |||
return Executors.newSingleThreadExecutor(threadFactory); | |||
} | |||
} |
@@ -1,108 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.plugins.edition; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.net.URI; | |||
import java.net.URISyntaxException; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import org.sonar.api.utils.HttpDownloader; | |||
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.updatecenter.common.Release; | |||
import org.sonar.updatecenter.common.UpdateCenter; | |||
import org.sonar.updatecenter.common.Version; | |||
import static org.apache.commons.io.FileUtils.toFile; | |||
import static org.apache.commons.lang.StringUtils.substringAfterLast; | |||
public class EditionPluginDownloader { | |||
private static final Logger LOG = Loggers.get(EditionPluginDownloader.class); | |||
private static final String PLUGIN_EXTENSION = "jar"; | |||
private final Path tmpDir; | |||
private final Path downloadDir; | |||
private final HttpDownloader downloader; | |||
public EditionPluginDownloader(HttpDownloader downloader, ServerFileSystem fileSystem) { | |||
this.downloadDir = fileSystem.getEditionDownloadedPluginsDir().toPath(); | |||
this.downloader = downloader; | |||
this.tmpDir = downloadDir.resolveSibling(downloadDir.getFileName() + "_tmp"); | |||
} | |||
public void downloadEditionPlugins(Set<String> pluginKeys, UpdateCenter updateCenter) { | |||
try { | |||
Set<Release> pluginsToInstall = new HashSet<>(); | |||
for (String pluginKey : pluginKeys) { | |||
pluginsToInstall.addAll(updateCenter.findInstallablePlugins(pluginKey, Version.create(""))); | |||
} | |||
FileUtils.deleteDirectory(tmpDir); | |||
Files.createDirectories(tmpDir); | |||
for (Release r : pluginsToInstall) { | |||
download(r); | |||
} | |||
FileUtils.deleteDirectory(downloadDir); | |||
Files.move(tmpDir, downloadDir); | |||
} catch (IOException e) { | |||
throw new IllegalStateException(e.getMessage(), e); | |||
} finally { | |||
FileUtils.deleteQuietly(tmpDir); | |||
} | |||
} | |||
protected void download(Release release) { | |||
try { | |||
LOG.info("Downloading plugin: {}", release.getArtifact().getKey()); | |||
downloadRelease(release); | |||
} catch (Exception e) { | |||
String message = String.format("Fail to download the plugin (%s, version %s) from %s (error is : %s)", | |||
release.getArtifact().getKey(), release.getVersion().getName(), release.getDownloadUrl(), e.getMessage()); | |||
LOG.debug(message, e); | |||
throw new IllegalStateException(message, e); | |||
} | |||
} | |||
private void downloadRelease(Release release) throws URISyntaxException, IOException { | |||
String url = release.getDownloadUrl(); | |||
URI uri = new URI(url); | |||
if (url.startsWith("file:")) { | |||
// used for tests | |||
File file = toFile(uri.toURL()); | |||
Files.copy(file.toPath(), tmpDir.resolve(file.getName())); | |||
} else { | |||
String filename = substringAfterLast(uri.getPath(), "/"); | |||
if (!filename.endsWith("." + PLUGIN_EXTENSION)) { | |||
filename = release.getKey() + "-" + release.getVersion() + "." + PLUGIN_EXTENSION; | |||
} | |||
Path targetFile = tmpDir.resolve(filename); | |||
downloader.download(uri, targetFile.toFile()); | |||
} | |||
} | |||
} |
@@ -1,31 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.plugins.edition; | |||
import org.sonar.server.platform.ServerFileSystem; | |||
import org.sonar.server.plugins.AbstractPluginUninstaller; | |||
import org.sonar.server.plugins.ServerPluginRepository; | |||
public class EditionPluginUninstaller extends AbstractPluginUninstaller { | |||
public EditionPluginUninstaller(ServerPluginRepository serverPluginRepository, ServerFileSystem fs) { | |||
super(serverPluginRepository, fs.getEditionUninstalledPluginsDir()); | |||
} | |||
} |
@@ -1,5 +0,0 @@ | |||
{ | |||
"currentEditionKey": "", | |||
"installationStatus": "AUTOMATIC_IN_PROGRESS", | |||
"nextEditionKey": "developer-edition" | |||
} |
@@ -1,4 +0,0 @@ | |||
{ | |||
"serverId": "uuid", | |||
"ncloc": 12345 | |||
} |
@@ -1,4 +0,0 @@ | |||
{ | |||
"nextEditionKey": "developer-edition", | |||
"previewStatus": "AUTOMATIC_INSTALL" | |||
} |
@@ -1,5 +0,0 @@ | |||
{ | |||
"currentEditionKey": "", | |||
"installationStatus": "AUTOMATIC_READY", | |||
"nextEditionKey": "developer-edition" | |||
} |
@@ -1,39 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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 org.junit.Test; | |||
import org.sonar.core.platform.ComponentContainer; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonar.core.platform.ComponentContainer.COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER; | |||
public class EditionsWsModuleTest { | |||
private EditionsWsModule underTest = new EditionsWsModule(); | |||
@Test | |||
public void verify_component_count() { | |||
ComponentContainer container = new ComponentContainer(); | |||
underTest.configure(container); | |||
assertThat(container.getPicoContainer().getComponentAdapters()) | |||
.hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 8); | |||
} | |||
} |
@@ -1,275 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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(); | |||
} | |||
} |
@@ -1,150 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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 com.google.common.collect.ImmutableSet; | |||
import java.io.IOException; | |||
import java.io.StringWriter; | |||
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 static org.assertj.core.api.Assertions.assertThat; | |||
public class LicenseTest { | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
@Test | |||
public void constructor_fails_if_editionKey_is_null() { | |||
expectedException.expect(NullPointerException.class); | |||
expectedException.expectMessage("editionKey can't be null"); | |||
new License(null, Collections.emptyList(), "content"); | |||
} | |||
@Test | |||
public void constructor_fails_if_editionKey_is_empty() { | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("editionKey can't be empty"); | |||
new License("", Collections.emptyList(), "content"); | |||
} | |||
@Test | |||
public void constructor_fails_if_plugins_is_null() { | |||
expectedException.expect(NullPointerException.class); | |||
new License("edition-key", null, "content"); | |||
} | |||
@Test | |||
public void constructor_fails_if_content_is_null() { | |||
expectedException.expect(NullPointerException.class); | |||
expectedException.expectMessage("content can't be null"); | |||
new License("edition-key", Collections.emptyList(), null); | |||
} | |||
@Test | |||
public void constructor_fails_if_content_is_empty() { | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("content can't be empty"); | |||
new License("edition-key", Collections.emptyList(), ""); | |||
} | |||
@Test | |||
public void parse_returns_empty_if_license_is_invalid_string() { | |||
assertThat(License.parse("trash")).isEmpty(); | |||
} | |||
@Test | |||
public void parse_succeeds() throws IOException { | |||
Properties props = new Properties(); | |||
props.setProperty("Plugins", "plugin1,plugin2"); | |||
props.setProperty("Edition", "dev"); | |||
StringWriter writer = new StringWriter(); | |||
props.store(writer, ""); | |||
byte[] encoded = Base64.getEncoder().encode(writer.toString().getBytes()); | |||
Optional<License> license = License.parse(new String(encoded)); | |||
assertThat(license).isPresent(); | |||
assertThat(license.get().getEditionKey()).isEqualTo("dev"); | |||
assertThat(license.get().getPluginKeys()).containsOnly("plugin1", "plugin2"); | |||
} | |||
@Test | |||
public void parse_is_empty_if_license_has_no_plugin() throws IOException { | |||
Properties props = new Properties(); | |||
props.setProperty("Plugins", ""); | |||
props.setProperty("Edition", "dev"); | |||
StringWriter writer = new StringWriter(); | |||
props.store(writer, ""); | |||
byte[] encoded = Base64.getEncoder().encode(writer.toString().getBytes()); | |||
Optional<License> license = License.parse(new String(encoded)); | |||
assertThat(license).isEmpty(); | |||
} | |||
@Test | |||
public void parse_is_empty_if_license_has_empty_edition_key() throws IOException { | |||
Properties props = new Properties(); | |||
props.setProperty("Plugins", "p1"); | |||
props.setProperty("Edition", ""); | |||
StringWriter writer = new StringWriter(); | |||
props.store(writer, ""); | |||
byte[] encoded = Base64.getEncoder().encode(writer.toString().getBytes()); | |||
Optional<License> license = License.parse(new String(encoded)); | |||
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 = License.parse(new String(encoded)); | |||
assertThat(license).isEmpty(); | |||
} | |||
@Test | |||
public void verify_getters() { | |||
ImmutableSet<String> pluginKeys = ImmutableSet.of("a", "b", "c"); | |||
License underTest = new License("edition-key", pluginKeys, "content"); | |||
assertThat(underTest.getEditionKey()).isEqualTo("edition-key"); | |||
assertThat(underTest.getPluginKeys()).isEqualTo(pluginKeys); | |||
assertThat(underTest.getContent()).isEqualTo("content"); | |||
} | |||
} |
@@ -1,356 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.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.Optional; | |||
import java.util.Properties; | |||
import javax.annotation.Nullable; | |||
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.platform.WebServer; | |||
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.Editions; | |||
import org.sonarqube.ws.Editions.StatusResponse; | |||
import static java.util.Collections.singleton; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.ArgumentMatchers.any; | |||
import static org.mockito.Mockito.doThrow; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.times; | |||
import static org.mockito.Mockito.verify; | |||
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.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 WebServer webServer = mock(WebServer.class); | |||
private ApplyLicenseAction underTest = new ApplyLicenseAction(userSessionRule, mutableEditionManagementState, editionInstaller, | |||
webServer, licenseCommit); | |||
private WsActionTester actionTester = new WsActionTester(underTest); | |||
@Test | |||
public void verify_definition() { | |||
WebService.Action def = actionTester.getDef(); | |||
assertThat(def.key()).isEqualTo("apply_license"); | |||
assertThat(def.since()).isEqualTo("6.7"); | |||
assertThat(def.isPost()).isTrue(); | |||
assertThat(def.isInternal()).isFalse(); | |||
assertThat(def.description()).isNotEmpty(); | |||
assertThat(def.params()).hasSize(1); | |||
WebService.Param licenseParam = def.param("license"); | |||
assertThat(licenseParam.isRequired()).isTrue(); | |||
assertThat(licenseParam.description()).isNotEmpty(); | |||
} | |||
@Test | |||
public void request_fails_if_user_not_logged_in() { | |||
userSessionRule.anonymous(); | |||
TestRequest request = actionTester.newRequest(); | |||
expectedException.expect(UnauthorizedException.class); | |||
expectedException.expectMessage("Authentication is required"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void request_fails_if_user_is_not_system_administer() { | |||
userSessionRule.logIn(); | |||
TestRequest request = actionTester.newRequest(); | |||
expectedException.expect(ForbiddenException.class); | |||
expectedException.expectMessage("Insufficient privileges"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void request_fails_if_license_param_is_not_provided() { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(mutableEditionManagementState.getPendingInstallationStatus()).thenReturn(NONE); | |||
TestRequest request = actionTester.newRequest(); | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("The 'license' parameter is missing"); | |||
request.execute(); | |||
} | |||
@Test | |||
@UseDataProvider("notNonePendingInstallationStatuses") | |||
public void request_fails_with_BadRequestException_is_pendingStatus_is_not_NONE(EditionManagementState.PendingStatus notNone) { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(mutableEditionManagementState.getCurrentEditionKey()).thenReturn(Optional.empty()); | |||
when(mutableEditionManagementState.getPendingEditionKey()).thenReturn(Optional.empty()); | |||
when(mutableEditionManagementState.getPendingInstallationStatus()).thenReturn(notNone); | |||
TestRequest request = actionTester.newRequest() | |||
.setParam(PARAM_LICENSE, "foo"); | |||
expectedException.expect(BadRequestException.class); | |||
expectedException.expectMessage("Can't apply a license when applying one is already in progress"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void request_fails_with_BadRequestException_if_license_is_invalid() { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
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 request_fails_with_ISE_if_is_cluster_and_license_plugin_is_not_installed() throws IOException { | |||
underTest = new ApplyLicenseAction(userSessionRule, mutableEditionManagementState, editionInstaller, webServer, null); | |||
actionTester = new WsActionTester(underTest); | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(mutableEditionManagementState.getPendingInstallationStatus()).thenReturn(NONE); | |||
when(webServer.isStandalone()).thenReturn(false); | |||
TestRequest request = actionTester.newRequest().setParam(PARAM_LICENSE, createLicenseParam("dev", "plugin1")); | |||
expectedException.expect(IllegalStateException.class); | |||
expectedException.expectMessage("License-manager plugin should be installed"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void request_fails_with_ISE_if_no_change_needed_but_license_plugin_is_not_installed() throws IOException { | |||
underTest = new ApplyLicenseAction(userSessionRule, mutableEditionManagementState, editionInstaller, webServer, null); | |||
actionTester = new WsActionTester(underTest); | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
setPendingLicense(NONE); | |||
when(webServer.isStandalone()).thenReturn(true); | |||
TestRequest request = actionTester.newRequest().setParam(PARAM_LICENSE, createLicenseParam("dev", "plugin1")); | |||
expectedException.expect(IllegalStateException.class); | |||
expectedException.expectMessage("Can't decide edition does not require install if LicenseCommit instance is null"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void always_apply_license_when_is_cluster() throws IOException { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(webServer.isStandalone()).thenReturn(false); | |||
setPendingLicense(NONE); | |||
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); | |||
verifyZeroInteractions(editionInstaller); | |||
} | |||
@Test | |||
public void verify_example() throws IOException { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
setPendingLicense(AUTOMATIC_IN_PROGRESS, null); | |||
TestRequest request = actionTester.newRequest() | |||
.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(singleton("plugin1"))).thenReturn(false); | |||
String base64License = createLicenseParam(PENDING_EDITION_NAME, "plugin1"); | |||
when(webServer.isStandalone()).thenReturn(true); | |||
TestRequest request = actionTester.newRequest() | |||
.setMediaType(MediaTypes.PROTOBUF) | |||
.setParam(PARAM_LICENSE, base64License); | |||
TestResponse response = request.execute(); | |||
assertResponse(response, PENDING_EDITION_NAME, "", NONE); | |||
verify(mutableEditionManagementState).newEditionWithoutInstall(PENDING_EDITION_NAME); | |||
verify(licenseCommit).update(base64License); | |||
} | |||
@Test | |||
public void execute_throws_BadRequestException_if_license_validation_fails_when_there_is_no_need_to_install() throws IOException { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
setPendingLicense(NONE, null); | |||
when(editionInstaller.requiresInstallationChange(singleton("plugin1"))).thenReturn(false); | |||
String base64License = createLicenseParam(PENDING_EDITION_NAME, "plugin1"); | |||
TestRequest request = actionTester.newRequest() | |||
.setMediaType(MediaTypes.PROTOBUF) | |||
.setParam(PARAM_LICENSE, base64License); | |||
IllegalArgumentException fakeValidationError = new IllegalArgumentException("Faking failed validation of license on update call"); | |||
doThrow(fakeValidationError) | |||
.when(licenseCommit) | |||
.update(base64License); | |||
expectedException.expect(BadRequestException.class); | |||
expectedException.expectMessage(fakeValidationError.getMessage()); | |||
request.execute(); | |||
} | |||
@Test | |||
public void apply_offline() throws IOException { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
setPendingLicense(PendingStatus.MANUAL_IN_PROGRESS); | |||
when(editionInstaller.requiresInstallationChange(singleton("plugin1"))).thenReturn(true); | |||
when(webServer.isStandalone()).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.MANUAL_IN_PROGRESS); | |||
verify(mutableEditionManagementState, times(0)).startManualInstall(any(License.class)); | |||
verify(mutableEditionManagementState, times(0)).startAutomaticInstall(any(License.class)); | |||
} | |||
@Test | |||
public void apply_successfully_auto_installation() throws IOException { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
setPendingLicense(PendingStatus.AUTOMATIC_IN_PROGRESS); | |||
when(editionInstaller.requiresInstallationChange(singleton("plugin1"))).thenReturn(true); | |||
when(webServer.isStandalone()).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, times(0)).startAutomaticInstall(any(License.class)); | |||
verify(mutableEditionManagementState, times(0)).startManualInstall(any(License.class)); | |||
} | |||
@Test | |||
public void returns_auto_install_fails_instantly() throws IOException { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
String errorMessage = "error! an error!"; | |||
setPendingLicense(PendingStatus.NONE, errorMessage); | |||
when(editionInstaller.requiresInstallationChange(singleton("plugin1"))).thenReturn(true); | |||
when(mutableEditionManagementState.getInstallErrorMessage()).thenReturn(Optional.of(errorMessage)); | |||
TestRequest request = actionTester.newRequest() | |||
.setMediaType(MediaTypes.PROTOBUF) | |||
.setParam(PARAM_LICENSE, createLicenseParam(PENDING_EDITION_NAME, "plugin1")); | |||
TestResponse response = request.execute(); | |||
StatusResponse parsedResponse = Editions.StatusResponse.parseFrom(response.getInputStream()); | |||
assertThat(parsedResponse.getInstallError()).isEqualTo(errorMessage); | |||
} | |||
private void assertResponse(TestResponse response, String expectedNextEditionKey, String expectedEditionKey, | |||
PendingStatus expectedPendingStatus) throws IOException { | |||
StatusResponse parsedResponse = Editions.StatusResponse.parseFrom(response.getInputStream()); | |||
assertThat(parsedResponse.getCurrentEditionKey()).isEqualTo(expectedEditionKey); | |||
assertThat(parsedResponse.getNextEditionKey()).isEqualTo(expectedNextEditionKey); | |||
assertThat(parsedResponse.getInstallationStatus()).isEqualTo(Editions.InstallationStatus.valueOf(expectedPendingStatus.toString())); | |||
} | |||
private void setPendingLicense(PendingStatus pendingStatus) { | |||
setPendingLicense(pendingStatus, null); | |||
} | |||
private void setPendingLicense(PendingStatus pendingStatus, @Nullable String errorMessage) { | |||
when(mutableEditionManagementState.getCurrentEditionKey()).thenReturn(Optional.empty()); | |||
when(mutableEditionManagementState.getPendingEditionKey()).thenReturn(Optional.of(PENDING_EDITION_NAME)); | |||
when(mutableEditionManagementState.getPendingInstallationStatus()) | |||
.thenReturn(NONE) | |||
.thenReturn(pendingStatus); | |||
when(mutableEditionManagementState.getInstallErrorMessage()).thenReturn(Optional.ofNullable(errorMessage)); | |||
} | |||
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); | |||
} | |||
} |
@@ -1,90 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.ws; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.mockito.Mockito; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.server.edition.MutableEditionManagementState; | |||
import org.sonar.server.exceptions.ForbiddenException; | |||
import org.sonar.server.exceptions.UnauthorizedException; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import org.sonar.server.ws.TestRequest; | |||
import org.sonar.server.ws.WsActionTester; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.verify; | |||
public class ClearErrorMessageActionTest { | |||
@Rule | |||
public UserSessionRule userSessionRule = UserSessionRule.standalone(); | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
private MutableEditionManagementState editionManagementState = Mockito.mock(MutableEditionManagementState.class); | |||
private ClearErrorMessageAction underTest = new ClearErrorMessageAction(userSessionRule, editionManagementState); | |||
private WsActionTester actionTester = new WsActionTester(underTest); | |||
@Test | |||
public void verify_definition() { | |||
WebService.Action def = actionTester.getDef(); | |||
assertThat(def.key()).isEqualTo("clear_error_message"); | |||
assertThat(def.since()).isEqualTo("6.7"); | |||
assertThat(def.isPost()).isTrue(); | |||
assertThat(def.isInternal()).isFalse(); | |||
assertThat(def.description()).isNotEmpty(); | |||
assertThat(def.responseExample()).isNull(); | |||
assertThat(def.params()).isEmpty(); | |||
} | |||
@Test | |||
public void request_fails_if_user_not_logged_in() { | |||
userSessionRule.anonymous(); | |||
TestRequest request = actionTester.newRequest(); | |||
expectedException.expect(UnauthorizedException.class); | |||
expectedException.expectMessage("Authentication is required"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void request_fails_if_user_is_not_system_administer() { | |||
userSessionRule.logIn(); | |||
TestRequest request = actionTester.newRequest(); | |||
expectedException.expect(ForbiddenException.class); | |||
expectedException.expectMessage("Insufficient privileges"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void request_clears_errorMessage_from_editionManagement_state() { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
actionTester.newRequest().execute(); | |||
verify(editionManagementState).clearInstallErrorMessage(); | |||
} | |||
} |
@@ -1,130 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.ws; | |||
import com.tngtech.java.junit.dataprovider.DataProviderRunner; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.junit.runner.RunWith; | |||
import org.sonar.api.platform.Server; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.metric.MetricDto; | |||
import org.sonar.server.exceptions.ForbiddenException; | |||
import org.sonar.server.exceptions.UnauthorizedException; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import org.sonar.server.ws.TestRequest; | |||
import org.sonar.server.ws.WsActionTester; | |||
import org.sonarqube.ws.Editions.FormDataResponse; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY; | |||
import static org.sonar.api.measures.Metric.ValueType.INT; | |||
import static org.sonar.test.JsonAssert.assertJson; | |||
@RunWith(DataProviderRunner.class) | |||
public class FormDataActionTest { | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
@Rule | |||
public UserSessionRule userSessionRule = UserSessionRule.standalone(); | |||
@Rule | |||
public DbTester db = DbTester.create(); | |||
private Server server = mock(Server.class); | |||
private DbClient dbClient = db.getDbClient(); | |||
private FormDataAction underTest = new FormDataAction(userSessionRule, server, dbClient); | |||
private WsActionTester ws = new WsActionTester(underTest); | |||
@Test | |||
public void definition() { | |||
WebService.Action def = ws.getDef(); | |||
assertThat(def.key()).isEqualTo("form_data"); | |||
assertThat(def.since()).isEqualTo("6.7"); | |||
assertThat(def.isPost()).isFalse(); | |||
assertThat(def.isInternal()).isTrue(); | |||
assertThat(def.description()).isNotEmpty(); | |||
assertThat(def.params()).isEmpty(); | |||
} | |||
@Test | |||
public void request_fails_if_user_not_logged_in() { | |||
userSessionRule.anonymous(); | |||
TestRequest request = ws.newRequest(); | |||
expectedException.expect(UnauthorizedException.class); | |||
expectedException.expectMessage("Authentication is required"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void request_fails_if_user_is_not_system_administer() { | |||
userSessionRule.logIn(); | |||
TestRequest request = ws.newRequest(); | |||
expectedException.expect(ForbiddenException.class); | |||
expectedException.expectMessage("Insufficient privileges"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void json_example() { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(server.getId()).thenReturn("uuid"); | |||
setNcloc(12345L); | |||
String result = ws.newRequest().execute().getInput(); | |||
assertJson(result).isSimilarTo(ws.getDef().responseExampleAsString()); | |||
} | |||
@Test | |||
public void returns_server_id_and_nloc() { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(server.getId()).thenReturn("myserver"); | |||
long ncloc = 256L; | |||
setNcloc(ncloc); | |||
FormDataResponse expectedResponse = FormDataResponse.newBuilder() | |||
.setServerId("myserver") | |||
.setNcloc(ncloc) | |||
.build(); | |||
FormDataResponse result = ws.newRequest().executeProtobuf(FormDataResponse.class); | |||
assertThat(result).isEqualTo(expectedResponse); | |||
} | |||
private void setNcloc(double ncloc) { | |||
ComponentDto project = db.components().insertMainBranch(); | |||
MetricDto nclocMetric = db.measures().insertMetric(m -> m.setValueType(INT.toString()).setKey(NCLOC_KEY)); | |||
db.measures().insertLiveMeasure(project, nclocMetric, m -> m.setValue(ncloc)); | |||
} | |||
} |
@@ -1,260 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.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.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.exceptions.BadRequestException; | |||
import org.sonar.server.exceptions.ForbiddenException; | |||
import org.sonar.server.exceptions.UnauthorizedException; | |||
import org.sonar.server.platform.WebServer; | |||
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.Editions; | |||
import org.sonarqube.ws.MediaTypes; | |||
import org.sonarqube.ws.Editions.PreviewResponse; | |||
import org.sonarqube.ws.Editions.PreviewStatus; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.server.edition.EditionManagementState.PendingStatus.NONE; | |||
@RunWith(DataProviderRunner.class) | |||
public class PreviewActionTest { | |||
private static final String PARAM_LICENSE = "license"; | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
@Rule | |||
public UserSessionRule userSessionRule = UserSessionRule.standalone(); | |||
private EditionManagementState editionManagementState = mock(EditionManagementState.class); | |||
private EditionInstaller editionInstaller = mock(EditionInstaller.class); | |||
private WebServer webServer = mock(WebServer.class); | |||
private PreviewAction underTest = new PreviewAction(userSessionRule, editionManagementState, editionInstaller, webServer); | |||
private WsActionTester actionTester = new WsActionTester(underTest); | |||
@Test | |||
public void verify_definition() { | |||
WebService.Action def = actionTester.getDef(); | |||
assertThat(def.key()).isEqualTo("preview"); | |||
assertThat(def.since()).isEqualTo("6.7"); | |||
assertThat(def.isPost()).isTrue(); | |||
assertThat(def.isInternal()).isFalse(); | |||
assertThat(def.description()).isNotEmpty(); | |||
assertThat(def.params()).hasSize(1); | |||
WebService.Param licenseParam = def.param("license"); | |||
assertThat(licenseParam.isRequired()).isTrue(); | |||
assertThat(licenseParam.description()).isNotEmpty(); | |||
} | |||
@Test | |||
public void request_fails_if_user_not_logged_in() { | |||
userSessionRule.anonymous(); | |||
TestRequest request = actionTester.newRequest(); | |||
expectedException.expect(UnauthorizedException.class); | |||
expectedException.expectMessage("Authentication is required"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void request_fails_if_user_is_not_system_administer() { | |||
userSessionRule.logIn(); | |||
TestRequest request = actionTester.newRequest(); | |||
expectedException.expect(ForbiddenException.class); | |||
expectedException.expectMessage("Insufficient privileges"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void request_fails_if_license_param_is_not_provided() { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE); | |||
TestRequest request = actionTester.newRequest(); | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("The 'license' parameter is missing"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void request_fails_if_license_param_is_empty() { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE); | |||
TestRequest request = actionTester.newRequest() | |||
.setParam(PARAM_LICENSE, ""); | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("The 'license' parameter is empty"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void request_fails_if_license_param_is_invalid() { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE); | |||
TestRequest request = actionTester.newRequest() | |||
.setParam(PARAM_LICENSE, "foo"); | |||
expectedException.expect(BadRequestException.class); | |||
expectedException.expectMessage("The license provided is invalid"); | |||
request.execute(); | |||
} | |||
@Test | |||
@UseDataProvider("notNonePendingInstallationStatuses") | |||
public void request_fails_with_BadRequestException_is_pendingStatus_is_not_NONE(EditionManagementState.PendingStatus notNone) { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(editionManagementState.getPendingInstallationStatus()).thenReturn(notNone); | |||
TestRequest request = actionTester.newRequest() | |||
.setParam(PARAM_LICENSE, "foo"); | |||
expectedException.expect(BadRequestException.class); | |||
expectedException.expectMessage("Can't apply a license when applying one is already in progress"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void verify_example() throws IOException { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(webServer.isStandalone()).thenReturn(true); | |||
when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE); | |||
when(editionInstaller.requiresInstallationChange(Collections.singleton("plugin1"))).thenReturn(true); | |||
when(editionInstaller.isOffline()).thenReturn(false); | |||
TestRequest request = actionTester.newRequest() | |||
.setParam(PARAM_LICENSE, createLicenseParam("developer-edition", "plugin1")); | |||
JsonAssert.assertJson(request.execute().getInput()).isSimilarTo(actionTester.getDef().responseExampleAsString()); | |||
} | |||
@Test | |||
public void license_requires_no_installation() throws IOException { | |||
when(webServer.isStandalone()).thenReturn(true); | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE); | |||
when(editionInstaller.requiresInstallationChange(Collections.singleton("plugin1"))).thenReturn(false); | |||
TestRequest request = actionTester.newRequest() | |||
.setMediaType(MediaTypes.PROTOBUF) | |||
.setParam(PARAM_LICENSE, createLicenseParam("developer-edition", "plugin1")); | |||
assertResponse(request.execute(), "developer-edition", PreviewStatus.NO_INSTALL); | |||
} | |||
@Test | |||
public void cluster_require_no_installation() throws IOException { | |||
when(webServer.isStandalone()).thenReturn(false); | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE); | |||
when(editionInstaller.requiresInstallationChange(Collections.singleton("plugin1"))).thenReturn(false); | |||
TestRequest request = actionTester.newRequest() | |||
.setMediaType(MediaTypes.PROTOBUF) | |||
.setParam(PARAM_LICENSE, createLicenseParam("developer-edition", "plugin1")); | |||
assertResponse(request.execute(), "developer-edition", PreviewStatus.NO_INSTALL); | |||
} | |||
@Test | |||
public void license_will_result_in_auto_install() throws IOException { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(webServer.isStandalone()).thenReturn(true); | |||
when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE); | |||
when(editionInstaller.requiresInstallationChange(Collections.singleton("plugin1"))).thenReturn(true); | |||
when(editionInstaller.isOffline()).thenReturn(false); | |||
TestRequest request = actionTester.newRequest() | |||
.setMediaType(MediaTypes.PROTOBUF) | |||
.setParam(PARAM_LICENSE, createLicenseParam("developer-edition", "plugin1")); | |||
assertResponse(request.execute(), "developer-edition", PreviewStatus.AUTOMATIC_INSTALL); | |||
} | |||
@Test | |||
public void license_will_result_in_manual_install() throws IOException { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(webServer.isStandalone()).thenReturn(true); | |||
when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE); | |||
when(editionInstaller.requiresInstallationChange(Collections.singleton("plugin1"))).thenReturn(true); | |||
when(editionInstaller.isOffline()).thenReturn(true); | |||
TestRequest request = actionTester.newRequest() | |||
.setMediaType(MediaTypes.PROTOBUF) | |||
.setParam(PARAM_LICENSE, createLicenseParam("developer-edition", "plugin1")); | |||
assertResponse(request.execute(), "developer-edition", PreviewStatus.MANUAL_INSTALL); | |||
} | |||
private void assertResponse(TestResponse response, String expectedNextEditionKey, PreviewStatus expectedPreviewStatus) throws IOException { | |||
PreviewResponse parsedResponse = Editions.PreviewResponse.parseFrom(response.getInputStream()); | |||
assertThat(parsedResponse.getPreviewStatus()).isEqualTo(expectedPreviewStatus); | |||
assertThat(parsedResponse.getNextEditionKey()).isEqualTo(expectedNextEditionKey); | |||
} | |||
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); | |||
} | |||
} |
@@ -1,132 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.ws; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.server.edition.EditionManagementState; | |||
import org.sonar.server.exceptions.ForbiddenException; | |||
import org.sonar.server.exceptions.UnauthorizedException; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import org.sonar.server.ws.TestRequest; | |||
import org.sonar.server.ws.WsActionTester; | |||
import org.sonar.test.JsonAssert; | |||
import static java.util.Optional.empty; | |||
import static java.util.Optional.of; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.server.edition.EditionManagementState.PendingStatus.NONE; | |||
public class StatusActionTest { | |||
@Rule | |||
public UserSessionRule userSessionRule = UserSessionRule.standalone(); | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
private EditionManagementState editionManagementState = mock(EditionManagementState.class); | |||
private StatusAction underTest = new StatusAction(userSessionRule, editionManagementState); | |||
private WsActionTester actionTester = new WsActionTester(underTest); | |||
@Test | |||
public void verify_definition() { | |||
WebService.Action def = actionTester.getDef(); | |||
assertThat(def.key()).isEqualTo("status"); | |||
assertThat(def.since()).isEqualTo("6.7"); | |||
assertThat(def.isPost()).isFalse(); | |||
assertThat(def.description()).isNotEmpty(); | |||
assertThat(def.params()).isEmpty(); | |||
} | |||
@Test | |||
public void request_fails_if_user_not_logged_in() { | |||
userSessionRule.anonymous(); | |||
TestRequest request = actionTester.newRequest(); | |||
expectedException.expect(UnauthorizedException.class); | |||
expectedException.expectMessage("Authentication is required"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void request_fails_if_user_is_not_system_administer() { | |||
userSessionRule.logIn(); | |||
TestRequest request = actionTester.newRequest(); | |||
expectedException.expect(ForbiddenException.class); | |||
expectedException.expectMessage("Insufficient privileges"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void verify_example() { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(editionManagementState.getCurrentEditionKey()).thenReturn(empty()); | |||
when(editionManagementState.getPendingEditionKey()).thenReturn(of("developer-edition")); | |||
when(editionManagementState.getPendingInstallationStatus()).thenReturn(EditionManagementState.PendingStatus.AUTOMATIC_READY); | |||
when(editionManagementState.getInstallErrorMessage()).thenReturn(empty()); | |||
TestRequest request = actionTester.newRequest(); | |||
JsonAssert.assertJson(request.execute().getInput()).isSimilarTo(actionTester.getDef().responseExampleAsString()); | |||
} | |||
@Test | |||
public void response_contains_optional_fields_as_empty_string() { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(editionManagementState.getCurrentEditionKey()).thenReturn(empty()); | |||
when(editionManagementState.getPendingEditionKey()).thenReturn(empty()); | |||
when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE); | |||
when(editionManagementState.getInstallErrorMessage()).thenReturn(empty()); | |||
TestRequest request = actionTester.newRequest(); | |||
JsonAssert.assertJson(request.execute().getInput()) | |||
.isSimilarTo("{" + | |||
" \"currentEditionKey\": \"\"," + | |||
" \"installationStatus\": \"NONE\"," + | |||
" \"nextEditionKey\": \"\"" + | |||
"}"); | |||
} | |||
@Test | |||
public void response_contains_automaticInstallError_when_present() { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(editionManagementState.getCurrentEditionKey()).thenReturn(empty()); | |||
when(editionManagementState.getPendingEditionKey()).thenReturn(empty()); | |||
when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE); | |||
String errorMessage = "an error! oh god, an error!"; | |||
when(editionManagementState.getInstallErrorMessage()).thenReturn(of(errorMessage)); | |||
TestRequest request = actionTester.newRequest(); | |||
JsonAssert.assertJson(request.execute().getInput()) | |||
.isSimilarTo("{" + | |||
" \"currentEditionKey\": \"\"," + | |||
" \"installationStatus\": \"NONE\"," + | |||
" \"nextEditionKey\": \"\"," + | |||
" \"installError\": \"" + errorMessage + "\"" + | |||
"}"); | |||
} | |||
} |
@@ -1,137 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.ws; | |||
import com.tngtech.java.junit.dataprovider.DataProvider; | |||
import com.tngtech.java.junit.dataprovider.DataProviderRunner; | |||
import com.tngtech.java.junit.dataprovider.UseDataProvider; | |||
import java.util.Arrays; | |||
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.MutableEditionManagementState; | |||
import org.sonar.server.exceptions.BadRequestException; | |||
import org.sonar.server.exceptions.ForbiddenException; | |||
import org.sonar.server.exceptions.UnauthorizedException; | |||
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 static org.assertj.core.api.Assertions.assertThat; | |||
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.NONE; | |||
import static org.sonar.server.edition.EditionManagementState.PendingStatus.UNINSTALL_IN_PROGRESS; | |||
@RunWith(DataProviderRunner.class) | |||
public class UninstallActionTest { | |||
@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 UninstallAction action = new UninstallAction(userSessionRule, mutableEditionManagementState, editionInstaller); | |||
private WsActionTester actionTester = new WsActionTester(action); | |||
@Test | |||
public void check_definition() { | |||
WebService.Action def = actionTester.getDef(); | |||
assertThat(def.key()).isEqualTo("uninstall"); | |||
assertThat(def.since()).isEqualTo("6.7"); | |||
assertThat(def.isPost()).isTrue(); | |||
assertThat(def.isInternal()).isFalse(); | |||
assertThat(def.responseExampleAsString()).isNull(); | |||
assertThat(def.description()).isNotEmpty(); | |||
assertThat(def.params()).isEmpty(); | |||
} | |||
@Test | |||
public void request_fails_if_user_not_logged_in() { | |||
userSessionRule.anonymous(); | |||
TestRequest request = actionTester.newRequest(); | |||
expectedException.expect(UnauthorizedException.class); | |||
expectedException.expectMessage("Authentication is required"); | |||
request.execute(); | |||
} | |||
@Test | |||
public void request_fails_if_user_is_not_system_administer() { | |||
userSessionRule.logIn(); | |||
TestRequest request = actionTester.newRequest(); | |||
expectedException.expect(ForbiddenException.class); | |||
expectedException.expectMessage("Insufficient privileges"); | |||
request.execute(); | |||
} | |||
@Test | |||
@UseDataProvider("notNoneOrUninstallStatuses") | |||
public void request_fails_if_current_status_is_not_none_nor_uninstall(EditionManagementState.PendingStatus notNoneOrUninstall) { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(mutableEditionManagementState.getPendingInstallationStatus()).thenReturn(notNoneOrUninstall); | |||
expectedException.expect(BadRequestException.class); | |||
expectedException.expectMessage("Uninstall of the current edition is not allowed when install of an edition"); | |||
actionTester.newRequest().execute(); | |||
} | |||
@Test | |||
public void successful_edition_uninstall() { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(mutableEditionManagementState.getPendingInstallationStatus()).thenReturn(NONE); | |||
TestResponse execute = actionTester.newRequest().execute(); | |||
assertThat(execute.getStatus()).isEqualTo(204); | |||
verify(editionInstaller).uninstall(); | |||
verify(mutableEditionManagementState).uninstall(); | |||
} | |||
@Test | |||
public void successful_edition_uninstall_when_state_is_already_uninstall_in_progress() { | |||
userSessionRule.logIn().setSystemAdministrator(); | |||
when(mutableEditionManagementState.getPendingInstallationStatus()).thenReturn(UNINSTALL_IN_PROGRESS); | |||
TestResponse execute = actionTester.newRequest().execute(); | |||
assertThat(execute.getStatus()).isEqualTo(204); | |||
verify(editionInstaller).uninstall(); | |||
verify(mutableEditionManagementState).uninstall(); | |||
} | |||
@DataProvider | |||
public static Object[][] notNoneOrUninstallStatuses() { | |||
return Arrays.stream(EditionManagementState.PendingStatus.values()) | |||
.filter(s -> s != NONE) | |||
.filter(s -> s != UNINSTALL_IN_PROGRESS) | |||
.map(s -> new Object[] {s}) | |||
.toArray(Object[][]::new); | |||
} | |||
} |
@@ -1,35 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.plugins.edition; | |||
import org.junit.Test; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.timeout; | |||
import static org.mockito.Mockito.verify; | |||
public class EditionInstallerExecutorTest { | |||
@Test | |||
public void execute() { | |||
Runnable r = mock(Runnable.class); | |||
new EditionInstallerExecutor().execute(r); | |||
verify(r, timeout(5000)).run(); | |||
} | |||
} |
@@ -1,293 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.plugins.edition; | |||
import com.google.common.base.Optional; | |||
import java.util.Arrays; | |||
import java.util.HashSet; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.mockito.ArgumentCaptor; | |||
import org.sonar.api.utils.log.LogTester; | |||
import org.sonar.api.utils.log.LoggerLevel; | |||
import org.sonar.core.platform.PluginInfo; | |||
import org.sonar.server.edition.License; | |||
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 java.util.Collections.singleton; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.ArgumentMatchers.any; | |||
import static org.mockito.ArgumentMatchers.anyBoolean; | |||
import static org.mockito.ArgumentMatchers.anySetOf; | |||
import static org.mockito.ArgumentMatchers.anyString; | |||
import static org.mockito.Mockito.doThrow; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.reset; | |||
import static org.mockito.Mockito.times; | |||
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.core.util.stream.MoreCollectors.uniqueIndex; | |||
public class EditionInstallerTest { | |||
@Rule | |||
public LogTester logTester = new LogTester(); | |||
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 synchronousExecutor = new EditionInstallerExecutor() { | |||
public void execute(Runnable r) { | |||
r.run(); | |||
} | |||
}; | |||
private EditionInstallerExecutor mockedExecutor = mock(EditionInstallerExecutor.class); | |||
private EditionInstaller underTestSynchronousExecutor = new EditionInstaller(downloader, uninstaller, pluginRepository, synchronousExecutor, updateCenterMatrixFactory, | |||
editionManagementState); | |||
private EditionInstaller underTestMockedExecutor = new EditionInstaller(downloader, uninstaller, pluginRepository, mockedExecutor, updateCenterMatrixFactory, | |||
editionManagementState); | |||
@Before | |||
public void setUp() { | |||
when(updateCenterMatrixFactory.getUpdateCenter(anyBoolean())).thenReturn(Optional.of(updateCenter)); | |||
} | |||
@Test | |||
public void launch_task_download_plugins() { | |||
underTestSynchronousExecutor.install(licenseWithPluginKeys(PLUGIN_KEY)); | |||
verify(downloader).downloadEditionPlugins(singleton(PLUGIN_KEY), updateCenter); | |||
} | |||
@Test | |||
public void editionManagementState_is_changed_before_running_background_thread() { | |||
PluginInfo commercial1 = createPluginInfo("p1", true); | |||
PluginInfo commercial2 = createPluginInfo("p2", true); | |||
PluginInfo open1 = createPluginInfo("p3", false); | |||
mockPluginRepository(commercial1, commercial2, open1); | |||
License newLicense = licenseWithPluginKeys("p1", "p4"); | |||
underTestMockedExecutor.install(newLicense); | |||
verify(editionManagementState).startAutomaticInstall(newLicense); | |||
verify(editionManagementState, times(0)).automaticInstallReady(); | |||
verifyNoMoreInteractions(editionManagementState); | |||
ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); | |||
verify(mockedExecutor).execute(runnableCaptor.capture()); | |||
reset(editionManagementState); | |||
runnableCaptor.getValue().run(); | |||
verify(editionManagementState).automaticInstallReady(); | |||
verifyNoMoreInteractions(editionManagementState); | |||
} | |||
@Test | |||
public void editionManagementState_is_changed_to_automatic_failure_when_read_existing_plugins_fails() { | |||
RuntimeException fakeException = new RuntimeException("Faking getPluginInfosByKeys throwing an exception"); | |||
when(pluginRepository.getPluginInfosByKeys()) | |||
.thenThrow(fakeException); | |||
License newLicense = licenseWithPluginKeys("p1", "p4"); | |||
underTestSynchronousExecutor.install(newLicense); | |||
verifyMoveToAutomaticFailureAndLogsError(newLicense, fakeException.getMessage()); | |||
} | |||
@Test | |||
public void editionManagementState_is_changed_to_automatic_failure_when_downloader_fails() { | |||
PluginInfo commercial1 = createPluginInfo("p1", true); | |||
PluginInfo commercial2 = createPluginInfo("p2", true); | |||
PluginInfo open1 = createPluginInfo("p3", false); | |||
mockPluginRepository(commercial1, commercial2, open1); | |||
RuntimeException fakeException = new RuntimeException("Faking downloadEditionPlugins throwing an exception"); | |||
doThrow(fakeException) | |||
.when(downloader) | |||
.downloadEditionPlugins(anySetOf(String.class), any(UpdateCenter.class)); | |||
License newLicense = licenseWithPluginKeys("p1", "p4"); | |||
underTestSynchronousExecutor.install(newLicense); | |||
verifyMoveToAutomaticFailureAndLogsError(newLicense, fakeException.getMessage()); | |||
} | |||
@Test | |||
public void editionManagementState_is_changed_to_automatic_failure_when_uninstaller_fails() { | |||
PluginInfo commercial1 = createPluginInfo("p1", true); | |||
PluginInfo commercial2 = createPluginInfo("p2", true); | |||
PluginInfo open1 = createPluginInfo("p3", false); | |||
mockPluginRepository(commercial1, commercial2, open1); | |||
RuntimeException fakeException = new RuntimeException("Faking uninstall throwing an exception"); | |||
doThrow(fakeException) | |||
.when(uninstaller) | |||
.uninstall(anyString()); | |||
License newLicense = licenseWithPluginKeys("p1", "p4"); | |||
underTestSynchronousExecutor.install(newLicense); | |||
verifyMoveToAutomaticFailureAndLogsError(newLicense, fakeException.getMessage()); | |||
} | |||
@Test | |||
public void check_plugins_to_install_and_remove() { | |||
PluginInfo commercial1 = createPluginInfo("p1", true); | |||
PluginInfo commercial2 = createPluginInfo("p2", true); | |||
PluginInfo open1 = createPluginInfo("p3", false); | |||
mockPluginRepository(commercial1, commercial2, open1); | |||
License newLicense = licenseWithPluginKeys("p1", "p4"); | |||
underTestSynchronousExecutor.install(newLicense); | |||
verify(editionManagementState).startAutomaticInstall(newLicense); | |||
verify(editionManagementState).automaticInstallReady(); | |||
verifyNoMoreInteractions(editionManagementState); | |||
verify(downloader).downloadEditionPlugins(singleton("p4"), updateCenter); | |||
verify(uninstaller).uninstall("p2"); | |||
verifyNoMoreInteractions(uninstaller); | |||
verifyNoMoreInteractions(downloader); | |||
assertThat(logTester.logs()).containsOnly("Installing edition 'edition-key', download: [p4], remove: [p2]"); | |||
} | |||
@Test | |||
public void uninstall_commercial_plugins() { | |||
PluginInfo commercial1 = createPluginInfo("p1", true); | |||
PluginInfo commercial2 = createPluginInfo("p2", true); | |||
PluginInfo open1 = createPluginInfo("p3", false); | |||
mockPluginRepository(commercial1, commercial2, open1); | |||
underTestSynchronousExecutor.uninstall(); | |||
verify(uninstaller).uninstall("p2"); | |||
verify(uninstaller).uninstall("p1"); | |||
verifyNoMoreInteractions(uninstaller); | |||
verifyZeroInteractions(downloader); | |||
} | |||
@Test | |||
public void move_to_manualInstall_state_when_offline() { | |||
mockPluginRepository(createPluginInfo("p1", true)); | |||
synchronousExecutor = mock(EditionInstallerExecutor.class); | |||
when(updateCenterMatrixFactory.getUpdateCenter(true)).thenReturn(Optional.absent()); | |||
underTestSynchronousExecutor = new EditionInstaller(downloader, uninstaller, pluginRepository, synchronousExecutor, updateCenterMatrixFactory, editionManagementState); | |||
License newLicense = licenseWithPluginKeys("p1"); | |||
underTestSynchronousExecutor.install(newLicense); | |||
verifyZeroInteractions(synchronousExecutor); | |||
verifyZeroInteractions(uninstaller); | |||
verifyZeroInteractions(downloader); | |||
verify(editionManagementState).startManualInstall(newLicense); | |||
verifyNoMoreInteractions(editionManagementState); | |||
assertThat(logTester.logs()).containsOnly("Installation of edition 'edition-key' needs to be done manually"); | |||
} | |||
@Test | |||
public void is_offline() { | |||
when(updateCenterMatrixFactory.getUpdateCenter(false)).thenReturn(Optional.absent()); | |||
assertThat(underTestSynchronousExecutor.isOffline()).isTrue(); | |||
} | |||
@Test | |||
public void is_not_offline() { | |||
assertThat(underTestSynchronousExecutor.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<String> editionPlugins = new HashSet<>(); | |||
editionPlugins.add("p1"); | |||
editionPlugins.add("p4"); | |||
boolean flag = underTestSynchronousExecutor.requiresInstallationChange(editionPlugins); | |||
assertThat(flag).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<String> editionPlugins = new HashSet<>(); | |||
editionPlugins.add("p1"); | |||
editionPlugins.add("p2"); | |||
boolean flag = underTestSynchronousExecutor.requiresInstallationChange(editionPlugins); | |||
assertThat(flag).isFalse(); | |||
verifyZeroInteractions(downloader); | |||
verifyZeroInteractions(uninstaller); | |||
verifyZeroInteractions(editionManagementState); | |||
} | |||
private void mockPluginRepository(PluginInfo... installedPlugins) { | |||
Map<String, PluginInfo> pluginsByKey = Arrays.stream(installedPlugins).collect(uniqueIndex(PluginInfo::getKey)); | |||
when(pluginRepository.getPluginInfosByKeys()).thenReturn(pluginsByKey); | |||
when(pluginRepository.getPluginInfos()).thenReturn(Arrays.asList(installedPlugins)); | |||
} | |||
private static PluginInfo createPluginInfo(String pluginKey, boolean commercial) { | |||
PluginInfo info = new PluginInfo(pluginKey); | |||
if (commercial) { | |||
info.setOrganizationName("SonarSource"); | |||
info.setLicense("Commercial"); | |||
} | |||
return info; | |||
} | |||
private static License licenseWithPluginKeys(String... pluginKeys) { | |||
return new License("edition-key", Arrays.asList(pluginKeys), "foo"); | |||
} | |||
private void verifyMoveToAutomaticFailureAndLogsError(License newLicense, String expectedErrorMessage) { | |||
verify(editionManagementState).startAutomaticInstall(newLicense); | |||
verify(editionManagementState).installFailed(expectedErrorMessage); | |||
verifyNoMoreInteractions(editionManagementState); | |||
assertThat(logTester.logs(LoggerLevel.ERROR)) | |||
.containsOnly("Failed to install edition " + newLicense.getEditionKey() + " with plugins " + newLicense.getPluginKeys()); | |||
} | |||
} |
@@ -1,133 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.plugins.edition; | |||
import com.google.common.collect.ImmutableList; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.net.URI; | |||
import java.net.URISyntaxException; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.utils.HttpDownloader; | |||
import org.sonar.api.utils.log.LogTester; | |||
import org.sonar.server.platform.ServerFileSystem; | |||
import org.sonar.updatecenter.common.Plugin; | |||
import org.sonar.updatecenter.common.Release; | |||
import org.sonar.updatecenter.common.UpdateCenter; | |||
import org.sonar.updatecenter.common.Version; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assert.fail; | |||
import static org.mockito.Mockito.doThrow; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.when; | |||
public class EditionPluginDownloaderTest { | |||
@Rule | |||
public LogTester logTester = new LogTester(); | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
private ServerFileSystem fs = mock(ServerFileSystem.class); | |||
private HttpDownloader httpDownloader = mock(HttpDownloader.class); | |||
private UpdateCenter updateCenter = mock(UpdateCenter.class); | |||
private File downloadDir; | |||
private File tmpDir; | |||
private EditionPluginDownloader downloader; | |||
@Before | |||
public void setUp() throws IOException { | |||
downloadDir = temp.newFolder("download"); | |||
tmpDir = new File(downloadDir.getParentFile(), downloadDir.getName() + "_tmp"); | |||
when(fs.getEditionDownloadedPluginsDir()).thenReturn(downloadDir); | |||
downloader = new EditionPluginDownloader(httpDownloader, fs); | |||
} | |||
@Test | |||
public void download_plugin_to_tmp() throws URISyntaxException { | |||
List<Release> releases = ImmutableList.of(createRelease("plugin1", "1.0", "http://host/plugin1.jar"), | |||
createRelease("plugin2", "1.0", "http://host/plugin2.jar")); | |||
when(updateCenter.findInstallablePlugins("plugins", Version.create(""))).thenReturn(releases); | |||
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")); | |||
assertThat(logTester.logs()).containsOnly("Downloading plugin: plugin1", "Downloading plugin: plugin2"); | |||
assertThat(downloadDir).isDirectory(); | |||
assertThat(tmpDir).doesNotExist(); | |||
} | |||
@Test | |||
public void download_plugin_to_tmp_with_file_uri() throws IOException { | |||
File plugin1 = temp.newFile("plugin1.jar"); | |||
File plugin2 = temp.newFile("plugin2.jar"); | |||
List<Release> releases = ImmutableList.of(createRelease("plugin1", "1.0", plugin1.toURI().toString()), | |||
createRelease("plugin2", "1.0", plugin2.toURI().toString())); | |||
when(updateCenter.findInstallablePlugins("plugins", Version.create(""))).thenReturn(releases); | |||
downloader.downloadEditionPlugins(Collections.singleton("plugins"), updateCenter); | |||
assertThat(logTester.logs()).containsOnly("Downloading plugin: plugin1", "Downloading plugin: plugin2"); | |||
assertThat(downloadDir).isDirectory(); | |||
assertThat(tmpDir).doesNotExist(); | |||
} | |||
@Test | |||
public void dont_write_download_dir_if_download_fails() throws URISyntaxException { | |||
List<Release> releases = ImmutableList.of(createRelease("plugin1", "1.0", "http://host/plugin1.jar"), | |||
createRelease("plugin2", "1.0", "http://host/plugin2.jar")); | |||
doThrow(new IllegalStateException("error")).when(httpDownloader).download(new URI("http://host/plugin1.jar"), new File(tmpDir, "plugin1.jar")); | |||
when(updateCenter.findInstallablePlugins("plugins", Version.create(""))).thenReturn(releases); | |||
try { | |||
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")); | |||
assertThat(downloadDir.list()).isEmpty(); | |||
assertThat(tmpDir).doesNotExist(); | |||
} | |||
private static Release createRelease(String key, String version, String url) { | |||
Release release = mock(Release.class); | |||
when(release.getKey()).thenReturn(key); | |||
when(release.getVersion()).thenReturn(Version.create(version)); | |||
when(release.getDownloadUrl()).thenReturn(url); | |||
when(release.getArtifact()).thenReturn(Plugin.factory(key)); | |||
return release; | |||
} | |||
} |
@@ -1,52 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.plugins.edition; | |||
import java.io.File; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.server.platform.ServerFileSystem; | |||
import org.sonar.server.plugins.ServerPluginRepository; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.verifyNoMoreInteractions; | |||
import static org.mockito.Mockito.when; | |||
public class EditionPluginUninstallerTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Test | |||
public void start_creates_uninstall_directory() { | |||
File dir = new File(temp.getRoot(), "uninstall"); | |||
ServerPluginRepository repo = mock(ServerPluginRepository.class); | |||
ServerFileSystem fs = mock(ServerFileSystem.class); | |||
when(fs.getEditionUninstalledPluginsDir()).thenReturn(dir); | |||
EditionPluginUninstaller uninstaller = new EditionPluginUninstaller(repo, fs); | |||
uninstaller.start(); | |||
verify(fs).getEditionUninstalledPluginsDir(); | |||
verifyNoMoreInteractions(fs); | |||
assertThat(dir).isDirectory(); | |||
} | |||
} |
@@ -1,143 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.api.config; | |||
import java.io.IOException; | |||
import java.io.StringReader; | |||
import java.nio.charset.StandardCharsets; | |||
import java.util.Calendar; | |||
import java.util.Date; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.codec.binary.Base64; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.utils.DateUtils; | |||
/** | |||
* SonarSource license. This class aims to extract metadata but not to validate or - of course - | |||
* to generate license | |||
* | |||
* @since 3.0 | |||
*/ | |||
public final class License { | |||
private String product; | |||
private String organization; | |||
private String expirationDate; | |||
private String type; | |||
private String server; | |||
private Map<String, String> additionalProperties; | |||
private License(Map<String, String> properties) { | |||
this.additionalProperties = new HashMap<>(properties); | |||
product = StringUtils.defaultString(get("Product", properties), get("Plugin", properties)); | |||
organization = StringUtils.defaultString(get("Organisation", properties), get("Name", properties)); | |||
expirationDate = StringUtils.defaultString(get("Expiration", properties), get("Expires", properties)); | |||
type = get("Type", properties); | |||
server = get("Server", properties); | |||
// SONAR-4340 Don't expose Digest and Obeo properties | |||
additionalProperties.remove("Digest"); | |||
additionalProperties.remove("Obeo"); | |||
} | |||
private String get(String key, Map<String, String> properties) { | |||
additionalProperties.remove(key); | |||
return properties.get(key); | |||
} | |||
/** | |||
* Get additional properties available on this license (like threshold conditions) | |||
* @since 3.6 | |||
*/ | |||
public Map<String, String> additionalProperties() { | |||
return additionalProperties; | |||
} | |||
@Nullable | |||
public String getProduct() { | |||
return product; | |||
} | |||
@Nullable | |||
public String getOrganization() { | |||
return organization; | |||
} | |||
@Nullable | |||
public String getExpirationDateAsString() { | |||
return expirationDate; | |||
} | |||
@Nullable | |||
public Date getExpirationDate() { | |||
return DateUtils.parseDateQuietly(expirationDate); | |||
} | |||
public boolean isExpired() { | |||
return isExpired(new Date()); | |||
} | |||
boolean isExpired(Date now) { | |||
Date date = getExpirationDate(); | |||
if (date == null) { | |||
return false; | |||
} | |||
// SONAR-6079 include last day | |||
Calendar cal = Calendar.getInstance(); | |||
cal.setTime(date); | |||
cal.add(Calendar.DAY_OF_MONTH, 1); | |||
cal.add(Calendar.SECOND, -1); | |||
return now.after(cal.getTime()); | |||
} | |||
@Nullable | |||
public String getType() { | |||
return type; | |||
} | |||
@Nullable | |||
public String getServer() { | |||
return server; | |||
} | |||
public static License readBase64(String base64) { | |||
return readPlainText(new String(Base64.decodeBase64(base64.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8)); | |||
} | |||
static License readPlainText(String data) { | |||
Map<String, String> props = new HashMap<>(); | |||
try { | |||
List<String> lines = IOUtils.readLines(new StringReader(data)); | |||
for (String line : lines) { | |||
if (StringUtils.isNotBlank(line) && line.indexOf(':') > 0) { | |||
String key = StringUtils.substringBefore(line, ":"); | |||
String value = StringUtils.substringAfter(line, ":"); | |||
props.put(StringUtils.trimToEmpty(key), StringUtils.trimToEmpty(value)); | |||
} | |||
} | |||
} catch (IOException e) { | |||
// silently ignore | |||
} | |||
return new License(props); | |||
} | |||
} |
@@ -1,161 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.api.config; | |||
import org.apache.commons.codec.binary.Base64; | |||
import org.junit.Test; | |||
import org.sonar.api.utils.DateUtils; | |||
import java.util.Calendar; | |||
import java.util.TimeZone; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class LicenseTest { | |||
private static final String V2_FORMAT = "Foo: bar\n" + | |||
"Organisation: ABC \n" + | |||
"Server: 12345 \n" + | |||
"Product: SQALE\n" + | |||
" Expiration: 2012-05-18 \n" + | |||
"Type: EVALUATION \n" + | |||
"Other: field\n"; | |||
private static final String V1_FORMAT = "Foo: bar\n" + | |||
"Name: ABC \n" + | |||
"Plugin: SQALE\n" + | |||
" Expires: 2012-05-18 \n" + | |||
"Other: field\n" + | |||
"Digest: abcdef\n" + | |||
"Obeo: obeo\n"; | |||
@Test | |||
public void readPlainTest() { | |||
License license = License.readPlainText(V2_FORMAT); | |||
assertThat(license.getOrganization()).isEqualTo("ABC"); | |||
assertThat(license.getServer()).isEqualTo("12345"); | |||
assertThat(license.getProduct()).isEqualTo("SQALE"); | |||
assertThat(license.getExpirationDateAsString()).isEqualTo("2012-05-18"); | |||
assertThat(license.getType()).isEqualTo("EVALUATION"); | |||
} | |||
@Test | |||
public void readPlainText_empty_fields() { | |||
License license = License.readPlainText(""); | |||
assertThat(license.getOrganization()).isNull(); | |||
assertThat(license.getServer()).isNull(); | |||
assertThat(license.getProduct()).isNull(); | |||
assertThat(license.getExpirationDateAsString()).isNull(); | |||
assertThat(license.getExpirationDate()).isNull(); | |||
assertThat(license.getType()).isNull(); | |||
} | |||
@Test | |||
public void readPlainText_not_valid_input() { | |||
License license = License.readPlainText("old pond ... a frog leaps in water’s sound"); | |||
assertThat(license.getOrganization()).isNull(); | |||
assertThat(license.getServer()).isNull(); | |||
assertThat(license.getProduct()).isNull(); | |||
assertThat(license.getExpirationDateAsString()).isNull(); | |||
assertThat(license.getExpirationDate()).isNull(); | |||
assertThat(license.getType()).isNull(); | |||
} | |||
@Test | |||
public void readPlainTest_version_1() { | |||
License license = License.readPlainText(V1_FORMAT); | |||
assertThat(license.getOrganization()).isEqualTo("ABC"); | |||
assertThat(license.getServer()).isNull(); | |||
assertThat(license.getProduct()).isEqualTo("SQALE"); | |||
assertThat(license.getExpirationDateAsString()).isEqualTo("2012-05-18"); | |||
assertThat(license.getType()).isNull(); | |||
} | |||
@Test | |||
public void readBase64() { | |||
License license = License.readBase64(new String(Base64.encodeBase64(V2_FORMAT.getBytes()))); | |||
assertThat(license.getOrganization()).isEqualTo("ABC"); | |||
assertThat(license.getServer()).isEqualTo("12345"); | |||
assertThat(license.getProduct()).isEqualTo("SQALE"); | |||
assertThat(license.getExpirationDateAsString()).isEqualTo("2012-05-18"); | |||
assertThat(license.getType()).isEqualTo("EVALUATION"); | |||
} | |||
@Test | |||
public void trimBeforeReadingBase64() { | |||
String encodedKeyWithTrailingWhiteSpaces = "Rm9vOiBiYXIKT3JnYW5pc2F0aW9uOiBBQkMgClNlcnZlcjogMTIzND \n" + | |||
"UgICAKUHJvZHVjdDogU1FBTEUKICBFeHBpcmF0aW9uOiAyMDEyLTA1 \n" + | |||
"LTE4ICAKVHlwZTogIEVWQUxVQVRJT04gICAKT3RoZXI6IGZpZWxkCg==\n"; | |||
License license = License.readBase64(new String(encodedKeyWithTrailingWhiteSpaces.getBytes())); | |||
assertThat(license.getOrganization()).isEqualTo("ABC"); | |||
assertThat(license.getServer()).isEqualTo("12345"); | |||
assertThat(license.getProduct()).isEqualTo("SQALE"); | |||
assertThat(license.getExpirationDateAsString()).isEqualTo("2012-05-18"); | |||
assertThat(license.getType()).isEqualTo("EVALUATION"); | |||
} | |||
@Test | |||
public void readBase64_not_base64() { | |||
License license = License.readBase64("çé '123$@"); | |||
assertThat(license.getOrganization()).isNull(); | |||
assertThat(license.getServer()).isNull(); | |||
assertThat(license.getProduct()).isNull(); | |||
assertThat(license.getExpirationDateAsString()).isNull(); | |||
assertThat(license.getExpirationDate()).isNull(); | |||
assertThat(license.getType()).isNull(); | |||
} | |||
@Test | |||
public void isExpired() { | |||
License license = License.readPlainText(V2_FORMAT); | |||
assertThat(license.isExpired(DateUtils.parseDate("2011-01-01"))).isFalse(); | |||
Calendar sameDay = Calendar.getInstance(TimeZone.getDefault()); | |||
sameDay.setTime(DateUtils.parseDate("2012-05-18")); | |||
assertThat(license.isExpired(sameDay.getTime())).isFalse(); | |||
sameDay.set(Calendar.HOUR_OF_DAY, 15); | |||
assertThat(license.isExpired(sameDay.getTime())).isFalse(); | |||
sameDay.set(Calendar.HOUR_OF_DAY, 23); | |||
sameDay.set(Calendar.MINUTE, 59); | |||
sameDay.set(Calendar.SECOND, 59); | |||
assertThat(license.isExpired(sameDay.getTime())).isFalse(); | |||
// The day after | |||
sameDay.add(Calendar.SECOND, 1); | |||
assertThat(license.isExpired(sameDay.getTime())).isTrue(); | |||
assertThat(license.isExpired(DateUtils.parseDate("2013-06-23"))).isTrue(); | |||
} | |||
@Test | |||
public void otherProperties() { | |||
License license = License.readPlainText(V2_FORMAT); | |||
assertThat(license.additionalProperties().get("Other")).isEqualTo("field"); | |||
assertThat(license.additionalProperties().containsKey("Digest")).isFalse(); | |||
assertThat(license.additionalProperties().containsKey("Obeo")).isFalse(); | |||
} | |||
} |
@@ -27,7 +27,6 @@ import org.sonarqube.ws.client.ce.CeService; | |||
import org.sonarqube.ws.client.components.ComponentsService; | |||
import org.sonarqube.ws.client.custommeasures.CustomMeasuresService; | |||
import org.sonarqube.ws.client.duplications.DuplicationsService; | |||
import org.sonarqube.ws.client.editions.EditionsService; | |||
import org.sonarqube.ws.client.emails.EmailsService; | |||
import org.sonarqube.ws.client.favorites.FavoritesService; | |||
import org.sonarqube.ws.client.favourites.FavouritesService; | |||
@@ -86,7 +85,6 @@ class DefaultWsClient implements WsClient { | |||
private final ComponentsService componentsService; | |||
private final CustomMeasuresService customMeasuresService; | |||
private final DuplicationsService duplicationsService; | |||
private final EditionsService editionsService; | |||
private final EmailsService emailsService; | |||
private final FavoritesService favoritesService; | |||
private final FavouritesService favouritesService; | |||
@@ -138,7 +136,6 @@ class DefaultWsClient implements WsClient { | |||
this.componentsService = new ComponentsService(wsConnector); | |||
this.customMeasuresService = new CustomMeasuresService(wsConnector); | |||
this.duplicationsService = new DuplicationsService(wsConnector); | |||
this.editionsService = new EditionsService(wsConnector); | |||
this.emailsService = new EmailsService(wsConnector); | |||
this.favoritesService = new FavoritesService(wsConnector); | |||
this.favouritesService = new FavouritesService(wsConnector); | |||
@@ -218,11 +215,6 @@ class DefaultWsClient implements WsClient { | |||
return duplicationsService; | |||
} | |||
@Override | |||
public EditionsService editions() { | |||
return editionsService; | |||
} | |||
@Override | |||
public EmailsService emails() { | |||
return emailsService; |
@@ -27,7 +27,6 @@ import org.sonarqube.ws.client.ce.CeService; | |||
import org.sonarqube.ws.client.components.ComponentsService; | |||
import org.sonarqube.ws.client.custommeasures.CustomMeasuresService; | |||
import org.sonarqube.ws.client.duplications.DuplicationsService; | |||
import org.sonarqube.ws.client.editions.EditionsService; | |||
import org.sonarqube.ws.client.emails.EmailsService; | |||
import org.sonarqube.ws.client.favorites.FavoritesService; | |||
import org.sonarqube.ws.client.favourites.FavouritesService; | |||
@@ -104,8 +103,6 @@ public interface WsClient { | |||
DuplicationsService duplications(); | |||
EditionsService editions(); | |||
EmailsService emails(); | |||
FavoritesService favorites(); |
@@ -1,47 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.sonarqube.ws.client.editions; | |||
import java.util.List; | |||
import javax.annotation.Generated; | |||
/** | |||
* This is part of the internal API. | |||
* This is a POST request. | |||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/editions/apply_license">Further information about this action online (including a response example)</a> | |||
* @since 6.7 | |||
*/ | |||
@Generated("sonar-ws-generator") | |||
public class ApplyLicenseRequest { | |||
private String license; | |||
/** | |||
* This is a mandatory parameter. | |||
*/ | |||
public ApplyLicenseRequest setLicense(String license) { | |||
this.license = license; | |||
return this; | |||
} | |||
public String getLicense() { | |||
return license; | |||
} | |||
} |
@@ -1,125 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.sonarqube.ws.client.editions; | |||
import java.util.stream.Collectors; | |||
import javax.annotation.Generated; | |||
import org.sonarqube.ws.MediaTypes; | |||
import org.sonarqube.ws.client.BaseService; | |||
import org.sonarqube.ws.client.GetRequest; | |||
import org.sonarqube.ws.client.PostRequest; | |||
import org.sonarqube.ws.client.WsConnector; | |||
import org.sonarqube.ws.Editions.FormDataResponse; | |||
import org.sonarqube.ws.Editions.PreviewResponse; | |||
import org.sonarqube.ws.Editions.StatusResponse; | |||
/** | |||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/editions">Further information about this web service online</a> | |||
*/ | |||
@Generated("sonar-ws-generator") | |||
public class EditionsService extends BaseService { | |||
public EditionsService(WsConnector wsConnector) { | |||
super(wsConnector, "api/editions"); | |||
} | |||
/** | |||
* | |||
* This is part of the internal API. | |||
* This is a POST request. | |||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/editions/apply_license">Further information about this action online (including a response example)</a> | |||
* @since 6.7 | |||
*/ | |||
public String applyLicense(ApplyLicenseRequest request) { | |||
return call( | |||
new PostRequest(path("apply_license")) | |||
.setParam("license", request.getLicense()) | |||
.setMediaType(MediaTypes.JSON) | |||
).content(); | |||
} | |||
/** | |||
* | |||
* This is part of the internal API. | |||
* This is a POST request. | |||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/editions/clear_error_message">Further information about this action online (including a response example)</a> | |||
* @since 6.7 | |||
*/ | |||
public void clearErrorMessage() { | |||
call( | |||
new PostRequest(path("clear_error_message")) | |||
.setMediaType(MediaTypes.JSON) | |||
).content(); | |||
} | |||
/** | |||
* | |||
* This is part of the internal API. | |||
* This is a GET request. | |||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/editions/form_data">Further information about this action online (including a response example)</a> | |||
* @since 6.7 | |||
*/ | |||
public FormDataResponse formData() { | |||
return call( | |||
new GetRequest(path("form_data")), | |||
FormDataResponse.parser()); | |||
} | |||
/** | |||
* | |||
* This is part of the internal API. | |||
* This is a POST request. | |||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/editions/preview">Further information about this action online (including a response example)</a> | |||
* @since 6.7 | |||
*/ | |||
public PreviewResponse preview(PreviewRequest request) { | |||
return call( | |||
new PostRequest(path("preview")) | |||
.setParam("license", request.getLicense()), | |||
PreviewResponse.parser()); | |||
} | |||
/** | |||
* | |||
* This is part of the internal API. | |||
* This is a GET request. | |||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/editions/status">Further information about this action online (including a response example)</a> | |||
* @since 6.7 | |||
*/ | |||
public StatusResponse status() { | |||
return call( | |||
new GetRequest(path("status")), | |||
StatusResponse.parser()); | |||
} | |||
/** | |||
* | |||
* This is part of the internal API. | |||
* This is a POST request. | |||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/editions/uninstall">Further information about this action online (including a response example)</a> | |||
* @since 6.7 | |||
*/ | |||
public void uninstall() { | |||
call( | |||
new PostRequest(path("uninstall")) | |||
.setMediaType(MediaTypes.JSON) | |||
).content(); | |||
} | |||
} |
@@ -1,47 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.sonarqube.ws.client.editions; | |||
import java.util.List; | |||
import javax.annotation.Generated; | |||
/** | |||
* This is part of the internal API. | |||
* This is a POST request. | |||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/editions/preview">Further information about this action online (including a response example)</a> | |||
* @since 6.7 | |||
*/ | |||
@Generated("sonar-ws-generator") | |||
public class PreviewRequest { | |||
private String license; | |||
/** | |||
* This is a mandatory parameter. | |||
*/ | |||
public PreviewRequest setLicense(String license) { | |||
this.license = license; | |||
return this; | |||
} | |||
public String getLicense() { | |||
return license; | |||
} | |||
} |
@@ -1,26 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
@Generated("sonar-ws-generator") | |||
package org.sonarqube.ws.client.editions; | |||
import javax.annotation.ParametersAreNonnullByDefault; | |||
import javax.annotation.Generated; | |||
@@ -1,60 +0,0 @@ | |||
// SonarQube, open source software quality management tool. | |||
// Copyright (C) 2008-2016 SonarSource | |||
// mailto:contact AT sonarsource DOT com | |||
// | |||
// SonarQube 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. | |||
// | |||
// SonarQube 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. | |||
syntax = "proto2"; | |||
package sonarqube.ws.editions; | |||
option java_package = "org.sonarqube.ws"; | |||
option java_outer_classname = "Editions"; | |||
option optimize_for = SPEED; | |||
// GET api/editions/status | |||
// POST api/editions/apply_license | |||
message StatusResponse { | |||
optional string currentEditionKey = 1; | |||
optional InstallationStatus installationStatus = 2; | |||
optional string nextEditionKey = 3; | |||
optional string installError = 4; | |||
} | |||
enum InstallationStatus { | |||
NONE = 0; | |||
AUTOMATIC_IN_PROGRESS = 1; | |||
AUTOMATIC_READY = 2; | |||
MANUAL_IN_PROGRESS = 3; | |||
UNINSTALL_IN_PROGRESS = 4; | |||
} | |||
// POST api/editions/preview | |||
message PreviewResponse { | |||
optional string nextEditionKey = 1; | |||
optional PreviewStatus previewStatus = 2; | |||
} | |||
enum PreviewStatus { | |||
NO_INSTALL = 0; | |||
AUTOMATIC_INSTALL = 1; | |||
MANUAL_INSTALL = 2; | |||
} | |||
// POST api/editions/form_data | |||
message FormDataResponse { | |||
optional string serverId = 1; | |||
optional int64 ncloc = 2; | |||
} |