Feature add optional name to thing created (#888)

* First implementation pushed because of debugging purpose

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Add name field and tests regarding name field functionality in THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* SonarQube realted changes in name field functionality in THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Add name field and tests regarding name field functionality in UPDATE_ATTRIBUTES

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Adapt documentation due to name field in THING_CREATED and UPDATE_ATTRIBUTES

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Add integration tests regarding name field functionality in THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Reformat after finding format bug regarding THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Reformat after finding the real format bug regarding THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Reformat regarding THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Use constant in THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Format in THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Resolving peer review comments regarding THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Resolving peer review comments (organize imports) regarding THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Refactoring regarding THING_CREATED

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Refactoring due to peer review

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Refactoring due to peer review

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Excluding UPDATE_ATTRIBUTES changes and provide functionality of updating the name property in THING_CREATED message

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Refactoring due to peer review

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Refactoring due to peer review

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Fix SonarQube finding

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Merge master into current branch

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>

* Fix peer review findings

Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>
This commit is contained in:
Ammar Bikic
2019-10-24 12:13:35 +02:00
committed by Dominic Schabel
parent b50a82fc0f
commit af0f7ceb5a
10 changed files with 525 additions and 388 deletions

View File

@@ -97,6 +97,7 @@ import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import com.google.common.collect.Lists;
@@ -238,11 +239,11 @@ public class JpaControllerManagement implements ControllerManagement {
* Constructor.
*
* @param defaultEventInterval
* default timer value to use for interval between events.
* This puts an upper bound for the timer value
* default timer value to use for interval between events. This puts
* an upper bound for the timer value
* @param minimumEventInterval
* for loading {@link DistributionSet#getModules()}. This
* puts a lower bound to the timer value
* for loading {@link DistributionSet#getModules()}. This puts a
* lower bound to the timer value
* @param timeUnit
* representing the unit of time to be used for timer.
*/
@@ -257,16 +258,15 @@ public class JpaControllerManagement implements ControllerManagement {
}
/**
* This method calculates the time interval until the next event based
* on the desired number of events before the time when interval is
* reset to default. The return value is bounded by
* {@link EventTimer#defaultEventInterval} and
* This method calculates the time interval until the next event based on the
* desired number of events before the time when interval is reset to default.
* The return value is bounded by {@link EventTimer#defaultEventInterval} and
* {@link EventTimer#minimumEventInterval}.
*
* @param eventCount
* number of events desired until the interval is reset to
* default. This is not guaranteed as the interval between
* events cannot be less than the minimum interval
* number of events desired until the interval is reset to default.
* This is not guaranteed as the interval between events cannot be
* less than the minimum interval
* @param timerResetTime
* time when exponential forwarding should reset to default
*
@@ -386,16 +386,25 @@ public class JpaControllerManagement implements ControllerManagement {
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address) {
return findOrRegisterTargetIfItDoesNotExist(controllerId, address, null);
}
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = ConcurrencyFailureException.class, exclude = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address,
final String name) {
final Specification<JpaTarget> spec = (targetRoot, query, cb) -> cb
.equal(targetRoot.get(JpaTarget_.controllerId), controllerId);
return targetRepository.findOne(spec).map(target -> updateTargetStatus(target, address))
.orElseGet(() -> createTarget(controllerId, address));
return targetRepository.findOne(spec).map(target -> updateTarget(target, address, name))
.orElseGet(() -> createTarget(controllerId, address, name));
}
private Target createTarget(final String controllerId, final URI address) {
private Target createTarget(final String controllerId, final URI address, String name) {
final Target result = targetRepository.save((JpaTarget) entityFactory.target().create()
.controllerId(controllerId).description("Plug and Play target: " + controllerId).name(controllerId)
.controllerId(controllerId).description("Plug and Play target: " + controllerId).name((StringUtils.hasText(name) ? name : controllerId))
.status(TargetUpdateStatus.REGISTERED).lastTargetQuery(System.currentTimeMillis())
.address(Optional.ofNullable(address).map(URI::toString).orElse(null)).build());
@@ -458,9 +467,8 @@ public class JpaControllerManagement implements ControllerManagement {
/**
* Sets {@link Target#getLastTargetQuery()} by native SQL in order to avoid
* raising opt lock revision as this update is not mission critical and in
* fact only written by {@link ControllerManagement}, i.e. the target
* itself.
* raising opt lock revision as this update is not mission critical and in fact
* only written by {@link ControllerManagement}, i.e. the target itself.
*/
private void setLastTargetQuery(final String tenant, final long currentTimeMillis, final List<String> chunk) {
final Map<String, String> paramMapping = Maps.newHashMapWithExpectedSize(chunk.size());
@@ -488,39 +496,40 @@ public class JpaControllerManagement implements ControllerManagement {
}
/**
* Stores target directly to DB in case either {@link Target#getAddress()}
* or {@link Target#getUpdateStatus()} changes or the buffer queue is full.
* Stores target directly to DB in case either {@link Target#getAddress()} or
* {@link Target#getUpdateStatus()} or {@link Target#getName()} changes or the buffer queue is full.
*
*/
private Target updateTargetStatus(final JpaTarget toUpdate, final URI address) {
boolean storeEager = isStoreEager(toUpdate, address);
if (TargetUpdateStatus.UNKNOWN == toUpdate.getUpdateStatus()) {
toUpdate.setUpdateStatus(TargetUpdateStatus.REGISTERED);
storeEager = true;
}
if (storeEager || !queue.offer(new TargetPoll(toUpdate))) {
toUpdate.setAddress(address.toString());
private Target updateTarget(final JpaTarget toUpdate, final URI address, final String name) {
if (isStoreEager(toUpdate, address, name) || !queue.offer(new TargetPoll(toUpdate))) {
if (isAddressChanged(toUpdate.getAddress(), address)) {
toUpdate.setAddress(address.toString());
}
if (isNameChanged(toUpdate.getName(), name)) {
toUpdate.setName(name);
}
if (isStatusUnknown(toUpdate.getUpdateStatus())) {
toUpdate.setUpdateStatus(TargetUpdateStatus.REGISTERED);
}
toUpdate.setLastTargetQuery(System.currentTimeMillis());
afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher()
.publishEvent(new TargetPollEvent(toUpdate, eventPublisherHolder.getApplicationId())));
return targetRepository.save(toUpdate);
}
return toUpdate;
}
private boolean isStoreEager(final JpaTarget toUpdate, final URI address) {
if (repositoryProperties.isEagerPollPersistence()) {
return true;
} else if (toUpdate.getAddress() == null) {
return true;
} else {
return !toUpdate.getAddress().equals(address);
}
private boolean isStoreEager(final JpaTarget toUpdate, final URI address, final String name) {
return repositoryProperties.isEagerPollPersistence() || isAddressChanged(toUpdate.getAddress(), address)
|| isNameChanged(toUpdate.getName(), name) || isStatusUnknown(toUpdate.getUpdateStatus());
}
private boolean isAddressChanged(final URI addressToUpdate, final URI address) {
return addressToUpdate == null || !addressToUpdate.equals(address);
}
private boolean isNameChanged(final String nameToUpdate, final String name) {
return StringUtils.hasText(name) && !nameToUpdate.equals(name);
}
private boolean isStatusUnknown(final TargetUpdateStatus statusToUpdate) {
return TargetUpdateStatus.UNKNOWN == statusToUpdate;
}
@Override
@@ -596,8 +605,8 @@ public class JpaControllerManagement implements ControllerManagement {
* ActionStatus updates are allowed mainly if the action is active. If the
* action is not active we accept further status updates if permitted so by
* repository configuration. In this case, only the values: Status.ERROR and
* Status.FINISHED are allowed. In the case of a DOWNLOAD_ONLY action, we
* accept status updates only once.
* Status.FINISHED are allowed. In the case of a DOWNLOAD_ONLY action, we accept
* status updates only once.
*/
private boolean isUpdatingActionStatusAllowed(final JpaAction action, final JpaActionStatus actionStatus) {
@@ -732,8 +741,8 @@ public class JpaControllerManagement implements ControllerManagement {
final UpdateMode mode) {
/*
* Constraints on attribute keys & values are not validated by
* EclipseLink. Hence, they are validated here.
* Constraints on attribute keys & values are not validated by EclipseLink.
* Hence, they are validated here.
*/
if (data.entrySet().stream().anyMatch(e -> !isAttributeEntryValid(e))) {
throw new InvalidTargetAttributeException();
@@ -744,7 +753,6 @@ public class JpaControllerManagement implements ControllerManagement {
// get the modifiable attribute map
final Map<String, String> controllerAttributes = target.getControllerAttributes();
final UpdateMode updateMode = mode != null ? mode : UpdateMode.MERGE;
switch (updateMode) {
case REMOVE:
@@ -811,8 +819,8 @@ public class JpaControllerManagement implements ControllerManagement {
}
/**
* Registers retrieved status for given {@link Target} and {@link Action} if
* it does not exist yet.
* Registers retrieved status for given {@link Target} and {@link Action} if it
* does not exist yet.
*
* @param actionId
* to the handle status for

View File

@@ -92,8 +92,8 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verifies that management get access react as specified on calls for non existing entities by means "
+ "of Optional not present.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 1)})
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
public void nonExistingEntityAccessReturnsNotPresent() {
final Target target = testdataFactory.createTarget();
final SoftwareModule module = testdataFactory.createSoftwareModuleOs();
@@ -113,8 +113,8 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verifies that management queries react as specified on calls for non existing entities "
+ " by means of throwing EntityNotFoundException.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 1)})
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
public void entityQueriesReferringToNotExistingEntitiesThrowsException() throws URISyntaxException {
final Target target = testdataFactory.createTarget();
final SoftwareModule module = testdataFactory.createSoftwareModuleOs();
@@ -149,13 +149,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Controller confirms successful update with FINISHED status.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerConfirmsUpdateWithFinished() {
final Long actionId = createTargetAndAssignDs();
@@ -172,11 +172,11 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Controller confirmation fails with invalid messages.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerConfirmationFailsWithInvalidMessages() {
final Long actionId = createTargetAndAssignDs();
@@ -200,14 +200,14 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Controller confirms successful update with FINISHED status on a action that is on canceling. "
+ "Reason: The decision to ignore the cancellation is in fact up to the controller.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerConfirmsUpdateWithFinishedAndIgnoresCancellationWithThat() {
final Long actionId = createTargetAndAssignDs();
deploymentManagement.cancelAction(actionId);
@@ -223,11 +223,11 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Update server rejects cancellation feedback if action is not in CANCELING state.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void cancellationFeedbackRejectedIfActionIsNotInCanceling() {
final Long actionId = createTargetAndAssignDs();
@@ -245,13 +245,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Controller confirms action cancellation with FINISHED status.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerConfirmsActionCancellationWithFinished() {
final Long actionId = createTargetAndAssignDs();
@@ -272,13 +272,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Controller confirms action cancellation with FINISHED status.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerConfirmsActionCancellationWithCanceled() {
final Long actionId = createTargetAndAssignDs();
@@ -300,13 +300,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Controller rejects action cancellation with CANCEL_REJECTED status. Action goes back to RUNNING status as it expects "
+ "that the controller will continue the original update.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 1),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerRejectsActionCancellationWithReject() {
final Long actionId = createTargetAndAssignDs();
@@ -328,13 +328,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Controller rejects action cancellation with ERROR status. Action goes back to RUNNING status as it expects "
+ "that the controller will continue the original update.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 1),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerRejectsActionCancellationWithError() {
final Long actionId = createTargetAndAssignDs();
@@ -465,12 +465,12 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verifies that assignment verification works based on SHA1 hash. By design it is not important which artifact "
+ "is actually used for the check as long as they have an identical binary, i.e. same SHA1 hash. ")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 2)})
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 2) })
public void hasTargetArtifactAssignedIsTrueWithMultipleArtifacts() {
final int artifactSize = 5 * 1024;
final byte[] random = RandomUtils.nextBytes(artifactSize);
@@ -500,7 +500,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Register a controller which does not exist")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 2)})
public void findOrRegisterTargetIfItDoesNotExist() {
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST);
@@ -511,6 +511,20 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(targetRepository.count()).as("Only 1 target should be registered").isEqualTo(1L);
}
@Test
@Description("Register a controller with name which does not exist and update its name")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 1) })
public void findOrRegisterTargetIfItDoesNotExistWithName() {
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST, "TestName");
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
"ChangedTestName");
assertThat(target.getId()).as("Target should be the equals").isEqualTo(sameTarget.getId());
assertThat(target.getName()).as("Taget names should be different").isNotEqualTo(sameTarget.getName());
assertThat(sameTarget.getName()).as("Taget name should be changed").isEqualTo("ChangedTestName");
assertThat(targetRepository.count()).as("Only 1 target should be registred").isEqualTo(1L);
}
@Test
@Description("Tries to register a target with an invalid controller id")
public void findOrRegisterTargetIfItDoesNotExistThrowsExceptionForInvalidControllerIdParam() {
@@ -556,8 +570,8 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Register a controller which does not exist, when a ConcurrencyFailureException is raised, the "
+ "exception is not rethrown when the max retries are not yet reached")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1)})
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void findOrRegisterTargetIfItDoesNotExistDoesNotThrowExceptionBeforeMaxRetries() {
final TargetRepository mockTargetRepository = Mockito.mock(TargetRepository.class);
@@ -580,6 +594,28 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
}
}
@Test
@Description("Register a controller which does not exist, then update the controller twice, first time by providing a name property and second time without a new name")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 3) ,
@Expect(type = TargetUpdatedEvent.class, count = 1)})
public void findOrRegisterTargetIfItDoesNotExistDoesUpdateNameOnExistingTargetProperly() {
String controllerId = "12345";
String targetName = "UpdatedName";
final Target newTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, LOCALHOST);
assertThat(newTarget.getName()).isEqualTo(controllerId);
Target firstTimeUpdatedTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, LOCALHOST, targetName);
assertThat(firstTimeUpdatedTarget.getName()).isEqualTo(targetName);
//Name should not change to default (name=targetId) if target is updated without new name provided
Target secondTimeUpdatedTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, LOCALHOST);
assertThat(secondTimeUpdatedTarget.getName()).isEqualTo(targetName);
}
@Test
@Description("Register a controller which does not exist, if a EntityAlreadyExistsException is raised, the "
+ "exception is rethrown and no further retries will be attempted")
@@ -629,9 +665,9 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verify that targetVisible metadata is returned from repository")
@ExpectEvents({@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 6)})
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 6) })
public void findTargetVisibleMetaDataBySoftwareModuleId() {
final DistributionSet set = testdataFactory.createDistributionSet();
testdataFactory.addSoftwareModuleMetadata(set);
@@ -646,8 +682,8 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verify that controller registration does not result in a TargetPollEvent if feature is disabled")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 0)})
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 0) })
public void targetPollEventNotSendIfDisabled() {
repositoryProperties.setPublishTargetPollEvent(false);
controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST);
@@ -656,12 +692,12 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Controller tries to finish an update process after it has been finished by an error action status.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void tryToFinishWithErrorUpdateProcessMoreThanOnce() {
final Long actionId = createTargetAndAssignDs();
@@ -702,13 +738,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Controller tries to finish an update process after it has been finished by an FINISHED action status.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void tryToFinishUpdateProcessMoreThanOnce() {
final Long actionId = prepareFinishedUpdate().getId();
@@ -739,13 +775,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Controller tries to send an update feedback after it has been finished which is reject as the repository is "
+ "configured to reject that.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void sendUpdatesForFinishUpdateProcessDroppedIfDisabled() {
repositoryProperties.setRejectActionStatusForClosedAction(true);
@@ -766,13 +802,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Controller tries to send an update feedback after it has been finished which is accepted as the repository is "
+ "configured to accept them.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void sendUpdatesForFinishUpdateProcessAcceptedIfEnabled() {
repositoryProperties.setRejectActionStatusForClosedAction(false);
@@ -792,8 +828,8 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Ensures that target attribute update is reflected by the repository.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 3)})
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 3) })
public void updateTargetAttributes() throws Exception {
final String controllerId = "test123";
final Target target = testdataFactory.createTarget(controllerId);
@@ -848,8 +884,8 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Ensures that target attributes can be updated using different update modes.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 4)})
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 4) })
public void updateTargetAttributesWithDifferentUpdateModes() {
final String controllerId = "testCtrl";
@@ -946,8 +982,8 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Ensures that target attribute update fails if quota hits.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2)})
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2) })
public void updateTargetAttributesFailsIfTooManyEntries() throws Exception {
final String controllerId = "test123";
final int allowedAttributes = quotaManagement.getMaxAttributeEntriesPerTarget();
@@ -1025,11 +1061,11 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Controller providing status entries fails if providing more than permitted by quota.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerProvidesIntermediateFeedbackFailsIfQuotaHit() {
final int allowStatusEntries = 10;
final Long actionId = createTargetAndAssignDs();
@@ -1073,11 +1109,11 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verifies that the quota specifying the maximum number of status entries per action is enforced.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 2),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 2),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6) })
public void addActionStatusUpdatesUntilQuotaIsExceeded() {
// any distribution set assignment causes 1 status entity to be created
@@ -1109,11 +1145,11 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verifies that the quota specifying the maximum number of messages per action status is enforced.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void createActionStatusWithTooManyMessages() {
final int maxMessages = quotaManagement.getMaxMessagesPerActionStatus();
@@ -1137,11 +1173,11 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verifies that a DOWNLOAD_ONLY action is not marked complete when the controller reports DOWNLOAD")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerReportsDownloadForDownloadOnlyAction() {
testdataFactory.createTarget();
final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID);
@@ -1158,13 +1194,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verifies that a DOWNLOAD_ONLY action is marked complete once the controller reports DOWNLOADED")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerReportsDownloadedForDownloadOnlyAction() {
testdataFactory.createTarget();
final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID);
@@ -1181,13 +1217,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verifies that a controller can report a FINISHED event for a DOWNLOAD_ONLY non-active action.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 3),
@Expect(type = TargetAttributesRequestedEvent.class, count = 2),
@Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerReportsActionFinishedForDownloadOnlyActionThatIsNotActive() {
testdataFactory.createTarget();
final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID);
@@ -1204,13 +1240,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verifies that multiple DOWNLOADED events for a DOWNLOAD_ONLY action are handled.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAttributesRequestedEvent.class, count = 3),
@Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerReportsMultipleDownloadedForDownloadOnlyAction() {
testdataFactory.createTarget();
final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID);
@@ -1229,13 +1265,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test(expected = QuotaExceededException.class)
@Description("Verifies that quota is asserted when a controller reports too many DOWNLOADED events for a "
+ "DOWNLOAD_ONLY action.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = TargetAttributesRequestedEvent.class, count = 9),
@Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void quotaExceptionWhencontrollerReportsTooManyDownloadedMessagesForDownloadOnlyAction() {
final int maxMessages = quotaManagement.getMaxMessagesPerActionStatus();
testdataFactory.createTarget();
@@ -1248,13 +1284,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verifies that quota is enforced for UpdateActionStatus events for DOWNLOAD_ONLY assignments.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAttributesRequestedEvent.class, count = 9),
@Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void quotaExceededExceptionWhenControllerReportsTooManyUpdateActionStatusMessagesForDownloadOnlyAction() {
final int maxMessages = quotaManagement.getMaxMessagesPerActionStatus();
testdataFactory.createTarget();
@@ -1279,11 +1315,11 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verifies that quota is enforced for UpdateActionStatus events for FORCED assignments.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void quotaExceededExceptionWhenControllerReportsTooManyUpdateActionStatusMessagesForForced() {
final int maxMessages = quotaManagement.getMaxMessagesPerActionStatus();
final Long actionId = createTargetAndAssignDs();
@@ -1344,13 +1380,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verifies that a target can report FINISHED/ERROR updates for DOWNLOAD_ONLY assignments regardless of "
+ "repositoryProperties.rejectActionStatusForClosedAction value.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 4),
@Expect(type = ActionCreatedEvent.class, count = 4), @Expect(type = TargetUpdatedEvent.class, count = 12),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 4),
@Expect(type = TargetAttributesRequestedEvent.class, count = 6),
@Expect(type = ActionUpdatedEvent.class, count = 8),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 12)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 12) })
public void targetCanAlwaysReportFinishedOrErrorAfterActionIsClosedForDownloadOnlyAssignments() {
testdataFactory.createTarget();
@@ -1395,13 +1431,13 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verifies that a controller can report a FINISHED event for a DOWNLOAD_ONLY action after having"
+ " installed an intermediate update.")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 5),
@Expect(type = TargetAttributesRequestedEvent.class, count = 3),
@Expect(type = ActionUpdatedEvent.class, count = 3),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6)})
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6) })
public void controllerReportsFinishedForOldDownloadOnlyActionAfterSuccessfulForcedAssignment() {
testdataFactory.createTarget();