Prioritisation of assignments via mgmt-API (#895)

* Updating the schema for targetfilterquery and rollout
* Updating the weight validation logic and tests
* Make weight optional
* Fix existing multi assignment tests by adding weight, remove weight from TargetFilterQuery
* Add weight validation tests, fix tests
* Add mgmt api tests for assignment and getting action with weight
* Add management layer validation and tests for creating rollouts with weight
* Fix amqp test, add repo level validation to resource tests
* Add weight to rollout mgmt-api and tests
* Add weight to mgmt api target Filter create and update
* Add target filter auto assign weight. disable enforcement of setting a weight in multiassign mode
* Remove ignored tests, fix api doc
* Fix minor findings
* Fix findings
* Remove hardcoded min weight
* Add docu text, fix findings
* Fix api documentation
* Expose weight via DMF
* Expose actions according to weight via ddi
* Fix documentation
* Add method to get actions ordered by weight to deploymentManagement
* Updating the schema for targetfilterquery and rollout
* Updated the indentation
* Updated the helper class, fixed the randomUID in test factory
* Updated the class name with prefix JPA
* Adding the missing License for WeightValidationHelper class
* Adding documentation to the dmf api on weight
* Removed the merger markers
* Updated the class name
* Removed the redundant method
* Addressed final PR comments
* Updated the missing testcase with latest default weight value
* Reverting the default value of weight back to 1000 and updated tests

Signed-off-by: Shruthi Manavalli Ramanna <shruthimanavalli.ramanna@bosch-si.com>
Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>
This commit is contained in:
Stefan Klotz
2019-11-08 10:47:35 +01:00
committed by Stefan Behl
parent 09f2d8a481
commit 9cb5d31396
98 changed files with 2425 additions and 875 deletions

View File

@@ -181,17 +181,19 @@ public class AmqpMessageHandlerServiceTest {
@Description("Tests the creation of a target/thing by calling the same method that incoming RabbitMQ messages would access.")
public void createThing() {
final String knownThingId = "1";
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_CREATED);
messageProperties.setHeader(MessageHeaderKey.THING_ID, "1");
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
final Target targetMock = mock(Target.class);
targetIdCaptor = ArgumentCaptor.forClass(String.class);
uriCaptor = ArgumentCaptor.forClass(URI.class);
final ArgumentCaptor<String> targetIdCaptor = ArgumentCaptor.forClass(String.class);
final ArgumentCaptor<URI> uriCaptor = ArgumentCaptor.forClass(URI.class);
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotExist(targetIdCaptor.capture(),
uriCaptor.capture())).thenReturn(targetMock);
when(controllerManagementMock.findOldestActiveActionByTarget(any())).thenReturn(Optional.empty());
when(controllerManagementMock.findActiveActionWithHighestWeight(any())).thenReturn(Optional.empty());
amqpMessageHandlerService.onMessage(createMessage(new byte[0], getThingCreatedMessageProperties(knownThingId)),
MessageType.THING_CREATED.name(), TENANT, VIRTUAL_HOST);
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, VIRTUAL_HOST);
// verify
assertThat(targetIdCaptor.getValue()).as("Thing id is wrong").isEqualTo(knownThingId);
@@ -214,7 +216,7 @@ public class AmqpMessageHandlerServiceTest {
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotExist(targetIdCaptor.capture(),
uriCaptor.capture(), targetNameCaptor.capture())).thenReturn(targetMock);
when(controllerManagementMock.findOldestActiveActionByTarget(any())).thenReturn(Optional.empty());
when(controllerManagementMock.findActiveActionWithHighestWeight(any())).thenReturn(Optional.empty());
amqpMessageHandlerService.onMessage(
createMessage(targetProperties, getThingCreatedMessageProperties(knownThingId)),
@@ -488,7 +490,8 @@ public class AmqpMessageHandlerServiceTest {
when(create.status(any())).thenReturn(create);
when(entityFactoryMock.actionStatus()).thenReturn(builder);
// for the test the same action can be used
when(controllerManagementMock.findOldestActiveActionByTarget(any())).thenReturn(Optional.of(action));
when(controllerManagementMock.findActiveActionWithHighestWeight(any()))
.thenReturn(Optional.of(action));
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());

View File

@@ -188,16 +188,17 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
final DmfDownloadAndUpdateRequest downloadAndUpdateRequest = (DmfDownloadAndUpdateRequest) getDmfClient()
.getMessageConverter().fromMessage(replyMessage);
Assert.assertThat(dsModules,
SoftwareModuleJsonMatcher.containsExactly(downloadAndUpdateRequest.getSoftwareModules()));
assertDmfDownloadAndUpdateRequest(downloadAndUpdateRequest, dsModules, controllerId);
}
downloadAndUpdateRequest.getSoftwareModules()
protected void assertDmfDownloadAndUpdateRequest(final DmfDownloadAndUpdateRequest request,
final Set<SoftwareModule> softwareModules, final String controllerId) {
Assert.assertThat(softwareModules, SoftwareModuleJsonMatcher.containsExactly(request.getSoftwareModules()));
request.getSoftwareModules()
.forEach(dmfModule -> assertThat(dmfModule.getMetadata()).containsExactly(
new DmfMetadata(TestdataFactory.VISIBLE_SM_MD_KEY, TestdataFactory.VISIBLE_SM_MD_VALUE)));
final Target updatedTarget = waitUntilIsPresent(() -> targetManagement.getByControllerID(controllerId));
assertThat(updatedTarget.getSecurityToken()).isEqualTo(downloadAndUpdateRequest.getTargetSecurityToken());
assertThat(updatedTarget.getSecurityToken()).isEqualTo(request.getTargetSecurityToken());
}
protected void assertDownloadAndInstallMessage(final Set<SoftwareModule> softwareModules,

View File

@@ -15,6 +15,7 @@ import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ON
import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -26,11 +27,13 @@ import java.util.stream.Collectors;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.json.model.DmfActionRequest;
import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus;
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadAndUpdateRequest;
import org.eclipse.hawkbit.dmf.json.model.DmfMultiActionRequest;
import org.eclipse.hawkbit.dmf.json.model.DmfMultiActionRequest.DmfMultiActionElement;
import org.eclipse.hawkbit.dmf.json.model.DmfSoftwareModule;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.event.remote.MultiActionEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
@@ -49,8 +52,10 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedE
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
@@ -100,7 +105,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
final String controllerId = TARGET_PREFIX + "sendDownloadStatusBeforeWindowStartTime";
registerAndAssertTargetWithExistingTenant(controllerId);
final DistributionSet distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
testdataFactory.addSoftwareModuleMetadata(distributionSet);
assignDistributionSetWithMaintenanceWindow(distributionSet.getId(), controllerId, getTestSchedule(2),
getTestDuration(10), getTestTimeZone());
@@ -122,7 +127,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
final String controllerId = TARGET_PREFIX + "sendDAndIStatusMessageDuringWindow";
registerAndAssertTargetWithExistingTenant(controllerId);
final DistributionSet distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
testdataFactory.addSoftwareModuleMetadata(distributionSet);
assignDistributionSetWithMaintenanceWindow(distributionSet.getId(), controllerId, getTestSchedule(-5),
getTestDuration(10), getTestTimeZone());
@@ -145,7 +150,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
final String controllerId = TARGET_PREFIX + "assignDistributionSetMultipleTimes";
final DistributionSetAssignmentResult assignmentResult = registerTargetAndAssignDistributionSet(controllerId);
final DistributionSet distributionSet2 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final DistributionSet distributionSet2 = testdataFactory.createDistributionSet();
testdataFactory.addSoftwareModuleMetadata(distributionSet2);
assignDistributionSet(distributionSet2.getId(), controllerId);
assertDownloadAndInstallMessage(distributionSet2.getModules(), controllerId);
@@ -171,17 +176,74 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
final String controllerId = TARGET_PREFIX + "assignMultipleDsInMultiAssignMode";
registerAndAssertTargetWithExistingTenant(controllerId);
final Long actionId1 = assignNewDsToTarget(controllerId);
final Long actionId1 = assignNewDsToTarget(controllerId, 450);
final Entry<Long, EventTopic> action1Install = new SimpleEntry<>(actionId1, EventTopic.DOWNLOAD_AND_INSTALL);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install));
final Long actionId2 = assignNewDsToTarget(controllerId);
final Long actionId2 = assignNewDsToTarget(controllerId, 111);
final Entry<Long, EventTopic> action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install, action2Install));
}
@Test
@Description("Verify payload of multi action messages.")
public void assertMultiActionMessagePayloads() {
final int expectedWeightIfNotSet = 1000;
final int weight1 = 600;
final String controllerId = UUID.randomUUID().toString();
registerAndAssertTargetWithExistingTenant(controllerId);
final DistributionSet ds = testdataFactory.createDistributionSet();
testdataFactory.addSoftwareModuleMetadata(ds);
final Long installActionId = makeAssignment(DeploymentManagement.deploymentRequest(controllerId, ds.getId())
.setActionType(ActionType.FORCED).build()).getAssignedEntity().get(0).getId();
enableMultiAssignments();
final Long downloadActionId = makeAssignment(DeploymentManagement.deploymentRequest(controllerId, ds.getId())
.setActionType(ActionType.DOWNLOAD_ONLY).setWeight(weight1).build()).getAssignedEntity().get(0).getId();
final Long cancelActionId = makeAssignment(
DeploymentManagement.deploymentRequest(controllerId, ds.getId()).setWeight(DEFAULT_TEST_WEIGHT).build())
.getAssignedEntity().get(0).getId();
// make sure the latest message in the queue is the one triggered by the
// cancellation
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.DOWNLOAD_AND_INSTALL, EventTopic.MULTI_ACTION,
EventTopic.MULTI_ACTION);
deploymentManagement.cancelAction(cancelActionId);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
final List<DmfMultiActionElement> multiActionMessages = getLatestMultiActionMessages(controllerId);
assertThat(multiActionMessages).hasSize(3);
final DmfMultiActionElement installMessage = multiActionMessages.stream()
.filter(message -> message.getTopic().equals(EventTopic.DOWNLOAD_AND_INSTALL)).findFirst().get();
final DmfMultiActionElement downloadMessage = multiActionMessages.stream()
.filter(message -> message.getTopic().equals(EventTopic.DOWNLOAD)).findFirst().get();
final DmfMultiActionElement cancelMessage = multiActionMessages.stream()
.filter(message -> message.getTopic().equals(EventTopic.CANCEL_DOWNLOAD)).findFirst().get();
assertThat(installMessage.getWeight()).isEqualTo(expectedWeightIfNotSet);
assertThat(downloadMessage.getWeight()).isEqualTo(weight1);
assertThat(cancelMessage.getWeight()).isEqualTo(DEFAULT_TEST_WEIGHT);
assertThat(installMessage.getAction()).isExactlyInstanceOf(DmfDownloadAndUpdateRequest.class)
.hasFieldOrPropertyWithValue("actionId", installActionId);
assertThat(downloadMessage.getAction()).isExactlyInstanceOf(DmfDownloadAndUpdateRequest.class)
.hasFieldOrPropertyWithValue("actionId", downloadActionId);
assertThat(cancelMessage.getAction()).isExactlyInstanceOf(DmfActionRequest.class)
.hasFieldOrPropertyWithValue("actionId", cancelActionId);
assertDmfDownloadAndUpdateRequest((DmfDownloadAndUpdateRequest) installMessage.getAction(), ds.getModules(),
controllerId);
assertDmfDownloadAndUpdateRequest((DmfDownloadAndUpdateRequest) downloadMessage.getAction(), ds.getModules(),
controllerId);
}
private List<DmfMultiActionElement> getLatestMultiActionMessages(final String expectedControllerId) {
final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION);
assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID))
.isEqualTo(expectedControllerId);
return ((DmfMultiActionRequest) getDmfClient().getMessageConverter().fromMessage(multiactionMessage))
.getElements();
}
@Test
@Description("Handle cancelation process of an action in multi assignment mode.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@@ -197,8 +259,8 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
final String controllerId = TARGET_PREFIX + "cancelActionInMultiAssignMode";
registerAndAssertTargetWithExistingTenant(controllerId);
final long actionId1 = assignNewDsToTarget(controllerId);
final long actionId2 = assignNewDsToTarget(controllerId);
final long actionId1 = assignNewDsToTarget(controllerId, 675);
final long actionId2 = assignNewDsToTarget(controllerId, 343);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION);
deploymentManagement.cancelAction(actionId1);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
@@ -229,8 +291,8 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
final String controllerId = TARGET_PREFIX + "finishActionInMultiAssignMode";
registerAndAssertTargetWithExistingTenant(controllerId);
final long actionId1 = assignNewDsToTarget(controllerId);
final long actionId2 = assignNewDsToTarget(controllerId);
final long actionId1 = assignNewDsToTarget(controllerId, 66);
final long actionId2 = assignNewDsToTarget(controllerId, 767);
final Entry<Long, EventTopic> action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION);
@@ -254,11 +316,11 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
enableMultiAssignments();
final String controllerId = TARGET_PREFIX + "assignDsMultipleTimesInMultiAssignMode";
registerAndAssertTargetWithExistingTenant(controllerId);
final DistributionSet ds = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final Long dsId = testdataFactory.createDistributionSet().getId();
final Long actionId1 = getFirstAssignedActionId(assignDistributionSet(ds.getId(), controllerId));
final Long actionId1 = getFirstAssignedAction(assignDistributionSet(dsId, controllerId, 344)).getId();
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
final Long actionId2 = getFirstAssignedActionId(assignDistributionSet(ds.getId(), controllerId));
final Long actionId2 = getFirstAssignedAction(assignDistributionSet(dsId, controllerId, 775)).getId();
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
final Entry<Long, EventTopic> action1Install = new SimpleEntry<>(actionId1, EventTopic.DOWNLOAD_AND_INSTALL);
@@ -272,8 +334,14 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
}
private Long assignNewDsToTarget(final String controllerId) {
final DistributionSet ds = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds.getId(), controllerId));
return assignNewDsToTarget(controllerId, null);
}
private Long assignNewDsToTarget(final String controllerId, final Integer weight) {
final DistributionSet ds = testdataFactory.createDistributionSet();
final Long actionId = getFirstAssignedActionId(
assignDistributionSet(ds.getId(), Collections.singletonList(controllerId), ActionType.FORCED,
RepositoryModelConstants.NO_FORCE_TIME, weight));
waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.PENDING);
return actionId;
}
@@ -296,15 +364,15 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
final String controllerId = TARGET_PREFIX + "startRolloutsWithSameDsInMultiAssignMode";
registerAndAssertTargetWithExistingTenant(controllerId);
final DistributionSet ds = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final DistributionSet ds = testdataFactory.createDistributionSet();
final Set<Long> smIds = getSoftwareModuleIds(ds);
final String filterQuery = "controllerId==" + controllerId;
createAndStartRollout(ds, filterQuery);
createAndStartRollout(ds, filterQuery, 122);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds));
createAndStartRollout(ds, filterQuery);
createAndStartRollout(ds, filterQuery, 43);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds, smIds));
}
@@ -327,15 +395,15 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
registerAndAssertTargetWithExistingTenant(controllerId);
final String filterQuery = "controllerId==" + controllerId;
final DistributionSet ds1 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final DistributionSet ds1 = testdataFactory.createDistributionSet();
final Set<Long> smIds1 = getSoftwareModuleIds(ds1);
final DistributionSet ds2 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final DistributionSet ds2 = testdataFactory.createDistributionSet();
final Set<Long> smIds2 = getSoftwareModuleIds(ds2);
createAndStartRollout(ds1, filterQuery);
createAndStartRollout(ds2, filterQuery);
createAndStartRollout(ds1, filterQuery, 12);
createAndStartRollout(ds2, filterQuery, 45);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION);
createAndStartRollout(ds1, filterQuery);
createAndStartRollout(ds1, filterQuery, 65);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds1, smIds2, smIds1));
@@ -357,8 +425,12 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
}
private Rollout createAndStartRollout(final DistributionSet ds, final String filterQuery) {
return createAndStartRollout(ds, filterQuery, null);
}
private Rollout createAndStartRollout(final DistributionSet ds, final String filterQuery, final Integer weight) {
final Rollout rollout = testdataFactory.createRolloutByVariables(UUID.randomUUID().toString(), "", 1,
filterQuery, ds, "50", "5");
filterQuery, ds, "50", "5", ActionType.FORCED, weight);
rolloutManagement.start(rollout.getId());
rolloutManagement.handleRollouts();
return rollout;
@@ -483,11 +555,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
}
private List<Entry<Long, EventTopic>> getLatestMultiActionMessageActions(final String expectedControllerId) {
final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION);
assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID))
.isEqualTo(expectedControllerId);
final List<DmfMultiActionElement> multiActionRequest = ((DmfMultiActionRequest) getDmfClient()
.getMessageConverter().fromMessage(multiactionMessage)).getElements();
final List<DmfMultiActionElement> multiActionRequest = getLatestMultiActionMessages(expectedControllerId);
return multiActionRequest.stream()
.map(request -> new SimpleEntry<>(request.getAction().getActionId(), request.getTopic()))
.collect(Collectors.toList());