Merge remote-tracking branch 'eclipse/master' into

fix-set-db-read-uncommited

Fixed small typo


Signed-off-by: Kai Zimmermann <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2016-05-02 18:10:21 +02:00
50 changed files with 1241 additions and 662 deletions

View File

@@ -11,9 +11,6 @@ package org.eclipse.hawkbit;
import javax.persistence.EntityManager;
import javax.transaction.Transaction;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.springframework.beans.factory.annotation.Autowired;
@@ -38,38 +35,12 @@ public class MultiTenantJpaTransactionManager extends JpaTransactionManager {
protected void doBegin(final Object transaction, final TransactionDefinition definition) {
super.doBegin(transaction, definition);
final EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager
.getResource(getEntityManagerFactory());
final EntityManager em = emHolder.getEntityManager();
if (notTenantManagement(definition) && notCurrentTenantKeyGenerator(definition)
&& notRolloutScheduler(definition) && notGetOrCreateTenantMetadata(definition)) {
final String currentTenant = tenantAware.getCurrentTenant();
if (currentTenant == null) {
throw new TenantNotExistException("Tenant Unknown. Canceling transaction.");
}
final String currentTenant = tenantAware.getCurrentTenant();
if (currentTenant != null) {
final EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager
.getResource(getEntityManagerFactory());
final EntityManager em = emHolder.getEntityManager();
em.setProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, currentTenant.toUpperCase());
}
}
private boolean notGetOrCreateTenantMetadata(final TransactionDefinition definition) {
return !definition.getName()
.startsWith(SystemManagement.class.getCanonicalName() + ".getOrCreateTenantMetadata");
}
private boolean notRolloutScheduler(final TransactionDefinition definition) {
return !definition.getName().startsWith(RolloutManagement.class.getCanonicalName() + ".rolloutScheduler");
}
private boolean notCurrentTenantKeyGenerator(final TransactionDefinition definition) {
return !definition.getName()
.startsWith(SystemManagement.class.getCanonicalName() + ".currentTenantKeyGenerator");
}
private boolean notTenantManagement(final TransactionDefinition definition) {
return !definition.getName().startsWith(SystemManagement.class.getCanonicalName() + ".deleteTenant")
&& !definition.getName().startsWith(SystemManagement.class.getCanonicalName() + ".findTenants");
}
}

View File

@@ -59,6 +59,10 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Isolation;
<<<<<<< HEAD
=======
import org.springframework.transaction.annotation.Propagation;
>>>>>>> eclipse/master
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
@@ -135,7 +139,7 @@ public class RolloutManagement {
/**
* Retrieves all rollouts.
*
*
* @param page
* the page request to sort and limit the result
* @return a page of found rollouts
@@ -147,7 +151,7 @@ public class RolloutManagement {
/**
* Retrieves all rollouts found by the given specification.
*
*
* @param specification
* the specification to filter rollouts
* @param page
@@ -164,7 +168,7 @@ public class RolloutManagement {
/**
* Retrieves a specific rollout by its ID.
*
*
* @param rolloutId
* the ID of the rollout to retrieve
* @return the founded rollout or {@code null} if rollout with given ID does
@@ -181,13 +185,13 @@ public class RolloutManagement {
* which are effected by this rollout to create. The targets will then be
* split up into groups. The size of the groups can be defined in the
* {@code groupSize} parameter.
*
*
* The rollout is not started. Only the preparation of the rollout is done,
* persisting and creating all the necessary groups. The Rollout and the
* groups are persisted in {@link RolloutStatus#READY} and
* {@link RolloutGroupStatus#READY} so they can be started
* {@link #startRollout(Rollout)}.
*
*
* @param rollout
* the rollout entity to create
* @param amountGroup
@@ -196,7 +200,7 @@ public class RolloutManagement {
* the rolloutgroup conditions and actions which should be
* applied for each {@link RolloutGroup}
* @return the persisted rollout.
*
*
* @throws IllegalArgumentException
* in case the given groupSize is zero or lower.
*/
@@ -216,22 +220,22 @@ public class RolloutManagement {
* will be done synchronously and will be returned. The targets will then be
* split up into groups. The size of the groups can be defined in the
* {@code groupSize} parameter.
*
*
* The creation of the rollout groups is executed asynchronously due it
* might take some time to split up the targets into groups. The creation of
* the {@link RolloutGroup} is published as event
* {@link RolloutGroupCreatedEvent}.
*
*
* The rollout is in status {@link RolloutStatus#CREATING} until all rollout
* groups has been created and the targets are split up, then the rollout
* will change the status to {@link RolloutStatus#READY}.
*
*
* The rollout is not started. Only the preparation of the rollout is done,
* persisting and creating all the necessary groups. The Rollout and the
* groups are persisted in {@link RolloutStatus#READY} and
* {@link RolloutGroupStatus#READY} so they can be started
* {@link #startRollout(Rollout)}.
*
*
* @param rollout
* the rollout to be created
* @param amountGroup
@@ -260,13 +264,7 @@ public class RolloutManagement {
entityManager.flush();
executor.execute(() -> {
try {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("creatingRollout");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
new TransactionTemplate(txManager, def).execute(status -> {
createRolloutGroups(amountGroup, conditions, savedRollout);
return null;
});
createRolloutGroupsInNewTransaction(amountGroup, conditions, savedRollout);
} finally {
creatingRollouts.remove(savedRollout.getName());
}
@@ -289,13 +287,22 @@ public class RolloutManagement {
}
}
private Rollout createRolloutGroupsInNewTransaction(final int amountOfGroups, final RolloutGroupConditions conditions,
final Rollout savedRollout) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("creatingRollout");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
return new TransactionTemplate(txManager, def)
.execute(status -> createRolloutGroups(amountOfGroups, conditions, savedRollout));
}
/**
* Method for creating rollout groups and calculating group sizes. Group
* sizes are calculated by dividing the total count of targets through the
* amount of given groups. In same cases this will lead to less rollout
* groups than given by client.
*
* @param amountGroup
*
* @param amountOfGroups
* the amount of groups
* @param conditions
* the rollout group conditions
@@ -303,17 +310,17 @@ public class RolloutManagement {
* the rollout
* @return the rollout with created groups
*/
private Rollout createRolloutGroups(final int amountGroup, final RolloutGroupConditions conditions,
private Rollout createRolloutGroups(final int amountOfGroups, final RolloutGroupConditions conditions,
final Rollout savedRollout) {
int pageIndex = 0;
int groupIndex = 0;
final Long totalCount = savedRollout.getTotalTargets();
final int groupSize = (int) Math.ceil((double) totalCount / (double) amountGroup);
final int groupSize = (int) Math.ceil((double) totalCount / (double) amountOfGroups);
// validate if the amount of groups that will be created are the amount
// of groups that the client what's to have created.
int amountGroupValidated = amountGroup;
int amountGroupValidated = amountOfGroups;
final int amountGroupCreation = (int) (Math.ceil((double) totalCount / (double) groupSize));
if (amountGroupCreation == (amountGroup - 1)) {
if (amountGroupCreation == (amountOfGroups - 1)) {
amountGroupValidated--;
}
RolloutGroup lastSavedGroup = null;
@@ -357,14 +364,14 @@ public class RolloutManagement {
* for each affected target in the rollout. The actions of the first group
* will be started immediately {@link RolloutGroupStatus#RUNNING} as the
* other groups will be {@link RolloutGroupStatus#SCHEDULED} state.
*
*
* The rollout itself will be then also in {@link RolloutStatus#RUNNING}.
*
*
* @param rollout
* the rollout to be started
*
*
* @return started rollout
*
*
* @throws RolloutIllegalStateException
* if given rollout is not in {@link RolloutStatus#READY}. Only
* ready rollouts can be started.
@@ -386,14 +393,14 @@ public class RolloutManagement {
* actions of the first group will be started immediately
* {@link RolloutGroupStatus#RUNNING} as the other groups will be
* {@link RolloutGroupStatus#SCHEDULED} state.
*
*
* The rollout itself will be then also in {@link RolloutStatus#RUNNING}.
*
*
* @param rollout
* the rollout to be started
*
*
* @return the started rollout
*
*
* @throws RolloutIllegalStateException
* if given rollout is not in {@link RolloutStatus#READY}. Only
* ready rollouts can be started.
@@ -461,14 +468,14 @@ public class RolloutManagement {
* {@link RolloutGroupStatus#SCHEDULED} will not be started and keep in
* {@link RolloutGroupStatus#SCHEDULED} state until the rollout is
* {@link RolloutManagement#resumeRollout(Rollout)}.
*
*
* Switching the rollout status to {@link RolloutStatus#PAUSED} is
* sufficient due the {@link #checkRunningRollouts(long)} will not check
* this rollout anymore.
*
*
* @param rollout
* the rollout to be paused.
*
*
* @throws RolloutIllegalStateException
* if given rollout is not in {@link RolloutStatus#RUNNING}.
* Only running rollouts can be paused.
@@ -496,7 +503,7 @@ public class RolloutManagement {
* Resumes a paused rollout. The rollout switches back to
* {@link RolloutStatus#RUNNING} state which is then picked up again by the
* {@link #checkRunningRollouts(long)}.
*
*
* @param rollout
* the rollout to be resumed
* @throws RolloutIllegalStateException
@@ -521,7 +528,7 @@ public class RolloutManagement {
* Checking running rollouts. Rollouts which are checked updating the
* {@link Rollout#setLastCheck(long)} to indicate that the current instance
* is handling the specific rollout. This code should run as system-code.
*
*
* <pre>
* {@code
* SystemSecurityContext.runAsSystem(new Callable<Void>() {
@@ -531,21 +538,21 @@ public class RolloutManagement {
* });
* }
* </pre>
*
*
* This method is attend to be called by a scheduler.
* {@link RolloutScheduler}. And must be running in an transaction so it's
* splitted from the scheduler.
*
*
* Rollouts which are currently running are investigated, by means the
* error- and finish condition of running groups in this rollout are
* evaluated.
*
*
* @param delayBetweenChecks
* the time in milliseconds of the delay between the further and
* this check. This check is only applied if the last check is
* less than (lastcheck-delay).
*/
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@@ -728,7 +735,7 @@ public class RolloutManagement {
/**
* Count rollouts by specified filter text.
*
*
* @param searchText
* name or description
* @return total count rollouts for specified filter text.
@@ -750,7 +757,7 @@ public class RolloutManagement {
/**
* * Retrieves a specific rollout by its ID.
*
*
* @param pageable
* the page request to sort and limit the result
* @param searchText
@@ -768,7 +775,7 @@ public class RolloutManagement {
/**
* Retrieves a specific rollout by its name.
*
*
* @param rolloutName
* the name of the rollout to retrieve
* @return the founded rollout or {@code null} if rollout with given name
@@ -781,10 +788,10 @@ public class RolloutManagement {
/**
* Update rollout details.
*
*
* @param rollout
* rollout to be updated
*
*
* @return Rollout updated rollout
*/
@NotNull
@@ -798,7 +805,7 @@ public class RolloutManagement {
/**
* Get count of targets in different status in rollout.
*
*
* @param page
* the page request to sort and limit the result
* @return a list of rollouts with details of targets count for different
@@ -860,7 +867,7 @@ public class RolloutManagement {
/***
* Get finished percentage details for a specified group which is in running
* state.
*
*
* @param rolloutId
* the ID of the {@link Rollout}
* @param rolloutGroup

View File

@@ -165,6 +165,7 @@ public class SystemManagement {
* @return the {@link CurrentTenantKeyGenerator}
*/
@Bean
@Transactional(propagation = Propagation.SUPPORTS)
public CurrentTenantKeyGenerator currentTenantKeyGenerator() {
return new CurrentTenantKeyGenerator();
}
@@ -207,7 +208,6 @@ public class SystemManagement {
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
// tenant independent
public List<String> findTenants() {
return tenantMetaDataRepository.findAll().stream().map(md -> md.getTenant()).collect(Collectors.toList());
}
@@ -222,7 +222,6 @@ public class SystemManagement {
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
// tenant independent
public void deleteTenant(@NotNull final String tenant) {
cacheManager.evictCaches(tenant);
cacheManager.getCache("currentTenant").evict(currentTenantKeyGenerator().generate(null, null));

View File

@@ -24,13 +24,18 @@ import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.eclipse.persistence.annotations.ExistenceChecking;
import org.eclipse.persistence.annotations.ExistenceType;
/**
* @author Michael Hirsch
*
* Entity with JPA annotation to store the information which {@link Target} is
* in a specific {@link RolloutGroup}.
*
*/
@IdClass(RolloutTargetGroupId.class)
@Entity
@Table(name = "sp_rollouttargetgroup")
@ExistenceChecking(ExistenceType.ASSUME_NON_EXISTENCE)
public class RolloutTargetGroup implements Serializable {
private static final long serialVersionUID = 1L;

View File

@@ -78,6 +78,22 @@ public class MultiTenancyEntityTest extends AbstractIntegrationTest {
assertThat(findTargetsForTenant).hasSize(1);
}
@Test
@Description(value = "Ensures that tenant with proper permissions can read and delete other tenants.")
@WithUser(tenantId = "mytenant", allSpPermissions = true)
public void deleteAnotherTenantPossible() throws Exception {
// create target for another tenant
final String anotherTenant = "anotherTenant";
final String controllerAnotherTenant = "anotherController";
createTargetForTenant(controllerAnotherTenant, anotherTenant);
assertThat(systemManagement.findTenants()).as("Expected number if tenants before deletion is").hasSize(3);
systemManagement.deleteTenant(anotherTenant);
assertThat(systemManagement.findTenants()).as("Expected number if tenants after deletion is").hasSize(2);
}
@Test
@Description(value = "Ensures that tenant metadata is retrieved for the current tenant.")
@WithUser(tenantId = "mytenant", autoCreateTenant = false, allSpPermissions = true)