diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2017-04-25 10:47:37 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2017-04-26 16:34:31 +0200 |
commit | 4d9cbe9276073d6b026cbaf8510743aa0b62c4fb (patch) | |
tree | 6cad753829d8d037a98dffd1a35aade91968170d /server | |
parent | c3403683032b6077ef3e38caad872c040a22daaf (diff) | |
download | sonarqube-4d9cbe9276073d6b026cbaf8510743aa0b62c4fb.tar.gz sonarqube-4d9cbe9276073d6b026cbaf8510743aa0b62c4fb.zip |
SONAR-9126 Allow preventing projects analysis on a organization
Diffstat (limited to 'server')
10 files changed, 301 insertions, 3 deletions
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java index d0600dc7e0d..90fe0838bb2 100644 --- a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java +++ b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java @@ -96,6 +96,7 @@ import org.sonar.server.notification.NotificationCenter; import org.sonar.server.notification.NotificationService; import org.sonar.server.notification.email.AlertsEmailTemplate; import org.sonar.server.notification.email.EmailNotificationChannel; +import org.sonar.server.organization.BillingValidationsProxyImpl; import org.sonar.server.organization.DefaultOrganizationProviderImpl; import org.sonar.server.permission.GroupPermissionChanger; import org.sonar.server.permission.PermissionTemplateService; @@ -289,6 +290,7 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer { ResourceTypes.class, DefaultResourceTypes.get(), Periods.class, + BillingValidationsProxyImpl.class, // quality profile ActiveRuleIndexer.class, diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index 89a1da47da4..edf0062e394 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -88,7 +88,7 @@ public class ComputeEngineContainerImplTest { assertThat(picoContainer.getComponentAdapters()) .hasSize( CONTAINER_ITSELF - + 73 // level 4 + + 74 // level 4 + 4 // content of CeConfigurationModule + 5 // content of CeQueueModule + 3 // content of CeHttpModule diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java index fd8f7cb451d..c89f97140d1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java @@ -38,6 +38,9 @@ import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysi import org.sonar.server.computation.task.projectanalysis.analysis.Organization; import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReader; import org.sonar.server.computation.task.step.ComputationStep; +import org.sonar.server.organization.BillingValidations; +import org.sonar.server.organization.BillingValidations.BillingValidationsException; +import org.sonar.server.organization.BillingValidationsProxy; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.qualityprofile.QualityProfile; @@ -66,14 +69,16 @@ public class LoadReportAnalysisMetadataHolderStep implements ComputationStep { private final MutableAnalysisMetadataHolder mutableAnalysisMetadataHolder; private final DefaultOrganizationProvider defaultOrganizationProvider; private final DbClient dbClient; + private final BillingValidations billingValidations; public LoadReportAnalysisMetadataHolderStep(CeTask ceTask, BatchReportReader reportReader, MutableAnalysisMetadataHolder mutableAnalysisMetadataHolder, - DefaultOrganizationProvider defaultOrganizationProvider, DbClient dbClient) { + DefaultOrganizationProvider defaultOrganizationProvider, DbClient dbClient, BillingValidationsProxy billingValidations) { this.ceTask = ceTask; this.reportReader = reportReader; this.mutableAnalysisMetadataHolder = mutableAnalysisMetadataHolder; this.defaultOrganizationProvider = defaultOrganizationProvider; this.dbClient = dbClient; + this.billingValidations = billingValidations; } @Override @@ -84,6 +89,7 @@ public class LoadReportAnalysisMetadataHolderStep implements ComputationStep { checkProjectKeyConsistency(reportMetadata); Organization organization = toOrganization(ceTask.getOrganizationUuid()); checkOrganizationKeyConsistency(reportMetadata, organization); + checkOrganizationCanExecuteAnalysis(organization); checkQualityProfilesConsistency(reportMetadata, organization); mutableAnalysisMetadataHolder.setRootComponentRef(reportMetadata.getRootComponentRef()); @@ -146,6 +152,14 @@ public class LoadReportAnalysisMetadataHolderStep implements ComputationStep { } } + private void checkOrganizationCanExecuteAnalysis(Organization organization) { + try { + billingValidations.checkOnProjectAnalysis(new BillingValidations.Organization(organization.getKey(), organization.getUuid())); + } catch (BillingValidationsException e) { + throw MessageException.of(e.getMessage()); + } + } + private String resolveReportOrganizationKey(@Nullable String organizationKey) { if (reportBelongsToDefaultOrganization(organizationKey)) { return defaultOrganizationProvider.get().getKey(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidations.java b/server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidations.java new file mode 100644 index 00000000000..1c524acce1a --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidations.java @@ -0,0 +1,76 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.organization; + +import static java.util.Objects.requireNonNull; + +/** + * Available checks that will be done by the billing plugin. + * When the billing plugin is not loaded, no check will be done. + * This is not the interface that should be implemented by the plugin, but {@link BillingValidationsExtension} + */ +public interface BillingValidations { + + /** + * @throws BillingValidationsException when projects analysis on organization is not allowed + */ + void checkOnProjectAnalysis(Organization organization); + + class Organization { + private final String key; + private final String uuid; + + public Organization(String key, String uuid) { + this.key = requireNonNull(key, "Organization key cannot be null"); + this.uuid = requireNonNull(uuid, "Organization uuid cannot be null"); + } + + public String getKey() { + return key; + } + + public String getUuid() { + return uuid; + } + } + + class BillingValidationsException extends RuntimeException { + public BillingValidationsException(String message) { + super(message); + } + + /** + * Does not fill in the stack trace + * + * @see java.lang.Throwable#fillInStackTrace() + */ + @Override + public synchronized Throwable fillInStackTrace() { + return this; + } + + @Override + public String toString() { + return getMessage(); + } + + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidationsExtension.java b/server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidationsExtension.java new file mode 100644 index 00000000000..0b70671d8c9 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidationsExtension.java @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.organization; + +import org.sonar.api.ce.ComputeEngineSide; +import org.sonar.api.server.ServerSide; + +/** + * The billing plugin must implement this interface + */ +@ServerSide +@ComputeEngineSide +public interface BillingValidationsExtension extends BillingValidations { +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidationsProxy.java b/server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidationsProxy.java new file mode 100644 index 00000000000..f0b52192d2b --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidationsProxy.java @@ -0,0 +1,30 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.organization; + +/** + * The goal of this class is to handle the 2 different use case : + * - The billing plugin exists, the proxy will redirect method calls to the plugin + * - No billing plugin, every methods won't do anything + */ +public interface BillingValidationsProxy extends BillingValidations { + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidationsProxyImpl.java b/server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidationsProxyImpl.java new file mode 100644 index 00000000000..c091e346b9e --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidationsProxyImpl.java @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.organization; + +public class BillingValidationsProxyImpl implements BillingValidationsProxy { + + private final BillingValidationsExtension billingValidationsExtension; + + public BillingValidationsProxyImpl(BillingValidationsExtension billingValidationsExtension) { + this.billingValidationsExtension = billingValidationsExtension; + } + + // Used when no plugin is providing the extension + public BillingValidationsProxyImpl() { + this.billingValidationsExtension = null; + } + + @Override + public void checkOnProjectAnalysis(Organization organization) { + if (billingValidationsExtension == null) { + return; + } + billingValidationsExtension.checkOnProjectAnalysis(organization); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index a979638fdfd..294a3dbde4c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -84,6 +84,7 @@ import org.sonar.server.metric.CoreCustomMetrics; import org.sonar.server.metric.DefaultMetricFinder; import org.sonar.server.metric.ws.MetricsWsModule; import org.sonar.server.notification.NotificationModule; +import org.sonar.server.organization.BillingValidationsProxyImpl; import org.sonar.server.organization.OrganizationCreationImpl; import org.sonar.server.organization.OrganizationValidationImpl; import org.sonar.server.organization.ws.OrganizationsWsModule; @@ -256,6 +257,7 @@ public class PlatformLevel4 extends PlatformLevel { OrganizationValidationImpl.class, OrganizationCreationImpl.class, OrganizationsWsModule.class, + BillingValidationsProxyImpl.class, // quality profile DefinedQProfileRepositoryImpl.class, diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java index 8ae21842cc7..bc0a286f29e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.ArgumentCaptor; import org.sonar.api.utils.MessageException; import org.sonar.api.utils.System2; import org.sonar.ce.queue.CeTask; @@ -34,11 +35,17 @@ import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysi import org.sonar.server.computation.task.projectanalysis.analysis.Organization; import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReaderRule; import org.sonar.server.computation.task.step.ComputationStep; +import org.sonar.server.organization.BillingValidations; +import org.sonar.server.organization.BillingValidations.BillingValidationsException; +import org.sonar.server.organization.BillingValidationsProxy; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.organization.TestDefaultOrganizationProvider; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +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 LoadReportAnalysisMetadataHolderStepTest { @@ -58,6 +65,7 @@ public class LoadReportAnalysisMetadataHolderStepTest { private DbClient dbClient = dbTester.getDbClient(); private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(dbTester); + private BillingValidationsProxy billingValidations = mock(BillingValidationsProxy.class); private ComputationStep underTest; @Before @@ -308,8 +316,40 @@ public class LoadReportAnalysisMetadataHolderStepTest { underTest.execute(); } + @Test + public void execute_fails_with_MessageException_when_organization_is_not_allowed_to_execute_analysis() { + OrganizationDto organization = dbTester.organizations().insert(); + reportReader.setMetadata(newBatchReportBuilder() + .setOrganizationKey(organization.getKey()) + .build()); + ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization.getUuid())); + doThrow(new BillingValidationsException("This organization cannot execute project analysis")).when(billingValidations) + .checkOnProjectAnalysis(any(BillingValidations.Organization.class)); + + expectedException.expect(MessageException.class); + expectedException.expectMessage("This organization cannot execute project analysis"); + + underTest.execute(); + } + + @Test + public void execute_does_no_fails_when_organization_is_allowed_to_execute_analysis() { + OrganizationDto organization = dbTester.organizations().insert(); + reportReader.setMetadata(newBatchReportBuilder() + .setOrganizationKey(organization.getKey()) + .build()); + ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization.getUuid())); + + underTest.execute(); + + ArgumentCaptor<BillingValidations.Organization> argumentCaptor = ArgumentCaptor.forClass(BillingValidations.Organization.class); + verify(billingValidations).checkOnProjectAnalysis(argumentCaptor.capture()); + assertThat(argumentCaptor.getValue().getKey()).isEqualTo(organization.getKey()); + assertThat(argumentCaptor.getValue().getUuid()).isEqualTo(organization.getUuid()); + } + private LoadReportAnalysisMetadataHolderStep createStep(CeTask ceTask) { - return new LoadReportAnalysisMetadataHolderStep(ceTask, reportReader, analysisMetadataHolder, defaultOrganizationProvider, dbClient); + return new LoadReportAnalysisMetadataHolderStep(ceTask, reportReader, analysisMetadataHolder, defaultOrganizationProvider, dbClient, billingValidations); } private static ScannerReport.Metadata.Builder newBatchReportBuilder() { diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/BillingValidationsProxyTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/BillingValidationsProxyTest.java new file mode 100644 index 00000000000..649a35cc743 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/organization/BillingValidationsProxyTest.java @@ -0,0 +1,59 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.organization; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.sonar.server.organization.BillingValidations.Organization; + +public class BillingValidationsProxyTest { + + private static String ORGANIZATION_KEY = "ORGANIZATION_KEY"; + private static String ORGANIZATION_UUID = "ORGANIZATION_UUID"; + + private BillingValidationsExtension billingValidationsExtension = mock(BillingValidationsExtension.class); + + private BillingValidationsProxyImpl underTest; + + @Test + public void checkOnProjectAnalysis_calls_extension_when_available() { + underTest = new BillingValidationsProxyImpl(billingValidationsExtension); + + Organization organization = new Organization(ORGANIZATION_KEY, ORGANIZATION_UUID); + underTest.checkOnProjectAnalysis(organization); + + verify(billingValidationsExtension).checkOnProjectAnalysis(organization); + } + + @Test + public void checkOnProjectAnalysis_does_nothing_when_no_extension_available() { + underTest = new BillingValidationsProxyImpl(); + + Organization organization = new Organization(ORGANIZATION_KEY, ORGANIZATION_UUID); + underTest.checkOnProjectAnalysis(organization); + + verifyZeroInteractions(billingValidationsExtension); + } + +} |