diff --git a/docs/content/apis/dmf_api.md b/docs/content/apis/dmf_api.md
index 98ade42c4..372d25852 100644
--- a/docs/content/apis/dmf_api.md
+++ b/docs/content/apis/dmf_api.md
@@ -364,6 +364,8 @@ type=EVENT
tenant=tenant123
thingId=abc
topic=DOWNLOAD\_A
If `multi.assignments.enabled` is enabled, this message is sent instead of DOWNLOAD_AND_INSTALL, DOWNLOAD, or CANCEL_DOWNLOAD
by hawkBit to initialize update, download, or cancel task(s).
+
+ With weight, one can set the priority to the action. The higher the weight, the higher is the priority of an action.
Header | Description | Type | Mandatory
------- | -------------------------------- | ----------------------------------- | -------------------------------------------------------------
@@ -381,6 +383,7 @@ Payload Template (the Java representation is [DmfMultiActionRequest](https://git
```json
[{
"topic": "String",
+"weight": long,
"action": {
"actionId": long,
"targetSecurityToken": "String",
@@ -413,6 +416,7 @@ Payload Template (the Java representation is [DmfMultiActionRequest](https://git
},
{
"topic": "String",
+"weight": long,
"action": {
"actionId": long,
"targetSecurityToken": "String",
@@ -454,6 +458,7 @@ type=EVENT
tenant=tenant123
thingId=abc
topic=MULTI\_ACTI
```json
[{
"topic": "DOWNLOAD_AND_INSTALL",
+"weight": 600,
"action": {
"actionId":137,
"targetSecurityToken":"bH7XXAprK1ChnLfKSdtlsp7NOlPnZAYY",
@@ -486,6 +491,7 @@ type=EVENT
tenant=tenant123
thingId=abc
topic=MULTI\_ACTI
},
{
"topic": "DOWNLOAD",
+"weight": 500,
"action": {
"actionId":138,
"targetSecurityToken":"bH7XXAprK1ChnLfKSdtlsp7NOlPnZAYY",
diff --git a/docs/content/concepts/rollout-management.md b/docs/content/concepts/rollout-management.md
index 21d6b416a..fe1cb2eed 100644
--- a/docs/content/concepts/rollout-management.md
+++ b/docs/content/concepts/rollout-management.md
@@ -52,6 +52,10 @@ The cascading execution of the deployment groups is based on two thresholds that
One of the main paradigms of Eclipse hawkBit is, that a Distribution Set represents the currently installed software of a device. Hence, a device can have only one Distribution Set assigned/installed at a time. With _Multi-Assignments_ enabled, this paradigm shifts. Multi-Assignments allows to assign multiple Distribution Sets to a device simultaneously, without cancelling each other. As a consequence, an operator can trigger multiple campaigns addressing the same devices in parallel.
+### Action weight
+
+To differentiate between important and less important updates a property called _weight_ is used. When multi-assignments is enabled every action has a weight value between (and including) 0 and 1000. The higher the weight the more important is the assignment represented by the action. Also when defining a _rollout_ or an _auto-assignment_ and multi-assignments is enabled a weight value has to be provided. This value is passed to the actions created during the execution of these _rollouts_ and _auto-assignments_. If no weight was provided the highest value of 1000 is used instead.
+
### Consequences
While this feature provides more flexibility to the user and enables new use-cases, there are also some consequences one should be aware of:
@@ -60,11 +64,10 @@ While this feature provides more flexibility to the user and enables new use-cas
* This feature is in beta and may change unannounced.
* For now, this feature is **opt-in only**. Once activated, it cannot be deactivated.
-* Currently, there is no mechanism to hint devices to process the actions in a certain order.
**Minor**
-* While on DMF-API a MULTI_ACTION request is sent, DDI-API only exposes the oldest open action.
+* While on DMF-API a MULTI_ACTION request is sent, DDI-API only exposes the next action which has the highest priority in the list of open actions(according to their weight property).
* All information regarding the currently assigned or installed Distribution Set does only respect the last assignment, as well as the last successfully installed Distribution set. This also affects:
* Pinning a target or Distribution Set in Deployment View.
* Statistics about installed or assigned Distribution Sets.
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java
index 8d50b9e72..86757325c 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java
@@ -256,7 +256,13 @@ public enum SpServerError {
*
*/
SP_MULTIASSIGNMENT_NOT_ENABLED("hawkbit.server.error.multiassignmentNotEnabled",
- "The requested operation requires Multiassignments to be enabled.");
+ "The requested operation requires multi assignments to be enabled."),
+
+ /**
+ *
+ */
+ SP_NO_WEIGHT_PROVIDED_IN_MULTIASSIGNMENT_MODE("hawkbit.server.error.noWeightProvidedInMultiAssignmentMode",
+ "The requested operation requires a weight to be specified when multi assignments is enabled.");
private final String key;
private final String message;
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionFields.java
index 45b1a1993..e4a079bf3 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionFields.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionFields.java
@@ -21,7 +21,11 @@ public enum ActionFields implements FieldNameProvider, FieldValueConverter IpUtil.isAmqpUri(target.getAddress())).forEach(target -> {
final List activeActions = deploymentManagement
- .findActiveActionsByTarget(PageRequest.of(0, MAX_ACTION_COUNT), target.getControllerId())
- .getContent();
+ .findActiveActionsWithHighestWeight(target.getControllerId(), MAX_ACTION_COUNT);
activeActions.forEach(action -> action.getDistributionSet().getModules().forEach(
module -> softwareModuleMetadata.computeIfAbsent(module, this::getSoftwareModuleMetadata)));
@@ -204,7 +203,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
actions.forEach(action -> {
final DmfActionRequest actionRequest = createDmfActionRequest(target, action,
getSoftwareModuleMetaData.apply(action));
- multiActionRequest.addElement(getEventTypeForAction(action), actionRequest);
+ final int weight = deploymentManagement.getWeightConsideringDefault(action);
+ multiActionRequest.addElement(getEventTypeForAction(action), actionRequest, weight);
});
final Message message = getMessageConverter().toMessage(multiActionRequest,
diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java
index 124d420b7..6eca6ca92 100644
--- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java
+++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java
@@ -54,7 +54,6 @@ import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.MessageConversionException;
-import org.springframework.data.domain.PageRequest;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
@@ -233,8 +232,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
LOG.debug("Target {} reported online state.", thingId);
sendUpdateCommandToTarget(target);
} catch (final EntityAlreadyExistsException e) {
- throw new AmqpRejectAndDontRequeueException(
- "Tried to register previously registered target, message will be ignored!", e);
+ throw new AmqpRejectAndDontRequeueException("Tried to register previously registered target, message will be ignored!", e);
}
}
@@ -254,7 +252,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
private void sendCurrentActionsAsMultiActionToTarget(final Target target) {
final List actions = controllerManagement
- .findActiveActionsByTarget(PageRequest.of(0, MAX_ACTION_COUNT), target.getControllerId()).getContent();
+ .findActiveActionsWithHighestWeight(target.getControllerId(), MAX_ACTION_COUNT);
final Set distributionSets = actions.stream().map(Action::getDistributionSet)
.collect(Collectors.toSet());
@@ -267,7 +265,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
private void sendOldestActionToTarget(final Target target) {
final Optional actionOptional = controllerManagement
- .findOldestActiveActionByTarget(target.getControllerId());
+ .findActiveActionWithHighestWeight(target.getControllerId());
if (!actionOptional.isPresent()) {
return;
diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java
index 4485a0fe8..0d5956266 100644
--- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java
+++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java
@@ -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 targetIdCaptor = ArgumentCaptor.forClass(String.class);
+ final ArgumentCaptor 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());
diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java
index 8ba84f587..0689ad038 100644
--- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java
+++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java
@@ -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 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 softwareModules,
diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java
index 50ea5a8d4..e807b9baa 100644
--- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java
+++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java
@@ -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 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 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 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 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 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 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 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 smIds1 = getSoftwareModuleIds(ds1);
- final DistributionSet ds2 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
+ final DistributionSet ds2 = testdataFactory.createDistributionSet();
final Set 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> getLatestMultiActionMessageActions(final String expectedControllerId) {
- final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION);
- assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID))
- .isEqualTo(expectedControllerId);
- final List multiActionRequest = ((DmfMultiActionRequest) getDmfClient()
- .getMessageConverter().fromMessage(multiactionMessage)).getElements();
+ final List multiActionRequest = getLatestMultiActionMessages(expectedControllerId);
return multiActionRequest.stream()
.map(request -> new SimpleEntry<>(request.getAction().getActionId(), request.getTopic()))
.collect(Collectors.toList());
diff --git a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java
index 9f98d331c..6e0021cd9 100644
--- a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java
+++ b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java
@@ -52,10 +52,11 @@ public class DmfMultiActionRequest {
elements.add(element);
}
- public void addElement(final EventTopic topic, final DmfActionRequest action) {
+ public void addElement(final EventTopic topic, final DmfActionRequest action, final int weight) {
final DmfMultiActionElement element = new DmfMultiActionElement();
element.setTopic(topic);
element.setAction(action);
+ element.setWeight(weight);
addElement(element);
}
@@ -70,6 +71,9 @@ public class DmfMultiActionRequest {
@JsonProperty
private DmfActionRequest action;
+ @JsonProperty
+ private int weight;
+
public DmfActionRequest getAction() {
return action;
}
@@ -89,6 +93,13 @@ public class DmfMultiActionRequest {
this.topic = actionType;
}
+ public void setWeight(final int weight) {
+ this.weight = weight;
+ }
+
+ public int getWeight() {
+ return weight;
+ }
}
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java
index 28e314f28..562415c23 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java
@@ -46,13 +46,13 @@ import org.springframework.security.access.prepost.PreAuthorize;
public interface ControllerManagement {
/**
- * Adds an {@link ActionStatus} for a cancel {@link Action} including potential
- * state changes for the target and the {@link Action} itself.
+ * Adds an {@link ActionStatus} for a cancel {@link Action} including
+ * potential state changes for the target and the {@link Action} itself.
*
* @param create
* to be added
* @return the updated {@link Action}
- *
+ *
* @throws EntityAlreadyExistsException
* if a given entity already exists
*
@@ -93,8 +93,8 @@ public interface ControllerManagement {
@NotNull Collection moduleId);
/**
- * Simple addition of a new {@link ActionStatus} entry to the {@link Action} .
- * No state changes.
+ * Simple addition of a new {@link ActionStatus} entry to the
+ * {@link Action}. No state changes.
*
* @param create
* to add to the action
@@ -136,36 +136,48 @@ public interface ControllerManagement {
Action addUpdateActionStatus(@NotNull @Valid ActionStatusCreate create);
/**
- * Retrieves oldest {@link Action} that is active and assigned to a
- * {@link Target}.
- *
+ * Retrieves active {@link Action} with highest priority that is assigned to
+ * a {@link Target}.
+ *
* For performance reasons this method does not throw
- * {@link EntityNotFoundException} in case target with given controlelrId
+ * {@link EntityNotFoundException} in case target with given controllerId
* does not exist but will return an {@link Optional#empty()} instead.
*
* @param controllerId
- * identifies the target to retrieve the actions from
- * @return a list of actions assigned to given target which are active
- *
+ * identifies the target to retrieve the action from
+ * @return the action
+ *
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
- Optional findOldestActiveActionByTarget(@NotEmpty String controllerId);
+ Optional findActiveActionWithHighestWeight(@NotEmpty String controllerId);
/**
- * Retrieves all active actions which are assigned to the target with the given
- * controller ID.
- *
- * @param pageable
- * pagination parameter
+ * Retrieves active {@link Action}s with highest weight that are assigned to
+ * a {@link Target}.
+ *
* @param controllerId
- * of the target
- * @return the requested {@link Page} with {@link Action}s
+ * identifies the target to retrieve the action from
+ * @param maxActionCount
+ * max size of returned list
+ * @return the action
+ *
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
- Page findActiveActionsByTarget(@NotNull Pageable pageable, @NotEmpty String controllerId);
+ List findActiveActionsWithHighestWeight(@NotEmpty String controllerId, int maxActionCount);
/**
- * Get the {@link Action} entity for given actionId with all lazy attributes.
+ * Get weight of an Action. Returns the default value if the weight is null
+ * according to the properties.
+ *
+ * @param action
+ * to extract the weight from
+ * @return weight of the action
+ */
+ int getWeightConsideringDefault(final Action action);
+
+ /**
+ * Get the {@link Action} entity for given actionId with all lazy
+ * attributes.
*
* @param actionId
* to be id of the action
@@ -175,7 +187,8 @@ public interface ControllerManagement {
Optional findActionWithDetails(long actionId);
/**
- * Retrieves all the {@link ActionStatus} entries of the given {@link Action}.
+ * Retrieves all the {@link ActionStatus} entries of the given
+ * {@link Action}.
*
* @param pageReq
* pagination parameter
@@ -190,10 +203,11 @@ public interface ControllerManagement {
Page findActionStatusByAction(@NotNull Pageable pageReq, long actionId);
/**
- * Register new target in the repository (plug-and-play) and in case it already
- * exists updates {@link Target#getAddress()} and
+ * Register new target in the repository (plug-and-play) and in case it
+ * already exists updates {@link Target#getAddress()} and
* {@link Target#getLastTargetQuery()} and switches if
- * {@link TargetUpdateStatus#UNKNOWN} to {@link TargetUpdateStatus#REGISTERED}.
+ * {@link TargetUpdateStatus#UNKNOWN} to
+ * {@link TargetUpdateStatus#REGISTERED}.
*
* @param controllerId
* reference
@@ -205,10 +219,11 @@ public interface ControllerManagement {
Target findOrRegisterTargetIfItDoesNotExist(@NotEmpty String controllerId, @NotNull URI address);
/**
- * Register new target in the repository (plug-and-play) and in case it already
- * exists updates {@link Target#getAddress()} and
- * {@link Target#getLastTargetQuery()} and {@link Target#getName()} and switches if
- * {@link TargetUpdateStatus#UNKNOWN} to {@link TargetUpdateStatus#REGISTERED}.
+ * Register new target in the repository (plug-and-play) and in case it
+ * already exists updates {@link Target#getAddress()} and
+ * {@link Target#getLastTargetQuery()} and {@link Target#getName()} and
+ * switches if {@link TargetUpdateStatus#UNKNOWN} to
+ * {@link TargetUpdateStatus#REGISTERED}.
*
* @param controllerId
* reference
@@ -222,14 +237,14 @@ public interface ControllerManagement {
Target findOrRegisterTargetIfItDoesNotExist(@NotEmpty String controllerId, @NotNull URI address, String name);
/**
- * Retrieves last {@link Action} for a download of an artifact of given module
- * and target if exists and is not canceled.
+ * Retrieves last {@link Action} for a download of an artifact of given
+ * module and target if exists and is not canceled.
*
* @param controllerId
* to look for
* @param moduleId
- * of the the {@link SoftwareModule} that should be assigned to the
- * target
+ * of the the {@link SoftwareModule} that should be assigned to
+ * the target
* @return last {@link Action} for given combination
*
* @throws EntityNotFoundException
@@ -267,18 +282,18 @@ public interface ControllerManagement {
int getMaintenanceWindowPollCount();
/**
- * Returns polling time based on the maintenance window for an action. Server
- * will reduce the polling interval as the start time for maintenance window
- * approaches, so that at least these many attempts are made between current
- * polling until start of maintenance window. Poll time keeps reducing with
- * MinPollingTime as lower limit
- * {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}. After the start of
- * maintenance window, it resets to default
+ * Returns polling time based on the maintenance window for an action.
+ * Server will reduce the polling interval as the start time for maintenance
+ * window approaches, so that at least these many attempts are made between
+ * current polling until start of maintenance window. Poll time keeps
+ * reducing with MinPollingTime as lower limit
+ * {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}. After the start
+ * of maintenance window, it resets to default
* {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
*
* @param actionId
- * id the {@link Action} for which polling time is calculated based
- * on it having maintenance window or not
+ * id the {@link Action} for which polling time is calculated
+ * based on it having maintenance window or not
*
* @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
*/
@@ -286,20 +301,20 @@ public interface ControllerManagement {
String getPollingTimeForAction(long actionId);
/**
- * Checks if a given target has currently or has even been assigned to the given
- * artifact through the action history list. This can e.g. indicate if a target
- * is allowed to download a given artifact because it has currently assigned or
- * had ever been assigned to the target and so it's visible to a specific target
- * e.g. for downloading.
+ * Checks if a given target has currently or has even been assigned to the
+ * given artifact through the action history list. This can e.g. indicate if
+ * a target is allowed to download a given artifact because it has currently
+ * assigned or had ever been assigned to the target and so it's visible to a
+ * specific target e.g. for downloading.
*
* @param controllerId
* the ID of the target to check
* @param sha1Hash
* of the artifact to verify if the given target had even been
* assigned to
- * @return {@code true} if the given target has currently or had ever a relation
- * to the given artifact through the action history, otherwise
- * {@code false}
+ * @return {@code true} if the given target has currently or had ever a
+ * relation to the given artifact through the action history,
+ * otherwise {@code false}
*
* @throws EntityNotFoundException
* if target with given ID does not exist
@@ -308,20 +323,20 @@ public interface ControllerManagement {
boolean hasTargetArtifactAssigned(@NotEmpty String controllerId, @NotEmpty String sha1Hash);
/**
- * Checks if a given target has currently or has even been assigned to the given
- * artifact through the action history list. This can e.g. indicate if a target
- * is allowed to download a given artifact because it has currently assigned or
- * had ever been assigned to the target and so it's visible to a specific target
- * e.g. for downloading.
+ * Checks if a given target has currently or has even been assigned to the
+ * given artifact through the action history list. This can e.g. indicate if
+ * a target is allowed to download a given artifact because it has currently
+ * assigned or had ever been assigned to the target and so it's visible to a
+ * specific target e.g. for downloading.
*
* @param targetId
* the ID of the target to check
* @param sha1Hash
* of the artifact to verify if the given target had even been
* assigned to
- * @return {@code true} if the given target has currently or had ever a relation
- * to the given artifact through the action history, otherwise
- * {@code false}
+ * @return {@code true} if the given target has currently or had ever a
+ * relation to the given artifact through the action history,
+ * otherwise {@code false}
*
* @throws EntityNotFoundException
* if target with given ID does not exist
@@ -330,8 +345,8 @@ public interface ControllerManagement {
boolean hasTargetArtifactAssigned(long targetId, @NotEmpty String sha1Hash);
/**
- * 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
@@ -385,8 +400,9 @@ public interface ControllerManagement {
Optional getByControllerId(@NotEmpty String controllerId);
/**
- * Finds {@link Target} based on given ID returns found Target without details,
- * i.e. NO {@link Target#getTags()} and {@link Target#getActions()} possible.
+ * Finds {@link Target} based on given ID returns found Target without
+ * details, i.e. NO {@link Target#getTags()} and {@link Target#getActions()}
+ * possible.
*
* @param targetId
* to look for.
@@ -398,20 +414,21 @@ public interface ControllerManagement {
Optional get(long targetId);
/**
- * Retrieves the specified number of messages from action history of the given
- * {@link Action} based on messageCount. Regardless of the value of
+ * Retrieves the specified number of messages from action history of the
+ * given {@link Action} based on messageCount. Regardless of the value of
* messageCount, in order to restrict resource utilisation by controllers,
* maximum number of messages that are retrieved from database is limited by
- * {@link RepositoryConstants#MAX_ACTION_HISTORY_MSG_COUNT}. messageCount less
- * then zero, retrieves the maximum allowed number of action status messages
- * from history; messageCount equal zero, does not retrieve any message; and
- * messageCount larger then zero, retrieves the specified number of messages,
- * limited by maximum allowed number. A controller sends the feedback for an
- * {@link ActionStatus} as a list of messages; while returning the messages,
- * even though the messages from multiple {@link ActionStatus} are retrieved in
- * descending order by the reported time ({@link ActionStatus#getOccurredAt()}),
- * i.e. latest ActionStatus first, the sub-ordering of messages from within
- * single {@link ActionStatus} is unspecified.
+ * {@link RepositoryConstants#MAX_ACTION_HISTORY_MSG_COUNT}. messageCount
+ * less then zero, retrieves the maximum allowed number of action status
+ * messages from history; messageCount equal zero, does not retrieve any
+ * message; and messageCount larger then zero, retrieves the specified
+ * number of messages, limited by maximum allowed number. A controller sends
+ * the feedback for an {@link ActionStatus} as a list of messages; while
+ * returning the messages, even though the messages from multiple
+ * {@link ActionStatus} are retrieved in descending order by the reported
+ * time ({@link ActionStatus#getOccurredAt()}), i.e. latest ActionStatus
+ * first, the sub-ordering of messages from within single
+ * {@link ActionStatus} is unspecified.
*
* @param actionId
* to be filtered on
@@ -424,10 +441,10 @@ public interface ControllerManagement {
List getActionHistoryMessages(long actionId, int messageCount);
/**
- * Cancels given {@link Action} for this {@link Target}. However, it might be
- * possible that the controller will continue to work on the cancellation. The
- * controller needs to acknowledge or reject the cancellation using
- * {@link DdiRootController#postCancelActionFeedback}.
+ * Cancels given {@link Action} for this {@link Target}. However, it might
+ * be possible that the controller will continue to work on the
+ * cancellation. The controller needs to acknowledge or reject the
+ * cancellation using {@link DdiRootController#postCancelActionFeedback}.
*
* @param actionId
* to be canceled
@@ -454,8 +471,9 @@ public interface ControllerManagement {
void updateActionExternalRef(long actionId, @NotEmpty String externalRef);
/**
- * Retrieves list of {@link Action}s which matches the provided externalRefs.
- *
+ * Retrieves list of {@link Action}s which matches the provided
+ * externalRefs.
+ *
* @param externalRefs
* for which the actions need to be fetched.
* @return list of {@link Action}s matching the externalRefs.
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java
index 32cfda0f1..27d009e08 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java
@@ -14,6 +14,7 @@ import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
+import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@@ -76,7 +77,7 @@ public interface DeploymentManagement {
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
List assignDistributionSets(
- @NotEmpty List deploymentRequests);
+ @Valid @NotEmpty List deploymentRequests);
/**
* Assigns {@link DistributionSet}s to {@link Target}s according to the
@@ -107,9 +108,9 @@ public interface DeploymentManagement {
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
List assignDistributionSets(
- @NotEmpty List deploymentRequests, String actionMessage);
-
- /**
+ @Valid @NotEmpty List deploymentRequests, String actionMessage);
+
+ /**
* build a {@link DeploymentRequest} for a target distribution set
* assignment
*
@@ -372,6 +373,30 @@ public interface DeploymentManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page findInActiveActionsByTarget(@NotNull Pageable pageable, @NotEmpty String controllerId);
+ /**
+ * Retrieves active {@link Action}s with highest weight that are assigned to
+ * a {@link Target}.
+ *
+ * @param controllerId
+ * identifies the target to retrieve the action from
+ * @param maxActionCount
+ * max size of returned list
+ * @return the action
+ *
+ */
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
+ List findActiveActionsWithHighestWeight(@NotEmpty String controllerId, int maxActionCount);
+
+ /**
+ * Get weight of an Action. Returns the default value if the weight is null
+ * according to the properties.
+ *
+ * @param action
+ * to extract the weight from
+ * @return weight of the action
+ */
+ int getWeightConsideringDefault(final Action action);
+
/**
* Force cancels given {@link Action} for given {@link Target}. Force
* canceling means that the action is marked as canceled on the SP server
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryProperties.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryProperties.java
index 46e563cda..771707554 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryProperties.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryProperties.java
@@ -27,8 +27,8 @@ public class RepositoryProperties {
* especially useful if the action status feedback channel order from the
* device cannot be guaranteed.
*
- * Note: if this is enforced you have to make sure that the feedback
- * channel from the devices is in order.
+ * Note: if this is enforced you have to make sure that the feedback channel
+ * from the devices is in order.
*/
private boolean rejectActionStatusForClosedAction;
@@ -54,6 +54,11 @@ public class RepositoryProperties {
*/
private boolean eagerPollPersistence;
+ /**
+ * If an {@link Action} has a weight of null this value is used as weight.
+ */
+ private int actionWeightIfAbsent = 1000;
+
public boolean isEagerPollPersistence() {
return eagerPollPersistence;
}
@@ -94,4 +99,12 @@ public class RepositoryProperties {
this.publishTargetPollEvent = publishTargetPollEvent;
}
+ public int getActionWeightIfAbsent() {
+ return actionWeightIfAbsent;
+ }
+
+ public void setActionWeightIfAbsent(final int actionWeightIfAbsent) {
+ this.actionWeightIfAbsent = actionWeightIfAbsent;
+ }
+
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutManagement.java
index e96f73d07..393bc0ca1 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutManagement.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutManagement.java
@@ -129,7 +129,7 @@ public interface RolloutManagement {
* exceeded.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_CREATE)
- Rollout create(@NotNull RolloutCreate create, int amountGroup, @NotNull RolloutGroupConditions conditions);
+ Rollout create(@NotNull @Valid RolloutCreate create, int amountGroup, @NotNull RolloutGroupConditions conditions);
/**
* Persists a new rollout entity. The filter within the
@@ -166,7 +166,7 @@ public interface RolloutManagement {
* exceeded.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_CREATE)
- Rollout create(@NotNull @Valid RolloutCreate rollout, @NotNull @Valid List groups,
+ Rollout create(@Valid @NotNull RolloutCreate rollout, @NotNull @Valid List groups,
RolloutGroupConditions conditions);
/**
@@ -342,9 +342,9 @@ public interface RolloutManagement {
/**
* Approves or denies a created rollout being in state
- * {@link RolloutStatus#WAITING_FOR_APPROVAL}. If the rollout is approved, it
- * switches state to {@link RolloutStatus#READY}, otherwise it switches to state
- * {@link RolloutStatus#APPROVAL_DENIED}
+ * {@link RolloutStatus#WAITING_FOR_APPROVAL}. If the rollout is approved,
+ * it switches state to {@link RolloutStatus#READY}, otherwise it switches
+ * to state {@link RolloutStatus#APPROVAL_DENIED}
*
* @param rolloutId
* the rollout to be approved or denied.
@@ -365,9 +365,9 @@ public interface RolloutManagement {
/**
* Approves or denies a created rollout being in state
- * {@link RolloutStatus#WAITING_FOR_APPROVAL}. If the rollout is approved, it
- * switches state to {@link RolloutStatus#READY}, otherwise it switches to state
- * {@link RolloutStatus#APPROVAL_DENIED}
+ * {@link RolloutStatus#WAITING_FOR_APPROVAL}. If the rollout is approved,
+ * it switches state to {@link RolloutStatus#READY}, otherwise it switches
+ * to state {@link RolloutStatus#APPROVAL_DENIED}
*
* @param rolloutId
* the rollout to be approved or denied.
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java
index 937f9fb4f..fd5e2fb42 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java
@@ -15,6 +15,7 @@ import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
+import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -208,7 +209,7 @@ public interface TargetFilterQueryManagement {
* if fields are not filled as specified. Check
* {@link TargetFilterQueryUpdate} for field constraints.
*
- * @throws AssignmentQuotaExceededException
+ * @throws QuotaExceededException
* if the update contains a new query which addresses too many
* targets (auto-assignments only)
*/
@@ -216,46 +217,10 @@ public interface TargetFilterQueryManagement {
TargetFilterQuery update(@NotNull @Valid TargetFilterQueryUpdate update);
/**
- * Updates the the auto-assign {@link DistributionSet} and sets default
- * (FORCED) {@link ActionType} of the addressed {@link TargetFilterQuery}.
- *
- * @param queryId
- * of the target filter query to be updated
- * @param dsId
- * to be updated or null in order to remove it
- * together with the auto-assign {@link ActionType}
+ * Updates the auto assign settings of an {@link TargetFilterQuery}.
*
- * @return the updated {@link TargetFilterQuery}
- *
- * @throws EntityNotFoundException
- * if either {@link TargetFilterQuery} and/or autoAssignDs are
- * provided but not found
- *
- * @throws AssignmentQuotaExceededException
- * if the query that is already associated with this filter
- * query addresses too many targets (auto-assignments only)
- *
- * @throws InvalidAutoAssignDistributionSetException
- * if the provided auto-assign {@link DistributionSet} is not
- * valid (incomplete or soft deleted)
- */
- @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
- default TargetFilterQuery updateAutoAssignDS(final long queryId, final Long dsId) {
- return updateAutoAssignDSWithActionType(queryId, dsId, null);
- }
-
- /**
- * Updates the the auto-assign {@link DistributionSet} and
- * {@link ActionType} of the addressed {@link TargetFilterQuery}.
- *
- * @param queryId
- * of the target filter query to be updated
- * @param dsId
- * to be updated or null in order to remove it
- * together with the auto-assign {@link ActionType}
- * @param actionType
- * to be updated or null for default (FORCED) if
- * distribution set Id is present
+ * @param autoAssignDistributionSetUpdate
+ * the new auto assignment
*
* @return the updated {@link TargetFilterQuery}
*
@@ -276,5 +241,6 @@ public interface TargetFilterQueryManagement {
* valid (incomplete or soft deleted)
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
- TargetFilterQuery updateAutoAssignDSWithActionType(long queryId, Long dsId, ActionType actionType);
+ TargetFilterQuery updateAutoAssignDS(
+ @NotNull @Valid AutoAssignDistributionSetUpdate autoAssignDistributionSetUpdate);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/AutoAssignDistributionSetUpdate.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/AutoAssignDistributionSetUpdate.java
new file mode 100644
index 000000000..d80f96991
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/AutoAssignDistributionSetUpdate.java
@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2019 Bosch Software Innovations GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.hawkbit.repository.builder;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
+import org.eclipse.hawkbit.repository.model.Action;
+import org.eclipse.hawkbit.repository.model.Action.ActionType;
+import org.eclipse.hawkbit.repository.model.DistributionSet;
+import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
+
+/**
+ * Builder to update the auto assign {@link DistributionSet} of a
+ * {@link TargetFilterQuery} entry. Defines all fields that can be updated.
+ */
+public class AutoAssignDistributionSetUpdate {
+ private final long targetFilterId;
+ private Long dsId;
+ private ActionType actionType;
+
+ @Min(Action.WEIGHT_MIN)
+ @Max(Action.WEIGHT_MAX)
+ private Integer weight;
+
+ /**
+ * Constructor
+ *
+ * @param targetFilterId
+ * ID of {@link TargetFilterQuery} to update
+ */
+ public AutoAssignDistributionSetUpdate(final long targetFilterId) {
+ this.targetFilterId = targetFilterId;
+ }
+
+ /**
+ * Specify {@link DistributionSet}
+ *
+ * @param dsId
+ * ID of the {@link DistributionSet}
+ * @return updated builder instance
+ */
+ public AutoAssignDistributionSetUpdate ds(final Long dsId) {
+ this.dsId = dsId;
+ return this;
+ }
+
+ /**
+ * Specify {@link DistributionSet}
+ *
+ * @param actionType
+ * {@link ActionType} used for the auto assignment
+ * @return updated builder instance
+ */
+ public AutoAssignDistributionSetUpdate actionType(final ActionType actionType) {
+ this.actionType = actionType;
+ return this;
+ }
+
+ /**
+ * Specify weight of resulting {@link Action}
+ *
+ * @param weight
+ * weight used for the auto assignment
+ * @return updated builder instance
+ */
+ public AutoAssignDistributionSetUpdate weight(final Integer weight) {
+ this.weight = weight;
+ return this;
+ }
+
+ public Long getDsId() {
+ return dsId;
+ }
+
+ public ActionType getActionType() {
+ return actionType;
+ }
+
+ public Integer getWeight() {
+ return weight;
+ }
+
+ public long getTargetFilterId() {
+ return targetFilterId;
+ }
+
+}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/RolloutCreate.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/RolloutCreate.java
index 42e76f73f..f8cd2e6b2 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/RolloutCreate.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/RolloutCreate.java
@@ -28,6 +28,8 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
*/
public interface RolloutCreate {
/**
+ * Set name
+ *
* @param name
* for {@link Rollout#getName()}
* @return updated builder instance
@@ -35,6 +37,8 @@ public interface RolloutCreate {
RolloutCreate name(@Size(min = 1, max = NamedEntity.NAME_MAX_SIZE) @NotNull String name);
/**
+ * Set description
+ *
* @param description
* for {@link Rollout#getDescription()}
* @return updated builder instance
@@ -42,6 +46,8 @@ public interface RolloutCreate {
RolloutCreate description(@Size(max = NamedEntity.DESCRIPTION_MAX_SIZE) String description);
/**
+ * Set the {@link DistributionSet}
+ *
* @param set
* for {@link Rollout#getDistributionSet()}
* @return updated builder instance
@@ -51,6 +57,8 @@ public interface RolloutCreate {
}
/**
+ * Set the id of the {@link DistributionSet}
+ *
* @param setId
* for {@link Rollout#getDistributionSet()}
* @return updated builder instance
@@ -58,6 +66,8 @@ public interface RolloutCreate {
RolloutCreate set(long setId);
/**
+ * Set the {@link TargetFilterQuery}
+ *
* @param targetFilterQuery
* for {@link Rollout#getTargetFilterQuery()}
* @return updated builder instance
@@ -66,6 +76,8 @@ public interface RolloutCreate {
@Size(min = 1, max = TargetFilterQuery.QUERY_MAX_SIZE) @NotNull String targetFilterQuery);
/**
+ * Set the {@link ActionType}
+ *
* @param actionType
* for {@link Rollout#getActionType()}
* @return updated builder instance
@@ -73,6 +85,8 @@ public interface RolloutCreate {
RolloutCreate actionType(@NotNull ActionType actionType);
/**
+ * Set the forcedTime of the resulting {@link Actions}
+ *
* @param forcedTime
* for {@link Rollout#getForcedTime()}
* @return updated builder instance
@@ -80,6 +94,17 @@ public interface RolloutCreate {
RolloutCreate forcedTime(Long forcedTime);
/**
+ * Set the weight of the resulting {@link Actions}
+ *
+ * @param weight
+ * for {@link Rollout#getWeight()}
+ * @return updated builder instance
+ */
+ RolloutCreate weight(Integer weight);
+
+ /**
+ * set start
+ *
* @param startAt
* for {@link Rollout#getStartAt()}
* @return updated builder instance
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/RolloutUpdate.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/RolloutUpdate.java
index b9ecfee41..8c1a768ab 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/RolloutUpdate.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/RolloutUpdate.java
@@ -12,6 +12,7 @@ import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.eclipse.hawkbit.repository.model.Action;
+import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.Rollout;
@@ -22,6 +23,8 @@ import org.eclipse.hawkbit.repository.model.Rollout;
*/
public interface RolloutUpdate {
/**
+ * Set name of the {@link Rollout}
+ *
* @param name
* for {@link Rollout#getName()}
* @return updated builder instance
@@ -29,6 +32,8 @@ public interface RolloutUpdate {
RolloutUpdate name(@Size(min = 1, max = NamedEntity.NAME_MAX_SIZE) @NotNull String name);
/**
+ * Set description of the {@link Rollout}
+ *
* @param description
* for {@link Rollout#getDescription()}
* @return updated builder instance
@@ -36,6 +41,8 @@ public interface RolloutUpdate {
RolloutUpdate description(@Size(max = NamedEntity.DESCRIPTION_MAX_SIZE) String description);
/**
+ * Set ID of {@link DistributionSet} of the {@link Rollout}
+ *
* @param setId
* for {@link Rollout#getDistributionSet()}
* @return updated builder instance
@@ -43,6 +50,8 @@ public interface RolloutUpdate {
RolloutUpdate set(long setId);
/**
+ * Set action type of the {@link Rollout}
+ *
* @param actionType
* for {@link Rollout#getActionType()}
* @return updated builder instance
@@ -50,6 +59,8 @@ public interface RolloutUpdate {
RolloutUpdate actionType(@NotNull Action.ActionType actionType);
/**
+ * Set forcedTime of the {@link Rollout}
+ *
* @param forcedTime
* for {@link Rollout#getForcedTime()}
* @return updated builder instance
@@ -57,10 +68,20 @@ public interface RolloutUpdate {
RolloutUpdate forcedTime(Long forcedTime);
/**
+ * Set weight of {@link Action}s created by the {@link Rollout}
+ *
+ * @param weight
+ * for {@link Rollout#getWeight()}
+ * @return updated builder instance
+ */
+ RolloutUpdate weight(Integer weight);
+
+ /**
+ * Set start time of the {@link Rollout}
+ *
* @param startAt
* for {@link Rollout#getStartAt()}
* @return updated builder instance
*/
RolloutUpdate startAt(Long startAt);
-
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/TargetFilterQueryBuilder.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/TargetFilterQueryBuilder.java
index 2e9698743..8dd14bdc8 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/TargetFilterQueryBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/TargetFilterQueryBuilder.java
@@ -16,12 +16,23 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
*/
public interface TargetFilterQueryBuilder {
/**
+ * Used to update a {@link TargetFilterQuery}
+ *
* @param id
* of the updatable entity
* @return builder instance
*/
TargetFilterQueryUpdate update(long id);
+ /**
+ * Used to update a the auto assignment of a {@link TargetFilterQuery}
+ *
+ * @param id
+ * of the updatable entity
+ * @return builder instance
+ */
+ AutoAssignDistributionSetUpdate updateAutoAssign(long id);
+
/**
* @return builder instance
*/
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/TargetFilterQueryCreate.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/TargetFilterQueryCreate.java
index 187bfbf14..4d8b42474 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/TargetFilterQueryCreate.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/TargetFilterQueryCreate.java
@@ -13,6 +13,7 @@ import java.util.Optional;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
+import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -27,6 +28,8 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
*/
public interface TargetFilterQueryCreate {
/**
+ * Set filter name
+ *
* @param name
* of {@link TargetFilterQuery#getName()}
* @return updated builder instance
@@ -34,6 +37,8 @@ public interface TargetFilterQueryCreate {
TargetFilterQueryCreate name(@Size(min = 1, max = NamedEntity.NAME_MAX_SIZE) @NotNull String name);
/**
+ * Set filter query
+ *
* @param query
* of {@link TargetFilterQuery#getQuery()}
* @return updated builder instance
@@ -41,6 +46,8 @@ public interface TargetFilterQueryCreate {
TargetFilterQueryCreate query(@Size(min = 1, max = TargetFilterQuery.QUERY_MAX_SIZE) @NotNull String query);
/**
+ * Set {@link DistributionSet} for auto assignment
+ *
* @param distributionSet
* for {@link TargetFilterQuery#getAutoAssignDistributionSet()}
* @return updated builder instance
@@ -50,6 +57,8 @@ public interface TargetFilterQueryCreate {
}
/**
+ * Set ID of {@link DistributionSet} for auto assignment
+ *
* @param dsId
* for {@link TargetFilterQuery#getAutoAssignDistributionSet()}
* @return updated builder instance
@@ -57,12 +66,23 @@ public interface TargetFilterQueryCreate {
TargetFilterQueryCreate autoAssignDistributionSet(Long dsId);
/**
+ * Set {@link ActionType} for auto assignment
+ *
* @param actionType
* for {@link TargetFilterQuery#getAutoAssignActionType()}
* @return updated builder instance
*/
TargetFilterQueryCreate autoAssignActionType(ActionType actionType);
+ /**
+ * Set weight of {@link Action} created during auto assignment
+ *
+ * @param weight
+ * weight of {@link Action} generated within auto assignment
+ * @return updated builder instance
+ */
+ TargetFilterQueryCreate autoAssignWeight(Integer weight);
+
/**
* @return peek on current state of {@link TargetFilterQuery} in the builder
*/
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/TargetFilterQueryUpdate.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/TargetFilterQueryUpdate.java
index 15dbef687..601f62715 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/TargetFilterQueryUpdate.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/builder/TargetFilterQueryUpdate.java
@@ -32,5 +32,4 @@ public interface TargetFilterQueryUpdate {
* @return updated builder instance
*/
TargetFilterQueryUpdate query(@Size(min = 1, max = TargetFilterQuery.QUERY_MAX_SIZE) String query);
-
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/NoWeightProvidedInMultiAssignmentModeException.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/NoWeightProvidedInMultiAssignmentModeException.java
new file mode 100644
index 000000000..2c3758301
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/NoWeightProvidedInMultiAssignmentModeException.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2019 Bosch Software Innovations GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.hawkbit.repository.exception;
+
+import org.eclipse.hawkbit.exception.AbstractServerRtException;
+import org.eclipse.hawkbit.exception.SpServerError;
+
+/**
+ * This exception is thrown if multi assignments is enabled and an target
+ * distribution set assignment request does not contain a weight value
+ *
+ */
+public class NoWeightProvidedInMultiAssignmentModeException extends AbstractServerRtException {
+
+
+ private static final long serialVersionUID = 1L;
+ private static final SpServerError THIS_ERROR = SpServerError.SP_NO_WEIGHT_PROVIDED_IN_MULTIASSIGNMENT_MODE;
+
+ /**
+ * Default constructor.
+ */
+ public NoWeightProvidedInMultiAssignmentModeException() {
+ super(THIS_ERROR);
+ }
+
+ /**
+ * Parameterized constructor.
+ *
+ * @param cause
+ * of the exception
+ */
+ public NoWeightProvidedInMultiAssignmentModeException(final Throwable cause) {
+ super(THIS_ERROR, cause);
+ }
+
+ /**
+ * Parameterized constructor.
+ *
+ * @param message
+ * of the exception
+ * @param cause
+ * of the exception
+ */
+ public NoWeightProvidedInMultiAssignmentModeException(final String message, final Throwable cause) {
+ super(message, THIS_ERROR, cause);
+ }
+
+ /**
+ * Parameterized constructor.
+ *
+ * @param message
+ * of the exception
+ */
+ public NoWeightProvidedInMultiAssignmentModeException(final String message) {
+ super(message, THIS_ERROR);
+ }
+}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Action.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Action.java
index 30c2ef475..1c6f2e568 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Action.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Action.java
@@ -38,7 +38,16 @@ public interface Action extends TenantAwareBaseEntity {
* Maximum length of external reference.
*/
int EXTERNAL_REF_MAX_LENGTH = 512;
-
+
+ /**
+ * Minimum weight to indicate the priority of {@link Action}.
+ */
+ int WEIGHT_MIN = 0;
+ /**
+ * Maximum weight to indicate the priority of {@link Action}.
+ */
+ int WEIGHT_MAX = 1000;
+
/**
* @return the distributionSet
*/
@@ -80,6 +89,11 @@ public interface Action extends TenantAwareBaseEntity {
*/
long getForcedTime();
+ /**
+ * @return priority of the {@link Action}.
+ */
+ Optional getWeight();
+
/**
* @return rolloutGroup related to this {@link Action}.
*/
@@ -106,10 +120,11 @@ public interface Action extends TenantAwareBaseEntity {
String getMaintenanceWindowTimeZone();
/**
- * @param externalRef associated with this action
+ * @param externalRef
+ * associated with this action
*/
void setExternalRef(@NotEmpty String externalRef);
-
+
/**
* @return externalRef of the action
*/
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DeploymentRequest.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DeploymentRequest.java
index b1c00aa24..df0ee59c6 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DeploymentRequest.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DeploymentRequest.java
@@ -10,6 +10,8 @@ package org.eclipse.hawkbit.repository.model;
import java.util.Objects;
+import javax.validation.Valid;
+
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -19,6 +21,7 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
*/
public class DeploymentRequest {
private final Long distributionSetId;
+ @Valid
private final TargetWithActionType targetWithActionType;
/**
@@ -34,6 +37,8 @@ public class DeploymentRequest {
* specified for the action.
* @param forceTime
* at what time the type soft turns into forced.
+ * @param weight
+ * the priority of an {@link Action}.
* @param maintenanceSchedule
* is the cron expression to be used for scheduling maintenance
* windows. Expression has 6 mandatory fields and 1 last optional
@@ -51,11 +56,10 @@ public class DeploymentRequest {
* if the parameters do not define a valid maintenance schedule.
*/
public DeploymentRequest(final String controllerId, final Long distributionSetId, final ActionType actionType,
- final long forceTime, final String maintenanceSchedule, final String maintenanceWindowDuration,
- final String maintenanceWindowTimeZone) {
- this.targetWithActionType = new TargetWithActionType(controllerId, actionType, forceTime, maintenanceSchedule,
- maintenanceWindowDuration,
- maintenanceWindowTimeZone);
+ final long forceTime, final Integer weight, final String maintenanceSchedule,
+ final String maintenanceWindowDuration, final String maintenanceWindowTimeZone) {
+ this.targetWithActionType = new TargetWithActionType(controllerId, actionType, forceTime, weight,
+ maintenanceSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone);
this.distributionSetId = distributionSetId;
}
@@ -71,14 +75,13 @@ public class DeploymentRequest {
return targetWithActionType;
}
-
@Override
public String toString() {
return String.format(
- "DeploymentRequest [controllerId=%s, distributionSetId=%d, actionType=%s, forceTime=%d, maintenanceSchedule=%s, maintenanceWindowDuration=%s, maintenanceWindowTimeZone=%s]",
+ "DeploymentRequest [controllerId=%s, distributionSetId=%d, actionType=%s, forceTime=%d, weight=%d, maintenanceSchedule=%s, maintenanceWindowDuration=%s, maintenanceWindowTimeZone=%s]",
targetWithActionType.getControllerId(), getDistributionSetId(), targetWithActionType.getActionType(),
- targetWithActionType.getForceTime(), targetWithActionType.getMaintenanceSchedule(),
- targetWithActionType.getMaintenanceWindowDuration(),
+ targetWithActionType.getForceTime(), targetWithActionType.getWeight(),
+ targetWithActionType.getMaintenanceSchedule(), targetWithActionType.getMaintenanceWindowDuration(),
targetWithActionType.getMaintenanceWindowTimeZone());
}
@@ -86,7 +89,7 @@ public class DeploymentRequest {
public int hashCode() {
return Objects.hash(distributionSetId, targetWithActionType);
}
-
+
@Override
public boolean equals(final Object obj) {
if (this == obj) {
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DeploymentRequestBuilder.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DeploymentRequestBuilder.java
index 8386ef730..268a1bd70 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DeploymentRequestBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DeploymentRequestBuilder.java
@@ -18,7 +18,7 @@ public class DeploymentRequestBuilder {
private final String controllerId;
private final Long distributionSetId;
-
+ private Integer weight;
private long forceTime = RepositoryModelConstants.NO_FORCE_TIME;
private ActionType actionType = ActionType.FORCED;
private String maintenanceSchedule;
@@ -63,6 +63,18 @@ public class DeploymentRequestBuilder {
return this;
}
+ /**
+ * Set the weight of the action.
+ *
+ * @param weight
+ * the priority given to the action.
+ * @return builder
+ */
+ public DeploymentRequestBuilder setWeight(final Integer weight) {
+ this.weight = weight;
+ return this;
+ }
+
/**
* Set a maintenanceWindow
*
@@ -94,8 +106,8 @@ public class DeploymentRequestBuilder {
* @return the request object
*/
public DeploymentRequest build() {
- return new DeploymentRequest(controllerId, distributionSetId, actionType, forceTime, maintenanceSchedule,
- maintenanceWindowDuration, maintenanceWindowTimeZone);
+ return new DeploymentRequest(controllerId, distributionSetId, actionType, forceTime, weight,
+ maintenanceSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone);
}
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Rollout.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Rollout.java
index 5fac43173..7e1c50e2c 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Rollout.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Rollout.java
@@ -8,6 +8,7 @@
*/
package org.eclipse.hawkbit.repository.model;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -101,6 +102,11 @@ public interface Rollout extends NamedEntity {
*/
String getApprovalRemark();
+ /**
+ * @return the priority of {@link Rollout}.
+ */
+ Optional getWeight();
+
/**
*
* State machine for rollout.
@@ -197,5 +203,4 @@ public interface Rollout extends NamedEntity {
*/
DENIED
}
-
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetFilterQuery.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetFilterQuery.java
index 987880a33..e4b51e47a 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetFilterQuery.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetFilterQuery.java
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.model;
import java.util.Collections;
import java.util.EnumSet;
+import java.util.Optional;
import java.util.Set;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -70,4 +71,10 @@ public interface TargetFilterQuery extends TenantAwareBaseEntity {
*/
ActionType getAutoAssignActionType();
+ /**
+ * @return the weight of the {@link Action}s created during an auto
+ * assignment.
+ */
+ Optional getAutoAssignWeight();
+
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionType.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionType.java
index ac48f3849..847861636 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionType.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionType.java
@@ -10,6 +10,9 @@ package org.eclipse.hawkbit.repository.model;
import java.util.Objects;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -21,6 +24,9 @@ public class TargetWithActionType {
private final String controllerId;
private final ActionType actionType;
private final long forceTime;
+ @Min(Action.WEIGHT_MIN)
+ @Max(Action.WEIGHT_MAX)
+ private final Integer weight;
private String maintenanceSchedule;
private String maintenanceWindowDuration;
private String maintenanceWindowTimeZone;
@@ -32,7 +38,7 @@ public class TargetWithActionType {
* ID if the controller
*/
public TargetWithActionType(final String controllerId) {
- this(controllerId, ActionType.FORCED, 0);
+ this(controllerId, ActionType.FORCED, 0, null);
}
/**
@@ -43,13 +49,17 @@ public class TargetWithActionType {
* @param actionType
* specified for the action.
* @param forceTime
- * after that point in time the action is exposed as forcen in
+ * after that point in time the action is exposed as forced in
* case the type is {@link ActionType#TIMEFORCED}
+ * @param weight
+ * the priority of an {@link Action}
*/
- public TargetWithActionType(final String controllerId, final ActionType actionType, final long forceTime) {
+ public TargetWithActionType(final String controllerId, final ActionType actionType, final long forceTime,
+ final Integer weight) {
this.controllerId = controllerId;
this.actionType = actionType != null ? actionType : ActionType.FORCED;
this.forceTime = forceTime;
+ this.weight = weight;
}
/**
@@ -61,8 +71,10 @@ public class TargetWithActionType {
* @param actionType
* specified for the action.
* @param forceTime
- * after that point in time the action is exposed as forcen in
+ * after that point in time the action is exposed as forced in
* case the type is {@link ActionType#TIMEFORCED}
+ * @param weight
+ * the priority of an {@link Action}
* @param maintenanceSchedule
* is the cron expression to be used for scheduling maintenance
* windows. Expression has 6 mandatory fields and 1 last optional
@@ -80,9 +92,9 @@ public class TargetWithActionType {
* if the parameters do not define a valid maintenance schedule.
*/
public TargetWithActionType(final String controllerId, final ActionType actionType, final long forceTime,
- final String maintenanceSchedule, final String maintenanceWindowDuration,
+ final Integer weight, final String maintenanceSchedule, final String maintenanceWindowDuration,
final String maintenanceWindowTimeZone) {
- this(controllerId, actionType, forceTime);
+ this(controllerId, actionType, forceTime, weight);
this.maintenanceSchedule = maintenanceSchedule;
this.maintenanceWindowDuration = maintenanceWindowDuration;
@@ -104,6 +116,11 @@ public class TargetWithActionType {
return RepositoryModelConstants.NO_FORCE_TIME;
}
+ public Integer getWeight() {
+ return weight;
+
+ }
+
public String getControllerId() {
return controllerId;
}
@@ -138,14 +155,14 @@ public class TargetWithActionType {
@Override
public String toString() {
return "TargetWithActionType [controllerId=" + controllerId + ", actionType=" + getActionType() + ", forceTime="
- + getForceTime() + ", maintenanceSchedule=" + getMaintenanceSchedule() + ", maintenanceWindowDuration="
- + getMaintenanceWindowDuration() + ", maintenanceWindowTimeZone=" + getMaintenanceWindowTimeZone()
- + "]";
+ + getForceTime() + ", weight=" + getWeight() + ", maintenanceSchedule=" + getMaintenanceSchedule()
+ + ", maintenanceWindowDuration=" + getMaintenanceWindowDuration() + ", maintenanceWindowTimeZone="
+ + getMaintenanceWindowTimeZone() + "]";
}
@Override
public int hashCode() {
- return Objects.hash(actionType, controllerId, forceTime, maintenanceSchedule, maintenanceWindowDuration,
+ return Objects.hash(actionType, controllerId, forceTime, weight, maintenanceSchedule, maintenanceWindowDuration,
maintenanceWindowTimeZone);
}
@@ -163,10 +180,9 @@ public class TargetWithActionType {
}
final TargetWithActionType other = (TargetWithActionType) obj;
return Objects.equals(actionType, other.actionType) && Objects.equals(controllerId, other.controllerId)
- && Objects.equals(forceTime, other.forceTime)
+ && Objects.equals(forceTime, other.forceTime) && Objects.equals(weight, other.weight)
&& Objects.equals(maintenanceSchedule, other.maintenanceSchedule)
&& Objects.equals(maintenanceWindowDuration, other.maintenanceWindowDuration)
&& Objects.equals(maintenanceWindowTimeZone, other.maintenanceWindowTimeZone);
}
-
}
diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/AbstractRolloutManagement.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/AbstractRolloutManagement.java
index 4a10ff38b..61bdc7dff 100644
--- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/AbstractRolloutManagement.java
+++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/AbstractRolloutManagement.java
@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
+import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.context.ApplicationContext;
import org.springframework.integration.support.locks.LockRegistry;
@@ -54,12 +55,18 @@ public abstract class AbstractRolloutManagement implements RolloutManagement {
protected final RolloutApprovalStrategy rolloutApprovalStrategy;
+ protected final TenantConfigurationManagement tenantConfigurationManagement;
+
+ protected final SystemSecurityContext systemSecurityContext;
+
protected AbstractRolloutManagement(final TargetManagement targetManagement,
final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager,
final TenantAware tenantAware, final LockRegistry lockRegistry,
- final RolloutApprovalStrategy rolloutApprovalStrategy) {
+ final RolloutApprovalStrategy rolloutApprovalStrategy,
+ final TenantConfigurationManagement tenantConfigurationManagement,
+ final SystemSecurityContext systemSecurityContext) {
this.targetManagement = targetManagement;
this.deploymentManagement = deploymentManagement;
this.rolloutGroupManagement = rolloutGroupManagement;
@@ -70,6 +77,8 @@ public abstract class AbstractRolloutManagement implements RolloutManagement {
this.tenantAware = tenantAware;
this.lockRegistry = lockRegistry;
this.rolloutApprovalStrategy = rolloutApprovalStrategy;
+ this.tenantConfigurationManagement = tenantConfigurationManagement;
+ this.systemSecurityContext = systemSecurityContext;
}
protected RolloutGroupsValidation validateTargetsInGroups(final List groups, final String baseFilter,
@@ -156,5 +165,4 @@ public abstract class AbstractRolloutManagement implements RolloutManagement {
return new AsyncResult<>(validateTargetsInGroups(
groups.stream().map(RolloutGroupCreate::build).collect(Collectors.toList()), baseFilter, totalTargets));
}
-
}
diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractRolloutUpdateCreate.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractRolloutUpdateCreate.java
index 11f28ba77..f43e374b6 100644
--- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractRolloutUpdateCreate.java
+++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractRolloutUpdateCreate.java
@@ -10,8 +10,13 @@ package org.eclipse.hawkbit.repository.builder;
import java.util.Optional;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
import org.eclipse.hawkbit.repository.ValidString;
+import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
+import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.springframework.util.StringUtils;
/**
@@ -30,26 +35,77 @@ public abstract class AbstractRolloutUpdateCreate extends AbstractNamedEntity
protected Long forcedTime;
protected Long startAt;
+ @Min(Action.WEIGHT_MIN)
+ @Max(Action.WEIGHT_MAX)
+ protected Integer weight;
+
+ /**
+ * {@link DistributionSet} of rollout
+ *
+ * @param set
+ * ID of the set
+ * @return this builder
+ */
public T set(final long set) {
this.set = set;
return (T) this;
}
+ /**
+ * Filter of the rollout
+ *
+ * @param targetFilterQuery
+ * query
+ * @return this builder
+ */
public T targetFilterQuery(final String targetFilterQuery) {
this.targetFilterQuery = StringUtils.trimWhitespace(targetFilterQuery);
return (T) this;
}
+ /**
+ * {@link ActionType} used for {@link Action}s
+ *
+ * @param actionType
+ * type
+ * @return this builder
+ */
public T actionType(final ActionType actionType) {
this.actionType = actionType;
return (T) this;
}
+ /**
+ * forcedTime used for {@link Action}s
+ *
+ * @param forcedTime
+ * time
+ * @return this builder
+ */
public T forcedTime(final Long forcedTime) {
this.forcedTime = forcedTime;
return (T) this;
}
+ /**
+ * weight used for {@link Action}s
+ *
+ * @param weight
+ * weight
+ * @return this builder
+ */
+ public T weight(final Integer weight) {
+ this.weight = weight;
+ return (T) this;
+ }
+
+ /**
+ * Set start of the Rollout
+ *
+ * @param startAt
+ * start time point
+ * @return this builder
+ */
public T startAt(final Long startAt) {
this.startAt = startAt;
return (T) this;
@@ -67,6 +123,10 @@ public abstract class AbstractRolloutUpdateCreate extends AbstractNamedEntity
return Optional.ofNullable(forcedTime);
}
+ public Optional getWeight() {
+ return Optional.ofNullable(weight);
+ }
+
public Optional getStartAt() {
return Optional.ofNullable(startAt);
}
diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractTargetFilterQueryUpdateCreate.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractTargetFilterQueryUpdateCreate.java
index c7693e464..1044f353e 100644
--- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractTargetFilterQueryUpdateCreate.java
+++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractTargetFilterQueryUpdateCreate.java
@@ -10,8 +10,13 @@ package org.eclipse.hawkbit.repository.builder;
import java.util.Optional;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
import org.eclipse.hawkbit.repository.ValidString;
+import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
+import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.util.StringUtils;
/**
@@ -31,6 +36,17 @@ public abstract class AbstractTargetFilterQueryUpdateCreate extends AbstractB
protected ActionType actionType;
+ @Min(Action.WEIGHT_MIN)
+ @Max(Action.WEIGHT_MAX)
+ protected Integer weight;
+
+ /**
+ * Set DS ID of the {@link Action} created during auto assignment
+ *
+ * @param distributionSetId
+ * of the {@link TargetFilterQuery}
+ * @return this builder
+ */
public T autoAssignDistributionSet(final Long distributionSetId) {
this.distributionSetId = distributionSetId;
return (T) this;
@@ -40,15 +56,46 @@ public abstract class AbstractTargetFilterQueryUpdateCreate extends AbstractB
return Optional.ofNullable(distributionSetId);
}
+ /**
+ * Set {@link ActionType} of the {@link Action} created during auto
+ * assignment
+ *
+ * @param actionType
+ * of the {@link TargetFilterQuery}
+ * @return this builder
+ */
public T autoAssignActionType(final ActionType actionType) {
this.actionType = actionType;
return (T) this;
}
+ /**
+ * Set weight of the {@link Action} created during auto assignment
+ *
+ * @param weight
+ * of the {@link TargetFilterQuery}
+ * @return this builder
+ */
+ public T autoAssignWeight(final Integer weight) {
+ this.weight = weight;
+ return (T) this;
+ }
+
+ public Optional getAutoAssignWeight() {
+ return Optional.ofNullable(weight);
+ }
+
public Optional getAutoAssignActionType() {
return Optional.ofNullable(actionType);
}
+ /**
+ * Set name of the filter
+ *
+ * @param name
+ * of the {@link TargetFilterQuery}
+ * @return this builder
+ */
public T name(final String name) {
this.name = StringUtils.trimWhitespace(name);
return (T) this;
@@ -58,6 +105,13 @@ public abstract class AbstractTargetFilterQueryUpdateCreate extends AbstractB
return Optional.ofNullable(name);
}
+ /**
+ * Set query used by the filter
+ *
+ * @param query
+ * of the {@link TargetFilterQuery}
+ * @return this builder
+ */
public T query(final String query) {
this.query = StringUtils.trimWhitespace(query);
return (T) this;
diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/GenericRolloutUpdate.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/GenericRolloutUpdate.java
index a7ae902a6..2b2678175 100644
--- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/GenericRolloutUpdate.java
+++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/GenericRolloutUpdate.java
@@ -16,5 +16,4 @@ public class GenericRolloutUpdate extends AbstractRolloutUpdateCreate,
List findByTargetAndActiveOrderByIdAsc(JpaTarget target, boolean active);
/**
- * Retrieves the oldest {@link Action} that is active and referring to the
- * given {@link Target}.
- *
- * @param sort
- * order
+ * Retrieves the active {@link Action}s with the highest weights that refer
+ * to the given {@link Target}. If {@link Action}s have the same weight they
+ * are ordered ascending by ID (oldest ones first).
+ *
+ * @param pageable
+ * pageable
* @param controllerId
* the target to find assigned actions
- * @param active
- * the action active flag
- *
- * @return the found {@link Action}
+ * @return the found {@link Action}s
*/
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
- Optional findFirstByTargetControllerIdAndActive(Sort sort, String controllerId, boolean active);
+ Page findByTargetControllerIdAndActiveIsTrueAndWeightIsNotNullOrderByWeightDescIdAsc(Pageable pageable,
+ String controllerId);
+
+ /**
+ * Retrieves the active {@link Action}s with the lowest IDs (the oldest one)
+ * whose weight is null and that that refers to the given {@link Target}.
+ *
+ * @param pageable
+ * pageable
+ * @param controllerId
+ * the target to find assigned actions
+ * @return the found {@link Action}s
+ */
+ @EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
+ Page findByTargetControllerIdAndActiveIsTrueAndWeightIsNullOrderByIdAsc(Pageable pageable,
+ String controllerId);
/**
* Checks if an active action exists for given
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaActionManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaActionManagement.java
new file mode 100644
index 000000000..67ed64264
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaActionManagement.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2019 Bosch Software Innovations GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.hawkbit.repository.jpa;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.eclipse.hawkbit.repository.RepositoryProperties;
+import org.eclipse.hawkbit.repository.model.Action;
+import org.springframework.data.domain.PageRequest;
+
+/**
+ * Implements utility methods for managing {@link Action}s
+ */
+public class JpaActionManagement {
+
+ protected final ActionRepository actionRepository;
+ protected final RepositoryProperties repositoryProperties;
+
+ protected JpaActionManagement(final ActionRepository actionRepository,
+ final RepositoryProperties repositoryProperties) {
+ this.actionRepository = actionRepository;
+ this.repositoryProperties = repositoryProperties;
+ }
+
+ protected List findActiveActionsWithHighestWeightConsideringDefault(final String controllerId,
+ final int maxActionCount) {
+ if (!actionRepository.activeActionExistsForControllerId(controllerId)) {
+ return Collections.emptyList();
+ }
+ final List actions = new ArrayList<>();
+ final PageRequest pageable = PageRequest.of(0, maxActionCount);
+ actions.addAll(actionRepository
+ .findByTargetControllerIdAndActiveIsTrueAndWeightIsNotNullOrderByWeightDescIdAsc(pageable, controllerId)
+ .getContent());
+ actions.addAll(actionRepository
+ .findByTargetControllerIdAndActiveIsTrueAndWeightIsNullOrderByIdAsc(pageable, controllerId)
+ .getContent());
+ final Comparator actionImportance = Comparator.comparingInt(this::getWeightConsideringDefault)
+ .reversed().thenComparing(Action::getId);
+ return actions.stream().sorted(actionImportance).limit(maxActionCount).collect(Collectors.toList());
+ }
+
+ protected int getWeightConsideringDefault(final Action action) {
+ return action.getWeight().orElse(repositoryProperties.getActionWeightIfAbsent());
+ }
+
+}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java
index fb0eab30d..3ab73a0b5 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java
@@ -110,7 +110,7 @@ import com.google.common.collect.Sets;
*/
@Transactional(readOnly = true)
@Validated
-public class JpaControllerManagement implements ControllerManagement {
+public class JpaControllerManagement extends JpaActionManagement implements ControllerManagement {
private static final Logger LOG = LoggerFactory.getLogger(JpaControllerManagement.class);
private final BlockingDeque queue;
@@ -118,9 +118,6 @@ public class JpaControllerManagement implements ControllerManagement {
@Autowired
private EntityManager entityManager;
- @Autowired
- private ActionRepository actionRepository;
-
@Autowired
private TargetRepository targetRepository;
@@ -157,10 +154,9 @@ public class JpaControllerManagement implements ControllerManagement {
@Autowired
private TenantAware tenantAware;
- private final RepositoryProperties repositoryProperties;
-
JpaControllerManagement(final ScheduledExecutorService executorService,
- final RepositoryProperties repositoryProperties) {
+ final RepositoryProperties repositoryProperties, final ActionRepository actionRepository) {
+ super(actionRepository, repositoryProperties);
if (!repositoryProperties.isEagerPollPersistence()) {
executorService.scheduleWithFixedDelay(this::flushUpdateQueue,
@@ -171,8 +167,6 @@ public class JpaControllerManagement implements ControllerManagement {
} else {
queue = null;
}
-
- this.repositoryProperties = repositoryProperties;
}
@Override
@@ -346,23 +340,19 @@ public class JpaControllerManagement implements ControllerManagement {
}
@Override
- public Optional findOldestActiveActionByTarget(final String controllerId) {
- if (!actionRepository.activeActionExistsForControllerId(controllerId)) {
- return Optional.empty();
- }
-
- // used in favorite to findFirstByTargetAndActiveOrderByIdAsc due to
- // DATAJPA-841 issue.
- return actionRepository.findFirstByTargetControllerIdAndActive(new Sort(Direction.ASC, "id"), controllerId,
- true);
+ public Optional findActiveActionWithHighestWeight(final String controllerId) {
+ return findActiveActionsWithHighestWeight(controllerId, 1).stream().findFirst();
}
@Override
- public Page findActiveActionsByTarget(final Pageable pageable, final String controllerId) {
- if (!actionRepository.activeActionExistsForControllerId(controllerId)) {
- return Page.empty();
- }
- return actionRepository.findByActiveAndTarget(pageable, controllerId, true);
+ public List findActiveActionsWithHighestWeight(final String controllerId,
+ final int maxActionCount) {
+ return findActiveActionsWithHighestWeightConsideringDefault(controllerId, maxActionCount);
+ }
+
+ @Override
+ public int getWeightConsideringDefault(final Action action) {
+ return super.getWeightConsideringDefault(action);
}
@Override
@@ -376,7 +366,7 @@ public class JpaControllerManagement implements ControllerManagement {
}
@Override
- public void deleteExistingTarget(@NotEmpty String controllerId) {
+ public void deleteExistingTarget(@NotEmpty final String controllerId) {
final Target target = targetRepository.findByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
targetRepository.deleteById(target.getId());
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java
index 107921e57..06bcf995c 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java
@@ -8,7 +8,6 @@
*/
package org.eclipse.hawkbit.repository.jpa;
-import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
import java.io.Serializable;
@@ -38,6 +37,7 @@ import org.eclipse.hawkbit.repository.ActionFields;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants;
+import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
@@ -57,6 +57,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
+import org.eclipse.hawkbit.repository.jpa.utils.TenantConfigHelper;
+import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -103,7 +105,7 @@ import com.google.common.collect.Lists;
*/
@Transactional(readOnly = true)
@Validated
-public class JpaDeploymentManagement implements DeploymentManagement {
+public class JpaDeploymentManagement extends JpaActionManagement implements DeploymentManagement {
private static final Logger LOG = LoggerFactory.getLogger(JpaDeploymentManagement.class);
@@ -124,7 +126,6 @@ public class JpaDeploymentManagement implements DeploymentManagement {
}
private final EntityManager entityManager;
- private final ActionRepository actionRepository;
private final DistributionSetRepository distributionSetRepository;
private final TargetRepository targetRepository;
private final ActionStatusRepository actionStatusRepository;
@@ -146,9 +147,10 @@ public class JpaDeploymentManagement implements DeploymentManagement {
final EventPublisherHolder eventPublisherHolder, final AfterTransactionCommitExecutor afterCommit,
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager,
final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement,
- final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final Database database) {
+ final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final Database database,
+ final RepositoryProperties repositoryProperties) {
+ super(actionRepository, repositoryProperties);
this.entityManager = entityManager;
- this.actionRepository = actionRepository;
this.distributionSetRepository = distributionSetRepository;
this.targetRepository = targetRepository;
this.actionStatusRepository = actionStatusRepository;
@@ -194,6 +196,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Transactional(isolation = Isolation.READ_COMMITTED)
public List assignDistributionSets(
final List deploymentRequests, final String actionMessage) {
+ WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
+ .validate(deploymentRequests);
return assignDistributionSets(deploymentRequests, actionMessage, onlineDsAssignmentStrategy);
}
@@ -275,8 +279,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
final AbstractDsAssignmentStrategy assignmentStrategy) {
final JpaDistributionSet distributionSetEntity = getAndValidateDsById(dsID);
- final List targetIds = targetsWithActionType.stream().map(TargetWithActionType::getControllerId).distinct()
- .collect(Collectors.toList());
+ final List targetIds = targetsWithActionType.stream().map(TargetWithActionType::getControllerId)
+ .distinct().collect(Collectors.toList());
final List targetEntities = assignmentStrategy.findTargetsForAssignment(targetIds,
distributionSetEntity.getId());
@@ -285,8 +289,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
return allTargetsAlreadyAssignedResult(distributionSetEntity, targetsWithActionType.size());
}
- final List assignedActions = doAssignDistributionSetToTargets(targetsWithActionType,
- actionMessage, assignmentStrategy, distributionSetEntity, targetEntities);
+ final List assignedActions = doAssignDistributionSetToTargets(targetsWithActionType, actionMessage,
+ assignmentStrategy, distributionSetEntity, targetEntities);
return buildAssignmentResult(distributionSetEntity, assignedActions, targetsWithActionType.size());
}
@@ -659,6 +663,16 @@ public class JpaDeploymentManagement implements DeploymentManagement {
return actionRepository.findByActiveAndTarget(pageable, controllerId, false);
}
+ @Override
+ public List findActiveActionsWithHighestWeight(final String controllerId, final int maxActionCount) {
+ return findActiveActionsWithHighestWeightConsideringDefault(controllerId, maxActionCount);
+ }
+
+ @Override
+ public int getWeightConsideringDefault(final Action action) {
+ return super.getWeightConsideringDefault(action);
+ }
+
@Override
public long countActionsByTarget(final String controllerId) {
throwExceptionIfTargetDoesNotExist(controllerId);
@@ -823,7 +837,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
}
private boolean isMultiAssignmentsEnabled() {
- return getConfigValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class);
+ return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
+ .isMultiAssignmentsEnabled();
}
private T getConfigValue(final String key, final Class valueType) {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java
index 342fa64ff..26a31d931 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java
@@ -34,6 +34,7 @@ import org.eclipse.hawkbit.repository.RolloutHelper;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.RolloutStatusCache;
import org.eclipse.hawkbit.repository.TargetManagement;
+import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.builder.GenericRolloutUpdate;
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
@@ -43,6 +44,8 @@ import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEve
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
+import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
+import org.eclipse.hawkbit.repository.exception.NoWeightProvidedInMultiAssignmentModeException;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
@@ -57,6 +60,8 @@ import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
+import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper;
+import org.eclipse.hawkbit.repository.jpa.utils.TenantConfigHelper;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -74,6 +79,7 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
+import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -165,9 +171,12 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
final EventPublisherHolder eventPublisherHolder, final VirtualPropertyReplacer virtualPropertyReplacer,
final PlatformTransactionManager txManager, final TenantAware tenantAware, final LockRegistry lockRegistry,
- final Database database, final RolloutApprovalStrategy rolloutApprovalStrategy) {
+ final Database database, final RolloutApprovalStrategy rolloutApprovalStrategy,
+ final TenantConfigurationManagement tenantConfigurationManagement,
+ final SystemSecurityContext systemSecurityContext) {
super(targetManagement, deploymentManagement, rolloutGroupManagement, distributionSetManagement, context,
- virtualPropertyReplacer, txManager, tenantAware, lockRegistry, rolloutApprovalStrategy);
+ virtualPropertyReplacer, txManager, tenantAware, lockRegistry, rolloutApprovalStrategy,
+ tenantConfigurationManagement, systemSecurityContext);
this.eventPublisherHolder = eventPublisherHolder;
this.database = database;
}
@@ -226,7 +235,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
}
private JpaRollout createRollout(final JpaRollout rollout) {
-
+ WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(rollout);
final Long totalTargets = targetManagement.countByRsql(rollout.getTargetFilterQuery());
if (totalTargets == 0) {
throw new ValidationException("Rollout does not match any existing targets");
@@ -618,6 +627,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
action.setStatus(Status.SCHEDULED);
action.setRollout(rollout);
action.setRolloutGroup(rolloutGroup);
+ rollout.getWeight().ifPresent(action::setWeight);
actionRepository.save(action);
});
}
@@ -1008,6 +1018,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
update.getDescription().ifPresent(rollout::setDescription);
update.getActionType().ifPresent(rollout::setActionType);
update.getForcedTime().ifPresent(rollout::setForcedTime);
+ update.getWeight().ifPresent(rollout::setWeight);
update.getStartAt().ifPresent(rollout::setStartAt);
update.getSet().ifPresent(setId -> {
final DistributionSet set = distributionSetManagement.get(setId)
@@ -1136,5 +1147,4 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
QuotaHelper.assertAssignmentQuota(target.getId(), requested, quota, Action.class, Target.class,
actionRepository::countByTargetId);
}
-
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java
index c3b767077..b05e5f221 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetFilterQueryManagement.java
@@ -18,6 +18,8 @@ import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
+import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
+import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.builder.GenericTargetFilterQueryUpdate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
@@ -32,11 +34,14 @@ import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
+import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper;
+import org.eclipse.hawkbit.repository.jpa.utils.TenantConfigHelper;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
+import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
@@ -67,19 +72,24 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
private final DistributionSetManagement distributionSetManagement;
private final QuotaManagement quotaManagement;
+ private final TenantConfigurationManagement tenantConfigurationManagement;
+ private final SystemSecurityContext systemSecurityContext;
private final Database database;
JpaTargetFilterQueryManagement(final TargetFilterQueryRepository targetFilterQueryRepository,
final TargetRepository targetRepository, final VirtualPropertyReplacer virtualPropertyReplacer,
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
- final Database database) {
+ final Database database, final TenantConfigurationManagement tenantConfigurationManagement,
+ final SystemSecurityContext systemSecurityContext) {
this.targetFilterQueryRepository = targetFilterQueryRepository;
this.targetRepository = targetRepository;
this.virtualPropertyReplacer = virtualPropertyReplacer;
this.distributionSetManagement = distributionSetManagement;
this.quotaManagement = quotaManagement;
this.database = database;
+ this.tenantConfigurationManagement = tenantConfigurationManagement;
+ this.systemSecurityContext = systemSecurityContext;
}
@Override
@@ -92,6 +102,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
// enforce the 'max targets per auto assign' quota right here even if
// the result of the filter query can vary over time
if (create.getAutoAssignDistributionSetId().isPresent()) {
+ WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(create);
create.getQuery().ifPresent(this::assertMaxTargetsQuota);
}
@@ -222,30 +233,26 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Override
@Transactional
- public TargetFilterQuery updateAutoAssignDSWithActionType(final long queryId, final Long dsId,
- final ActionType actionType) {
- final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(queryId);
-
- if (dsId == null) {
+ public TargetFilterQuery updateAutoAssignDS(final AutoAssignDistributionSetUpdate update) {
+ final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(
+ update.getTargetFilterId());
+ if (update.getDsId() == null) {
targetFilterQuery.setAutoAssignDistributionSet(null);
targetFilterQuery.setAutoAssignActionType(null);
+ targetFilterQuery.setAutoAssignWeight(null);
} else {
+ WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(update);
// we cannot be sure that the quota was enforced at creation time
// because the Target Filter Query REST API does not allow to
// specify an
// auto-assign distribution set when creating a target filter query
assertMaxTargetsQuota(targetFilterQuery.getQuery());
-
- final JpaDistributionSet distributionSetToAutoAssign = findDistributionSetAndThrowExceptionIfNotFound(dsId);
- // must be completed and not soft deleted
- verifyDistributionSetAndThrowExceptionIfNotValid(distributionSetToAutoAssign);
-
- targetFilterQuery.setAutoAssignDistributionSet(distributionSetToAutoAssign);
- // the action type is set to FORCED per default (when not explicitly
- // specified)
- targetFilterQuery.setAutoAssignActionType(sanitizeAutoAssignActionType(actionType));
+ final JpaDistributionSet ds = findDistributionSetAndThrowExceptionIfNotFound(update.getDsId());
+ verifyDistributionSetAndThrowExceptionIfNotValid(ds);
+ targetFilterQuery.setAutoAssignDistributionSet(ds);
+ targetFilterQuery.setAutoAssignActionType(sanitizeAutoAssignActionType(update.getActionType()));
+ targetFilterQuery.setAutoAssignWeight(update.getWeight());
}
-
return targetFilterQueryRepository.save(targetFilterQuery);
}
@@ -288,5 +295,4 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
targetRepository.count(RSQLUtility.parse(query, TargetFields.class, virtualPropertyReplacer, database)),
quotaManagement.getMaxTargetsPerAutoAssignment(), Target.class, TargetFilterQuery.class);
}
-
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java
index 1f38331a9..7c072c713 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java
@@ -541,9 +541,11 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final TargetFilterQueryRepository targetFilterQueryRepository, final TargetRepository targetRepository,
final VirtualPropertyReplacer virtualPropertyReplacer,
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
- final JpaProperties properties) {
+ final JpaProperties properties, final TenantConfigurationManagement tenantConfigurationManagement,
+ final SystemSecurityContext systemSecurityContext) {
return new JpaTargetFilterQueryManagement(targetFilterQueryRepository, targetRepository,
- virtualPropertyReplacer, distributionSetManagement, quotaManagement, properties.getDatabase());
+ virtualPropertyReplacer, distributionSetManagement, quotaManagement, properties.getDatabase(),
+ tenantConfigurationManagement, systemSecurityContext);
}
/**
@@ -620,10 +622,13 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
final EventPublisherHolder eventPublisherHolder, final VirtualPropertyReplacer virtualPropertyReplacer,
final PlatformTransactionManager txManager, final TenantAware tenantAware, final LockRegistry lockRegistry,
- final JpaProperties properties, final RolloutApprovalStrategy rolloutApprovalStrategy) {
+ final JpaProperties properties, final RolloutApprovalStrategy rolloutApprovalStrategy,
+ final TenantConfigurationManagement tenantConfigurationManagement,
+ final SystemSecurityContext systemSecurityContext) {
return new JpaRolloutManagement(targetManagement, deploymentManagement, rolloutGroupManagement,
distributionSetManagement, context, eventPublisherHolder, virtualPropertyReplacer, txManager,
- tenantAware, lockRegistry, properties.getDatabase(), rolloutApprovalStrategy);
+ tenantAware, lockRegistry, properties.getDatabase(), rolloutApprovalStrategy,
+ tenantConfigurationManagement, systemSecurityContext);
}
/**
@@ -671,11 +676,11 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final PlatformTransactionManager txManager,
final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware,
- final JpaProperties properties) {
+ final JpaProperties properties, final RepositoryProperties repositoryProperties) {
return new JpaDeploymentManagement(entityManager, actionRepository, distributionSetRepository, targetRepository,
actionStatusRepository, auditorProvider, eventPublisherHolder, afterCommit, virtualPropertyReplacer,
txManager, tenantConfigurationManagement, quotaManagement, systemSecurityContext, tenantAware,
- properties.getDatabase());
+ properties.getDatabase(), repositoryProperties);
}
/**
@@ -686,8 +691,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Bean
@ConditionalOnMissingBean
ControllerManagement controllerManagement(final ScheduledExecutorService executorService,
- final RepositoryProperties repositoryProperties) {
- return new JpaControllerManagement(executorService, repositoryProperties);
+ final RepositoryProperties repositoryProperties, final ActionRepository actionRepository) {
+ return new JpaControllerManagement(executorService, repositoryProperties, actionRepository);
}
@Bean
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java
index 8364dc001..49bfd7b9f 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java
@@ -97,6 +97,8 @@ public class AutoAssignChecker {
final Page filterQueries = targetFilterQueryManagement.findWithAutoAssignDS(pageRequest);
+ // we should ensure that the filter queries are executed
+ // in the order of weights
for (final TargetFilterQuery filterQuery : filterQueries) {
checkByTargetFilterQueryAndAssignDS(filterQuery);
}
@@ -143,7 +145,8 @@ public class AutoAssignChecker {
return DeploymentHelper.runInNewTransaction(transactionManager, "autoAssignDSToTargets",
Isolation.READ_COMMITTED.value(), status -> {
final List deploymentRequests = createAssignmentRequests(
- targetFilterQuery.getQuery(), dsId, targetFilterQuery.getAutoAssignActionType(), PAGE_SIZE);
+ targetFilterQuery.getQuery(), dsId, targetFilterQuery.getAutoAssignActionType(),
+ targetFilterQuery.getAutoAssignWeight().orElse(null), PAGE_SIZE);
final int count = deploymentRequests.size();
if (count > 0) {
deploymentManagement.assignDistributionSets(deploymentRequests, actionMessage);
@@ -168,17 +171,15 @@ public class AutoAssignChecker {
* @return list of targets with action type
*/
private List createAssignmentRequests(final String targetFilterQuery, final Long dsId,
- final ActionType type, final int count) {
+ final ActionType type, final Integer weight, final int count) {
final Page targets = targetManagement.findByTargetFilterQueryAndNonDS(PageRequest.of(0, count), dsId,
targetFilterQuery);
// the action type is set to FORCED per default (when not explicitly
// specified)
final ActionType autoAssignActionType = type == null ? ActionType.FORCED : type;
- return targets.getContent().stream()
- .map(t -> DeploymentManagement.deploymentRequest(t.getControllerId(), dsId)
- .setActionType(autoAssignActionType).build())
- .collect(Collectors.toList());
+ return targets.getContent().stream().map(t -> DeploymentManagement.deploymentRequest(t.getControllerId(), dsId)
+ .setActionType(autoAssignActionType).setWeight(weight).build()).collect(Collectors.toList());
}
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutCreate.java
index f9e791b4f..9011b5026 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaRolloutCreate.java
@@ -31,6 +31,7 @@ public class JpaRolloutCreate extends AbstractRolloutUpdateCreate
rollout.setDistributionSet(findDistributionSetAndThrowExceptionIfNotFound(set));
rollout.setTargetFilterQuery(targetFilterQuery);
rollout.setStartAt(startAt);
+ rollout.setWeight(weight);
if (actionType != null) {
rollout.setActionType(actionType);
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryBuilder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryBuilder.java
index b6a91c8cc..24f4e4867 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryBuilder.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryBuilder.java
@@ -9,6 +9,7 @@
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
+import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.builder.GenericTargetFilterQueryUpdate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
@@ -36,4 +37,9 @@ public class JpaTargetFilterQueryBuilder implements TargetFilterQueryBuilder {
return new JpaTargetFilterQueryCreate(distributionSetManagement);
}
+ @Override
+ public AutoAssignDistributionSetUpdate updateAutoAssign(final long id) {
+ return new AutoAssignDistributionSetUpdate(id);
+ }
+
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryCreate.java
index 163aa6efb..f73b498ff 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryCreate.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaTargetFilterQueryCreate.java
@@ -36,7 +36,8 @@ public class JpaTargetFilterQueryCreate extends AbstractTargetFilterQueryUpdateC
return new JpaTargetFilterQuery(name, query,
getAutoAssignDistributionSetId().map(this::findDistributionSetAndThrowExceptionIfNotFound).orElse(null),
- getAutoAssignActionType().filter(JpaTargetFilterQueryCreate::isAutoAssignActionTypeValid).orElse(null));
+ getAutoAssignActionType().filter(JpaTargetFilterQueryCreate::isAutoAssignActionTypeValid).orElse(null),
+ weight);
}
private DistributionSet findDistributionSetAndThrowExceptionIfNotFound(final Long setId) {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java
index 243d07afd..3b86ba46a 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java
@@ -28,6 +28,8 @@ import javax.persistence.NamedEntityGraphs;
import javax.persistence.NamedSubgraph;
import javax.persistence.OneToMany;
import javax.persistence.Table;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
@@ -89,6 +91,11 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Column(name = "forced_time")
private long forcedTime;
+ @Column(name = "weight")
+ @Min(Action.WEIGHT_MIN)
+ @Max(Action.WEIGHT_MAX)
+ private Integer weight;
+
@Column(name = "status", nullable = false)
@ObjectTypeConverter(name = "status", objectType = Action.Status.class, dataType = Integer.class, conversionValues = {
@ConversionValue(objectValue = "FINISHED", dataValue = "0"),
@@ -126,7 +133,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Column(name = "maintenance_time_zone", updatable = false, length = Action.MAINTENANCE_WINDOW_TIMEZONE_LENGTH)
private String maintenanceWindowTimeZone;
-
+
@Column(name = "external_ref", length = Action.EXTERNAL_REF_MAX_LENGTH)
private String externalRef;
@@ -192,6 +199,15 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
this.forcedTime = forcedTime;
}
+ @Override
+ public Optional getWeight() {
+ return Optional.ofNullable(weight);
+ }
+
+ public void setWeight(final Integer weight) {
+ this.weight = weight;
+ }
+
@Override
public RolloutGroup getRolloutGroup() {
return rolloutGroup;
@@ -213,8 +229,8 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Override
public String toString() {
return "JpaAction [distributionSet=" + distributionSet.getId() + ", version=" + getOptLockRevision() + ", id="
- + getId() + ", actionType=" + getActionType() + ", isActive=" + isActive() + ", createdAt="
- + getCreatedAt() + ", lastModifiedAt=" + getLastModifiedAt() + "]";
+ + getId() + ", actionType=" + getActionType() + ", weight=" + getWeight() + ", isActive=" + isActive()
+ + ", createdAt=" + getCreatedAt() + ", lastModifiedAt=" + getLastModifiedAt() + "]";
}
@Override
@@ -337,7 +353,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
}
@Override
- public void setExternalRef(String externalRef) {
+ public void setExternalRef(final String externalRef) {
this.externalRef = externalRef;
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRollout.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRollout.java
index 535215a4d..ecf156e16 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRollout.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRollout.java
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.stream.Collectors;
import javax.persistence.Column;
@@ -23,6 +24,8 @@ import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@@ -90,7 +93,7 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
@ConversionValue(objectValue = "DELETING", dataValue = "9"),
@ConversionValue(objectValue = "DELETED", dataValue = "10"),
@ConversionValue(objectValue = "WAITING_FOR_APPROVAL", dataValue = "11"),
- @ConversionValue(objectValue = "APPROVAL_DENIED", dataValue = "12")})
+ @ConversionValue(objectValue = "APPROVAL_DENIED", dataValue = "12") })
@Convert("rolloutstatus")
@NotNull
private RolloutStatus status = RolloutStatus.CREATING;
@@ -131,6 +134,11 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
@Size(max = Rollout.APPROVAL_REMARK_MAX_SIZE)
private String approvalRemark;
+ @Column(name = "weight")
+ @Min(Action.WEIGHT_MIN)
+ @Max(Action.WEIGHT_MAX)
+ private Integer weight;
+
@Transient
private transient TotalTargetCountStatus totalTargetCountStatus;
@@ -204,6 +212,15 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
this.forcedTime = forcedTime;
}
+ @Override
+ public Optional getWeight() {
+ return Optional.ofNullable(weight);
+ }
+
+ public void setWeight(final Integer weight) {
+ this.weight = weight;
+ }
+
@Override
public long getTotalTargets() {
return totalTargets;
@@ -299,5 +316,4 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
public void setApprovalRemark(final String approvalRemark) {
this.approvalRemark = approvalRemark;
}
-
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetFilterQuery.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetFilterQuery.java
index 2a7737882..af945a327 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetFilterQuery.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetFilterQuery.java
@@ -8,6 +8,8 @@
*/
package org.eclipse.hawkbit.repository.jpa.model;
+import java.util.Optional;
+
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
@@ -66,11 +68,15 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
@ObjectTypeConverter(name = "autoAssignActionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
- // Conversion for 'TIMEFORCED' is disabled because it is not permitted in autoAssignment
- @ConversionValue(objectValue = "DOWNLOAD_ONLY", dataValue = "3")})
+ // Conversion for 'TIMEFORCED' is disabled because it is not
+ // permitted in autoAssignment
+ @ConversionValue(objectValue = "DOWNLOAD_ONLY", dataValue = "3") })
@Convert("autoAssignActionType")
private ActionType autoAssignActionType;
+ @Column(name = "auto_assign_weight", nullable = true)
+ private Integer autoAssignWeight;
+
public JpaTargetFilterQuery() {
// Default constructor for JPA.
}
@@ -86,13 +92,16 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
* of the {@link TargetFilterQuery}.
* @param autoAssignActionType
* of the {@link TargetFilterQuery}.
+ * @param autoAssignWeight
+ * of the {@link TargetFilterQuery}.
*/
public JpaTargetFilterQuery(final String name, final String query, final DistributionSet autoAssignDistributionSet,
- final ActionType autoAssignActionType) {
+ final ActionType autoAssignActionType, final Integer autoAssignWeight) {
this.name = name;
this.query = query;
this.autoAssignDistributionSet = (JpaDistributionSet) autoAssignDistributionSet;
this.autoAssignActionType = autoAssignActionType;
+ this.autoAssignWeight = autoAssignWeight;
}
@Override
@@ -131,6 +140,15 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
this.autoAssignActionType = actionType;
}
+ @Override
+ public Optional getAutoAssignWeight() {
+ return Optional.ofNullable(autoAssignWeight);
+ }
+
+ public void setAutoAssignWeight(final Integer weight) {
+ this.autoAssignWeight = weight;
+ }
+
@Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/utils/TenantConfigHelper.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/utils/TenantConfigHelper.java
new file mode 100644
index 000000000..d79a38861
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/utils/TenantConfigHelper.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2019 Bosch Software Innovations GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.hawkbit.repository.jpa.utils;
+
+import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
+
+import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
+import org.eclipse.hawkbit.security.SystemSecurityContext;
+
+/**
+ * A collection of static helper methods for the tenant configuration
+ */
+public final class TenantConfigHelper {
+
+ private final TenantConfigurationManagement tenantConfigurationManagement;
+ private final SystemSecurityContext systemSecurityContext;
+
+ private TenantConfigHelper(final SystemSecurityContext systemSecurityContext,
+ final TenantConfigurationManagement tenantConfigurationManagement) {
+ this.systemSecurityContext = systemSecurityContext;
+ this.tenantConfigurationManagement = tenantConfigurationManagement;
+ }
+
+ /**
+ * Setting the context of the tenant.
+ *
+ * @param systemSecurityContext
+ * Security context used to get the tenant and for execution
+ * @param tenantConfigurationManagement
+ * to get the value from
+ * @return is active
+ */
+ public static TenantConfigHelper usingContext(final SystemSecurityContext systemSecurityContext,
+ final TenantConfigurationManagement tenantConfigurationManagement) {
+ return new TenantConfigHelper(systemSecurityContext, tenantConfigurationManagement);
+ }
+
+ /**
+ * Is multi-assignments enabled for the current tenant
+ *
+ * @return is active
+ */
+ public boolean isMultiAssignmentsEnabled() {
+ return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
+ .getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue());
+ }
+}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/utils/WeightValidationHelper.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/utils/WeightValidationHelper.java
new file mode 100644
index 000000000..3e333b391
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/utils/WeightValidationHelper.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2019 Bosch Software Innovations GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.hawkbit.repository.jpa.utils;
+
+import java.util.List;
+
+import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
+import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
+import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
+import org.eclipse.hawkbit.repository.exception.NoWeightProvidedInMultiAssignmentModeException;
+import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryCreate;
+import org.eclipse.hawkbit.repository.model.DeploymentRequest;
+import org.eclipse.hawkbit.repository.model.Rollout;
+import org.eclipse.hawkbit.security.SystemSecurityContext;
+
+/**
+ * Utility class to handle weight validation in Rollout, Auto Assignments, and
+ * Online Assignment.
+ */
+public final class WeightValidationHelper {
+
+ private final TenantConfigurationManagement tenantConfigurationManagement;
+ private final SystemSecurityContext systemSecurityContext;
+
+ private WeightValidationHelper(final SystemSecurityContext systemSecurityContext,
+ final TenantConfigurationManagement tenantConfigurationManagement) {
+ this.systemSecurityContext = systemSecurityContext;
+ this.tenantConfigurationManagement = tenantConfigurationManagement;
+ }
+
+ /**
+ * Setting the context of the tenant
+ *
+ * @param systemSecurityContext
+ * security context used to get the tenant and for execution
+ * @param tenantConfigurationManagement
+ * to get the value from
+ */
+ public static WeightValidationHelper usingContext(final SystemSecurityContext systemSecurityContext,
+ final TenantConfigurationManagement tenantConfigurationManagement) {
+ return new WeightValidationHelper(systemSecurityContext, tenantConfigurationManagement);
+ }
+
+ /**
+ * Validating weights associated with all the {@link DeploymentRequest}s
+ *
+ * @param deploymentRequests
+ * the {@linkplain List} of {@link DeploymentRequest}s
+ */
+ public void validate(final List deploymentRequests) {
+ final long assignmentsWithWeight = deploymentRequests.stream()
+ .filter(request -> request.getTargetWithActionType().getWeight() != null).count();
+ final boolean containsAssignmentWithWeight = assignmentsWithWeight > 0;
+ final boolean containsAssignmentWithoutWeight = assignmentsWithWeight < deploymentRequests.size();
+
+ validateWeight(containsAssignmentWithWeight, containsAssignmentWithoutWeight);
+ }
+
+ /**
+ * Validating weight associated with the {@link Rollout}
+ *
+ * @param rollout
+ * the {@linkplain Rollout}
+ */
+ public void validate(final Rollout rollout) {
+ validateWeight(rollout.getWeight().orElse(null));
+ }
+
+ /**
+ * Validating weight associated with the target filter query
+ *
+ * @param targetFilterQueryCreate
+ * the target filter query
+ */
+ public void validate(final JpaTargetFilterQueryCreate targetFilterQueryCreate) {
+ validateWeight(targetFilterQueryCreate.getAutoAssignWeight().orElse(null));
+
+ }
+
+ /**
+ * Validating weight associated with the auto assignment
+ *
+ * @param autoAssignDistributionSetUpdate
+ * the auto assignment distribution set update
+ */
+ public void validate(final AutoAssignDistributionSetUpdate autoAssignDistributionSetUpdate) {
+ validateWeight(autoAssignDistributionSetUpdate.getWeight());
+
+ }
+
+ /**
+ * Checks if the weight is valid
+ *
+ * @param weight
+ * weight tied to the rollout, auto assignment, or online
+ * assignment.
+ */
+ public void validateWeight(final Integer weight) {
+ final boolean hasWeight = weight != null;
+ validateWeight(hasWeight, !hasWeight);
+ }
+
+ /**
+ * Checks if the weight is valid with the multi-assignments being turned
+ * off/on.
+ *
+ * @param hasWeight
+ * indicator of the weight if it has numerical value
+ * @param hasNoWeight
+ * indicator of the weight if it doesn't have a numerical value
+ */
+ public void validateWeight(final boolean hasWeight, final boolean hasNoWeight) {
+ // remove bypassing the weight enforcement as soon as weight can be set
+ // via UI
+ final boolean bypassWeightEnforcement = true;
+ final boolean multiAssignmentsEnabled = TenantConfigHelper
+ .usingContext(systemSecurityContext, tenantConfigurationManagement).isMultiAssignmentsEnabled();
+ if (!multiAssignmentsEnabled && hasWeight) {
+ throw new MultiAssignmentIsNotEnabledException();
+ } else if (bypassWeightEnforcement) {
+ return;
+ } else if (multiAssignmentsEnabled && hasNoWeight) {
+ throw new NoWeightProvidedInMultiAssignmentModeException();
+ }
+ }
+}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/DB2/V1_12_15__add_weight___DB2.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/DB2/V1_12_15__add_weight___DB2.sql
new file mode 100644
index 000000000..9eaad20e1
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/DB2/V1_12_15__add_weight___DB2.sql
@@ -0,0 +1,3 @@
+ALTER TABLE sp_action ADD weight INT;
+ALTER TABLE sp_rollout ADD weight INT;
+ALTER TABLE sp_target_filter_query ADD auto_assign_weight INT;
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_12_15__add_weight___H2.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_12_15__add_weight___H2.sql
new file mode 100644
index 000000000..9eaad20e1
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_12_15__add_weight___H2.sql
@@ -0,0 +1,3 @@
+ALTER TABLE sp_action ADD weight INT;
+ALTER TABLE sp_rollout ADD weight INT;
+ALTER TABLE sp_target_filter_query ADD auto_assign_weight INT;
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_12_15__add_weight___MYSQL.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_12_15__add_weight___MYSQL.sql
new file mode 100644
index 000000000..9eaad20e1
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_12_15__add_weight___MYSQL.sql
@@ -0,0 +1,3 @@
+ALTER TABLE sp_action ADD weight INT;
+ALTER TABLE sp_rollout ADD weight INT;
+ALTER TABLE sp_target_filter_query ADD auto_assign_weight INT;
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_15__add_weight___SQL_SERVER.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_15__add_weight___SQL_SERVER.sql
new file mode 100644
index 000000000..9eaad20e1
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_15__add_weight___SQL_SERVER.sql
@@ -0,0 +1,3 @@
+ALTER TABLE sp_action ADD weight INT;
+ALTER TABLE sp_rollout ADD weight INT;
+ALTER TABLE sp_target_filter_query ADD auto_assign_weight INT;
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/RemoteIdEventTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/RemoteIdEventTest.java
index 1094d8a68..c664644f9 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/RemoteIdEventTest.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/RemoteIdEventTest.java
@@ -105,5 +105,4 @@ public class RemoteIdEventTest extends AbstractRemoteEventTest {
// gets added because events inherit from of java.util.EventObject
assertThat(underTestCreatedEvent).isEqualToIgnoringGivenFields(event, "source");
}
-
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/RemoteTenantAwareEventTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/RemoteTenantAwareEventTest.java
index 1efb4b091..e94c3e2b7 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/RemoteTenantAwareEventTest.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/RemoteTenantAwareEventTest.java
@@ -98,5 +98,4 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
assertThat(actionProperties).isEqualToComparingFieldByField(new ActionProperties(action));
assertThat(underTest.getDistributionSetId()).isEqualTo(action.getDistributionSet().getId());
}
-
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java
index e1c18f02e..e37d01e20 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java
@@ -104,7 +104,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(controllerManagement.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(),
module.getId())).isNotPresent();
- assertThat(controllerManagement.findOldestActiveActionByTarget(NOT_EXIST_ID)).isNotPresent();
+ assertThat(controllerManagement.findActiveActionWithHighestWeight(NOT_EXIST_ID)).isNotPresent();
assertThat(controllerManagement.hasTargetArtifactAssigned(target.getControllerId(), "XXX")).isFalse();
assertThat(controllerManagement.hasTargetArtifactAssigned(target.getId(), "XXX")).isFalse();
@@ -457,7 +457,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
.getContent();
assertThat(actionStatusList.get(actionStatusList.size() - 1).getStatus()).isEqualTo(expectedActionStatus);
if (actionActive) {
- assertThat(controllerManagement.findOldestActiveActionByTarget(controllerId).get().getId())
+ assertThat(controllerManagement.findActiveActionWithHighestWeight(controllerId).get().getId())
.isEqualTo(actionId);
}
}
@@ -501,7 +501,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Register a controller which does not exist")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
- @Expect(type = TargetPollEvent.class, count = 2)})
+ @Expect(type = TargetPollEvent.class, count = 2) })
public void findOrRegisterTargetIfItDoesNotExist() {
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST);
assertThat(target).as("target should not be null").isNotNull();
@@ -517,7 +517,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@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,
+ 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());
@@ -560,8 +560,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
.isThrownBy(() -> controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST));
verify(mockTargetRepository, times(TX_RT_MAX)).findOne(any());
- }
- finally {
+ } finally {
// revert
((JpaControllerManagement) controllerManagement).setTargetRepository(targetRepository);
}
@@ -597,22 +596,23 @@ 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)})
+ @Expect(type = TargetPollEvent.class, count = 3), @Expect(type = TargetUpdatedEvent.class, count = 1) })
public void findOrRegisterTargetIfItDoesNotExistDoesUpdateNameOnExistingTargetProperly() {
- String controllerId = "12345";
- String targetName = "UpdatedName";
+ final String controllerId = "12345";
+ final String targetName = "UpdatedName";
- final Target newTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, LOCALHOST);
+ final Target newTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, LOCALHOST);
assertThat(newTarget.getName()).isEqualTo(controllerId);
-
- Target firstTimeUpdatedTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, LOCALHOST, targetName);
+ final 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);
+ // Name should not change to default (name=targetId) if target is
+ // updated without new name provided
+ final Target secondTimeUpdatedTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId,
+ LOCALHOST);
assertThat(secondTimeUpdatedTarget.getName()).isEqualTo(targetName);
}
@@ -633,8 +633,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
.isThrownBy(() -> controllerManagement.findOrRegisterTargetIfItDoesNotExist("1234", LOCALHOST));
verify(mockTargetRepository, times(1)).findOne(any());
verify(mockTargetRepository, times(1)).save(any());
- }
- finally {
+ } finally {
// revert
((JpaControllerManagement) controllerManagement).setTargetRepository(targetRepository);
}
@@ -651,13 +650,11 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
when(mockTargetRepository.findOne(any())).thenThrow(RuntimeException.class);
try {
- assertThatExceptionOfType(RuntimeException.class)
- .as("Expected a RuntimeException to be thrown!")
+ assertThatExceptionOfType(RuntimeException.class).as("Expected a RuntimeException to be thrown!")
.isThrownBy(() -> controllerManagement.findOrRegisterTargetIfItDoesNotExist("aControllerId",
LOCALHOST));
verify(mockTargetRepository, times(1)).findOne(any());
- }
- finally {
+ } finally {
// revert
((JpaControllerManagement) controllerManagement).setTargetRepository(targetRepository);
}
@@ -1054,9 +1051,8 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
Collections.singletonMap(keyValid, valueTooLong), null));
assertThatExceptionOfType(InvalidTargetAttributeException.class)
- .as("Attribute with key NULL should not be created")
- .isThrownBy(() -> controllerManagement.updateControllerAttributes(controllerId,
- Collections.singletonMap(keyNull, valueValid), null));
+ .as("Attribute with key NULL should not be created").isThrownBy(() -> controllerManagement
+ .updateControllerAttributes(controllerId, Collections.singletonMap(keyNull, valueValid), null));
}
@Test
@@ -1298,9 +1294,9 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(actionId).isNotNull();
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
- .as("No QuotaExceededException thrown for too many DOWNLOADED updateActionStatus updates")
- .isThrownBy(() -> IntStream.range(0, maxMessages).forEach(i -> controllerManagement
- .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.DOWNLOADED))));
+ .as("No QuotaExceededException thrown for too many DOWNLOADED updateActionStatus updates").isThrownBy(
+ () -> IntStream.range(0, maxMessages).forEach(i -> controllerManagement.addUpdateActionStatus(
+ entityFactory.actionStatus().create(actionId).status(Status.DOWNLOADED))));
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.as("No QuotaExceededException thrown for too many ERROR updateActionStatus updates")
@@ -1326,18 +1322,18 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(actionId).isNotNull();
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
- .as("No QuotaExceededException thrown for too many DOWNLOADED updateActionStatus updates")
- .isThrownBy( ()-> IntStream.range(0, maxMessages).forEach(i -> controllerManagement
- .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.DOWNLOADED))));
+ .as("No QuotaExceededException thrown for too many DOWNLOADED updateActionStatus updates").isThrownBy(
+ () -> IntStream.range(0, maxMessages).forEach(i -> controllerManagement.addUpdateActionStatus(
+ entityFactory.actionStatus().create(actionId).status(Status.DOWNLOADED))));
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.as("No QuotaExceededException thrown for too many ERROR updateActionStatus updates")
- .isThrownBy(()->IntStream.range(0, maxMessages).forEach(i -> controllerManagement
+ .isThrownBy(() -> IntStream.range(0, maxMessages).forEach(i -> controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.ERROR))));
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.as("No QuotaExceededException thrown for too many FINISHED updateActionStatus updates")
- .isThrownBy(()->IntStream.range(0, maxMessages).forEach(i -> controllerManagement
+ .isThrownBy(() -> IntStream.range(0, maxMessages).forEach(i -> controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.FINISHED))));
}
@@ -1472,6 +1468,39 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertActionStatus(actionId, controllerId, TargetUpdateStatus.IN_SYNC, actionStatus, actionStatus, false);
}
+ @Test
+ @Description("Actions are exposed according to thier weight in multi assignment mode.")
+ public void actionsAreExposedAccordingToTheirWeight() {
+ final String targetId = testdataFactory.createTarget().getControllerId();
+ final DistributionSet ds = testdataFactory.createDistributionSet();
+ final Long actionWeightNull = assignDistributionSet(ds.getId(), targetId).getAssignedEntity().get(0).getId();
+ enableMultiAssignments();
+ final Long actionWeight500old = assignDistributionSet(ds.getId(), targetId, 500).getAssignedEntity().get(0)
+ .getId();
+ final Long actionWeight500new = assignDistributionSet(ds.getId(), targetId, 500).getAssignedEntity().get(0)
+ .getId();
+ final Long actionWeight1000 = assignDistributionSet(ds.getId(), targetId, 1000).getAssignedEntity().get(0)
+ .getId();
+
+ assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId).get().getId())
+ .isEqualTo(actionWeightNull);
+ controllerManagement
+ .addUpdateActionStatus(entityFactory.actionStatus().create(actionWeightNull).status(Status.FINISHED));
+ assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId).get().getId())
+ .isEqualTo(actionWeight1000);
+ controllerManagement
+ .addUpdateActionStatus(entityFactory.actionStatus().create(actionWeight1000).status(Status.FINISHED));
+ assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId).get().getId())
+ .isEqualTo(actionWeight500old);
+ controllerManagement
+ .addUpdateActionStatus(entityFactory.actionStatus().create(actionWeight500old).status(Status.FINISHED));
+ assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId).get().getId())
+ .isEqualTo(actionWeight500new);
+ controllerManagement
+ .addUpdateActionStatus(entityFactory.actionStatus().create(actionWeight500new).status(Status.FINISHED));
+ assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId)).isEmpty();
+ }
+
private void assertAssignedDistributionSetId(final String controllerId, final Long dsId) {
final Optional target = controllerManagement.getByControllerId(controllerId);
assertThat(target).isPresent();
@@ -1494,12 +1523,10 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(actionRepository.activeActionExistsForControllerId(controllerId)).isEqualTo(false);
}
-
@Test
@Description("Delete a target on requested target deletion from client side")
- @ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
- @Expect(type = TargetPollEvent.class, count = 1),
- @Expect(type = TargetDeletedEvent.class, count = 1)})
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
+ @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TargetDeletedEvent.class, count = 1) })
public void deleteTargetWithValidThingId() {
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST);
assertThat(target).as("target should not be null").isNotNull();
@@ -1512,7 +1539,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Delete a target with a non existing thingId")
- @ExpectEvents({@Expect(type = TargetDeletedEvent.class, count = 0)})
+ @ExpectEvents({ @Expect(type = TargetDeletedEvent.class, count = 0) })
public void deleteTargetWithInvalidThingId() {
assertThatExceptionOfType(EntityNotFoundException.class)
.as("No EntityNotFoundException thrown when deleting a non-existing target")
@@ -1522,9 +1549,8 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Delete a target after it has been deleted already")
- @ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
- @Expect(type = TargetPollEvent.class, count = 1),
- @Expect(type = TargetDeletedEvent.class, count = 1)})
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
+ @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TargetDeletedEvent.class, count = 1) })
public void deleteTargetAfterItWasDeleted() {
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST);
assertThat(target).as("target should not be null").isNotNull();
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java
index 4de4ea3e0..3bf0e5446 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java
@@ -22,6 +22,8 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;
+import javax.validation.ConstraintViolationException;
+
import org.assertj.core.api.Assertions;
import org.eclipse.hawkbit.repository.ActionStatusFields;
import org.eclipse.hawkbit.repository.DeploymentManagement;
@@ -171,7 +173,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
- .isThrownBy(() -> assignDistributionSet(ds1, Collections.singletonList(testTarget)));
+ .isThrownBy(() -> assignDistributionSet(ds1.getId(), testTarget.getControllerId(), 77));
}
@Test
@@ -563,16 +565,17 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
public void previousAssignmentsAreNotCanceledInMultiAssignMode() {
enableMultiAssignments();
final List targets = testdataFactory.createTargets(10);
+ final List targetIds = targets.stream().map(Target::getControllerId).collect(Collectors.toList());
// First assignment
final DistributionSet ds1 = testdataFactory.createDistributionSet("Multi-assign-1");
- assignDistributionSet(ds1, targets);
+ assignDistributionSet(ds1.getId(), targetIds, 77);
assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, Status.RUNNING);
// Second assignment
final DistributionSet ds2 = testdataFactory.createDistributionSet("Multi-assign-2");
- assignDistributionSet(ds2, targets);
+ assignDistributionSet(ds2.getId(), targetIds, 45);
assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), STATE_ACTIVE, Status.RUNNING);
assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, Status.RUNNING);
@@ -601,7 +604,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
public void multiassignmentInOneRequest() {
final List targets = testdataFactory.createTargets(2);
final List distributionSets = testdataFactory.createDistributionSets(2);
- final List deploymentRequests = createAssignmentRequests(distributionSets, targets);
+ final List deploymentRequests = createAssignmentRequests(distributionSets, targets, 34);
enableMultiAssignments();
final List results = deploymentManagement
@@ -614,7 +617,14 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
.stream().map(action -> action.getDistributionSet().getId()).collect(Collectors.toList());
assertThat(assignedDsIds).containsExactlyInAnyOrderElementsOf(dsIds);
});
+ }
+ protected List createAssignmentRequests(final Collection distributionSets,
+ final Collection targets, final int weight) {
+ final List deploymentRequests = new ArrayList<>();
+ distributionSets.forEach(ds -> targets.forEach(target -> deploymentRequests.add(DeploymentManagement
+ .deploymentRequest(target.getControllerId(), ds.getId()).setWeight(weight).build())));
+ return deploymentRequests;
}
@Test
@@ -624,9 +634,10 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
final List distributionSets = testdataFactory.createDistributionSets(2);
final DeploymentRequest targetToDS0 = DeploymentManagement
- .deploymentRequest(target.getControllerId(), distributionSets.get(0).getId()).build();
+ .deploymentRequest(target.getControllerId(), distributionSets.get(0).getId()).setWeight(78).build();
+
final DeploymentRequest targetToDS1 = DeploymentManagement
- .deploymentRequest(target.getControllerId(), distributionSets.get(1).getId()).build();
+ .deploymentRequest(target.getControllerId(), distributionSets.get(1).getId()).setWeight(565).build();
Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class)
.isThrownBy(() -> deploymentManagement.assignDistributionSets(Arrays.asList(targetToDS0, targetToDS1)));
@@ -638,17 +649,26 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Duplicate Assignments are removed from a request when multiassignment is disabled, otherwise not")
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
+ @Expect(type = DistributionSetCreatedEvent.class, count = 1),
+ @Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
+ @Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
+ @Expect(type = ActionCreatedEvent.class, count = 3), @Expect(type = TargetUpdatedEvent.class, count = 2),
+ @Expect(type = MultiActionEvent.class, count = 1) })
public void duplicateAssignmentsInRequestAreOnlyRemovedIfMultiassignmentDisabled() {
- final Target target = testdataFactory.createTarget();
- final DistributionSet ds = testdataFactory.createDistributionSet();
+ final String targetId = testdataFactory.createTarget().getControllerId();
+ final Long dsId = testdataFactory.createDistributionSet().getId();
final List twoEqualAssignments = Collections.nCopies(2,
- DeploymentManagement.deploymentRequest(target.getControllerId(), ds.getId()).build());
+ DeploymentManagement.deploymentRequest(targetId, dsId).build());
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignments)))
.isEqualTo(1);
enableMultiAssignments();
- assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignments)))
+ final List twoEqualAssignmentsWithWeight = Collections.nCopies(2,
+ DeploymentManagement.deploymentRequest(targetId, dsId).setWeight(555).build());
+
+ assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignmentsWithWeight)))
.isEqualTo(2);
}
@@ -668,7 +688,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
final Long dsId = testdataFactory.createDistributionSet().getId();
final List deploymentRequests = Collections.nCopies(maxActions + 1,
- DeploymentManagement.deploymentRequest(controllerId, dsId).build());
+ DeploymentManagement.deploymentRequest(controllerId, dsId).setWeight(24).build());
enableMultiAssignments();
Assertions.assertThatExceptionOfType(AssignmentQuotaExceededException.class)
@@ -676,6 +696,67 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
assertThat(actionRepository.countByTargetControllerId(controllerId)).isEqualTo(0);
}
+ @Test
+ @Description("An assignment request without a weight is ok when multi assignment in enabled.")
+ public void weightNotRequiredInMultiAssignmentMode() {
+ final String targetId = testdataFactory.createTarget().getControllerId();
+ final Long dsId = testdataFactory.createDistributionSet().getId();
+
+ final DeploymentRequest assignWithoutWeight = DeploymentManagement.deploymentRequest(targetId, dsId).build();
+ final DeploymentRequest assignWithWeight = DeploymentManagement.deploymentRequest(targetId, dsId).setWeight(567)
+ .build();
+
+ enableMultiAssignments();
+ deploymentManagement.assignDistributionSets(Arrays.asList(assignWithoutWeight, assignWithWeight));
+ }
+
+ @Test
+ @Description("An assignment request containing a weight causes an error when multi assignment in disabled.")
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
+ @Expect(type = DistributionSetCreatedEvent.class, count = 1),
+ @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
+ public void weightNotAllowedWhenMultiAssignmentModeNotEnabled() {
+ final String targetId = testdataFactory.createTarget().getControllerId();
+ final Long dsId = testdataFactory.createDistributionSet().getId();
+
+ final DeploymentRequest assignWithoutWeight = DeploymentManagement.deploymentRequest(targetId, dsId)
+ .setWeight(456).build();
+
+ Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class).isThrownBy(
+ () -> deploymentManagement.assignDistributionSets(Collections.singletonList(assignWithoutWeight)));
+ }
+
+ @Test
+ @Description("Weights are validated and contained in the resulting Action.")
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
+ @Expect(type = DistributionSetCreatedEvent.class, count = 1),
+ @Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
+ @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2),
+ @Expect(type = MultiActionEvent.class, count = 2) })
+ public void weightValidatedAndSaved() {
+ final String targetId = testdataFactory.createTarget().getControllerId();
+ final Long dsId = testdataFactory.createDistributionSet().getId();
+ final DeploymentRequest valideRequest1 = DeploymentManagement.deploymentRequest(targetId, dsId)
+ .setWeight(Action.WEIGHT_MAX).build();
+ final DeploymentRequest valideRequest2 = DeploymentManagement.deploymentRequest(targetId, dsId)
+ .setWeight(Action.WEIGHT_MIN).build();
+ final DeploymentRequest weightTooLow = DeploymentManagement.deploymentRequest(targetId, dsId)
+ .setWeight(Action.WEIGHT_MIN - 1).build();
+ final DeploymentRequest weightTooHigh = DeploymentManagement.deploymentRequest(targetId, dsId)
+ .setWeight(Action.WEIGHT_MAX + 1).build();
+ enableMultiAssignments();
+ Assertions.assertThatExceptionOfType(ConstraintViolationException.class)
+ .isThrownBy(() -> deploymentManagement.assignDistributionSets(Collections.singletonList(weightTooLow)));
+ Assertions.assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
+ () -> deploymentManagement.assignDistributionSets(Collections.singletonList(weightTooHigh)));
+ final Long valideActionId1 = getFirstAssignedAction(
+ deploymentManagement.assignDistributionSets(Collections.singletonList(valideRequest1)).get(0)).getId();
+ final Long valideActionId2 = getFirstAssignedAction(
+ deploymentManagement.assignDistributionSets(Collections.singletonList(valideRequest2)).get(0)).getId();
+ assertThat(actionRepository.getById(valideActionId1).get().getWeight()).get().isEqualTo(Action.WEIGHT_MAX);
+ assertThat(actionRepository.getById(valideActionId2).get().getWeight()).get().isEqualTo(Action.WEIGHT_MIN);
+ }
+
/**
* test a simple deployment by calling the
* {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)}
@@ -1143,13 +1224,15 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Tests the computation of already assigned entities returned as a result of an assignment")
- public void testAlreadyAssignedAndAssignedActionsInAssignmentResult(){
- // create target1, distributionSet, assign ds to target1 and finish update (close all actions)
+ public void testAlreadyAssignedAndAssignedActionsInAssignmentResult() {
+ // create target1, distributionSet, assign ds to target1 and finish
+ // update (close all actions)
final Action action = prepareFinishedUpdate("target1", "ds", false);
final Target target2 = testdataFactory.createTarget("target2");
final Target target3 = testdataFactory.createTarget("target3");
- // assign ds to target2, but don't finish update (actions should be still open)
+ // assign ds to target2, but don't finish update (actions should be
+ // still open)
assignDistributionSet(action.getDistributionSet().getId(), target2.getControllerId());
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(
@@ -1283,7 +1366,5 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
public List getUndeployedTargetIDs() {
return undeployedTargetIDs;
}
-
}
-
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java
index b4376001f..02eea953a 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java
@@ -17,12 +17,15 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
+import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
+import org.assertj.core.api.Assertions;
import org.assertj.core.api.Condition;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
@@ -42,6 +45,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
+import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
@@ -126,7 +130,6 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
final Action rolloutCreatedAction = actionsByKnownTarget.stream()
.filter(action -> !action.getId().equals(manuallyAssignedActionId)).findAny().get();
assertThat(rolloutCreatedAction.getStatus()).isEqualTo(Status.FINISHED);
-
}
@Test
@@ -599,7 +602,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
final String successCondition = "50";
final String errorCondition = "80";
final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout,
- amountOtherTargets, amountGroups, successCondition, errorCondition, ActionType.DOWNLOAD_ONLY);
+ amountOtherTargets, amountGroups, successCondition, errorCondition, ActionType.DOWNLOAD_ONLY, null);
// targets have not started
Map validationMap = createInitStatusMap();
@@ -619,7 +622,8 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
changeStatusForAllRunningActions(createdRollout, Status.DOWNLOADED);
rolloutManagement.handleRollouts();
- // 4 targets are ready, 2 are finished(with DOWNLOADED action status) and 2 are running
+ // 4 targets are ready, 2 are finished(with DOWNLOADED action status)
+ // and 2 are running
validationMap = createInitStatusMap();
validationMap.put(TotalTargetCountStatus.Status.SCHEDULED, 4L);
validationMap.put(TotalTargetCountStatus.Status.FINISHED, 2L);
@@ -628,7 +632,8 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
changeStatusForAllRunningActions(createdRollout, Status.DOWNLOADED);
rolloutManagement.handleRollouts();
- // 2 targets are ready, 4 are finished(with DOWNLOADED action status) and 2 are running
+ // 2 targets are ready, 4 are finished(with DOWNLOADED action status)
+ // and 2 are running
validationMap = createInitStatusMap();
validationMap.put(TotalTargetCountStatus.Status.SCHEDULED, 2L);
validationMap.put(TotalTargetCountStatus.Status.FINISHED, 4L);
@@ -637,7 +642,8 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
changeStatusForAllRunningActions(createdRollout, Status.DOWNLOADED);
rolloutManagement.handleRollouts();
- // 0 targets are ready, 6 are finished(with DOWNLOADED action status) and 2 are running
+ // 0 targets are ready, 6 are finished(with DOWNLOADED action status)
+ // and 2 are running
validationMap = createInitStatusMap();
validationMap.put(TotalTargetCountStatus.Status.FINISHED, 6L);
validationMap.put(TotalTargetCountStatus.Status.RUNNING, 2L);
@@ -645,7 +651,8 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
changeStatusForAllRunningActions(createdRollout, Status.FINISHED);
rolloutManagement.handleRollouts();
- // 0 targets are ready, 6 are finished(with DOWNLOADED action status), 2 are finished and 0 are running
+ // 0 targets are ready, 6 are finished(with DOWNLOADED action status), 2
+ // are finished and 0 are running
validationMap = createInitStatusMap();
validationMap.put(TotalTargetCountStatus.Status.FINISHED, 8L);
validateRolloutActionStatus(createdRollout.getId(), validationMap);
@@ -1745,14 +1752,80 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
// verify that all running actions keep running
assertThat(actionRepository.findByRolloutIdAndStatus(PAGE, deletedRollout.getId(), Status.RUNNING)
.getNumberOfElements()).isEqualTo(2);
+ }
+ @Test
+ @Description("Creating a rollout without weight value when multi assignment in enabled.")
+ public void weightNotRequiredInMultiAssignmentMode() {
+ enableMultiAssignments();
+ createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 2, "50", "80", ActionType.FORCED, null);
+ }
+
+ @Test
+ @Description("Creating a rollout with a weight causes an error when multi assignment in disabled.")
+ public void weightNotAllowedWhenMultiAssignmentModeNotEnabled() {
+ Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class)
+ .isThrownBy(() -> createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 2, "50", "80",
+ ActionType.FORCED, 66));
+ }
+
+ @Test
+ @Description("Weight is validated and saved to the Rollout.")
+ public void weightValidatedAndSaved() {
+ final String targetPrefix = UUID.randomUUID().toString();
+ testdataFactory.createTargets(4, targetPrefix);
+ enableMultiAssignments();
+
+ Assertions.assertThatExceptionOfType(ConstraintViolationException.class)
+ .isThrownBy(() -> createTestRolloutWithTargetsAndDistributionSet(4, 2, "50", "80",
+ UUID.randomUUID().toString(), UUID.randomUUID().toString(), Action.WEIGHT_MAX + 1));
+ Assertions.assertThatExceptionOfType(ConstraintViolationException.class)
+ .isThrownBy(() -> createTestRolloutWithTargetsAndDistributionSet(4, 2, "50", "80",
+ UUID.randomUUID().toString(), UUID.randomUUID().toString(), Action.WEIGHT_MIN - 1));
+ final Rollout createdRollout1 = createTestRolloutWithTargetsAndDistributionSet(4, 2, "50", "80",
+ UUID.randomUUID().toString(), UUID.randomUUID().toString(), Action.WEIGHT_MAX);
+ final Rollout createdRollout2 = createTestRolloutWithTargetsAndDistributionSet(4, 2, "50", "80",
+ UUID.randomUUID().toString(), UUID.randomUUID().toString(), Action.WEIGHT_MIN);
+ assertThat(rolloutRepository.findById(createdRollout1.getId()).get().getWeight()).get()
+ .isEqualTo(Action.WEIGHT_MAX);
+ assertThat(rolloutRepository.findById(createdRollout2.getId()).get().getWeight()).get()
+ .isEqualTo(Action.WEIGHT_MIN);
+ }
+
+ @Test
+ @Description("A Rollout with weight creats actions with weights")
+ public void actionsWithWeightAreCreated() {
+ final int amountOfTargets = 5;
+ final int weight = 99;
+ enableMultiAssignments();
+ final Long rolloutId = createSimpleTestRolloutWithTargetsAndDistributionSet(amountOfTargets, 2, amountOfTargets,
+ "80", "50", null, weight).getId();
+ rolloutManagement.start(rolloutId);
+ rolloutManagement.handleRollouts();
+ final List actions = deploymentManagement.findActionsAll(PAGE).getContent();
+ assertThat(actions).hasSize(amountOfTargets);
+ assertThat(actions).allMatch(action -> action.getWeight().get() == weight);
+ }
+
+ @Test
+ @Description("Rollout can be created without weight in single assignment and be started in multi assignment")
+ public void createInSingleStartInMultiassigMode() {
+ final int amountOfTargets = 5;
+ final Long rolloutId = createSimpleTestRolloutWithTargetsAndDistributionSet(amountOfTargets, 2, amountOfTargets,
+ "80", "50", null, null).getId();
+
+ enableMultiAssignments();
+ rolloutManagement.start(rolloutId);
+ rolloutManagement.handleRollouts();
+ final List actions = deploymentManagement.findActionsAll(PAGE).getContent();
+ assertThat(actions).hasSize(amountOfTargets);
+ assertThat(actions).allMatch(action -> !action.getWeight().isPresent());
}
private RolloutGroupCreate generateRolloutGroup(final int index, final Integer percentage,
final String targetFilter) {
return entityFactory.rolloutGroup().create().name("Group" + index).description("Group" + index + "desc")
.targetPercentage(Float.valueOf(percentage)).targetFilterQuery(targetFilter);
-
}
private RolloutCreate generateTargetsAndRollout(final String rolloutName, final int amountTargetsForRollout) {
@@ -1790,27 +1863,35 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
final int amountOtherTargets, final int groupSize, final String successCondition,
final String errorCondition) {
return createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, amountOtherTargets,
- groupSize, successCondition, errorCondition, ActionType.FORCED);
+ groupSize, successCondition, errorCondition, ActionType.FORCED, null);
}
private Rollout createSimpleTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout,
final int amountOtherTargets, final int groupSize, final String successCondition,
- final String errorCondition, final ActionType actionType) {
+ final String errorCondition, final ActionType actionType, final Integer weight) {
final DistributionSet rolloutDS = testdataFactory.createDistributionSet("rolloutDS");
testdataFactory.createTargets(amountTargetsForRollout, "rollout-", "rollout");
testdataFactory.createTargets(amountOtherTargets, "others-", "rollout");
final String filterQuery = "controllerId==rollout-*";
return testdataFactory.createRolloutByVariables("test-rollout-name-1", "test-rollout-description-1", groupSize,
- filterQuery, rolloutDS, successCondition, errorCondition, actionType);
+ filterQuery, rolloutDS, successCondition, errorCondition, actionType, weight);
}
private Rollout createTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout,
final int groupSize, final String successCondition, final String errorCondition, final String rolloutName,
final String targetPrefixName) {
+ return createTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, groupSize, successCondition,
+ errorCondition, rolloutName, targetPrefixName, null);
+ }
+
+ private Rollout createTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout,
+ final int groupSize, final String successCondition, final String errorCondition, final String rolloutName,
+ final String targetPrefixName, final Integer weight) {
final DistributionSet dsForRolloutTwo = testdataFactory.createDistributionSet("dsFor" + rolloutName);
testdataFactory.createTargets(amountTargetsForRollout, targetPrefixName + "-", targetPrefixName);
return testdataFactory.createRolloutByVariables(rolloutName, rolloutName + "description", groupSize,
- "controllerId==" + targetPrefixName + "-*", dsForRolloutTwo, successCondition, errorCondition);
+ "controllerId==" + targetPrefixName + "-*", dsForRolloutTwo, successCondition, errorCondition,
+ Action.ActionType.FORCED, weight);
}
private int changeStatusForAllRunningActions(final Rollout rollout, final Status status) {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagementTest.java
index f6c91ff7b..7c976b512 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagementTest.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagementTest.java
@@ -20,6 +20,9 @@ import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.List;
+import javax.validation.ConstraintViolationException;
+
+import org.assertj.core.api.Assertions;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
@@ -28,8 +31,10 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryCreat
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
+import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
+import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
@@ -82,13 +87,18 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
verifyThrownExceptionBy(
() -> targetFilterQueryManagement.update(entityFactory.targetFilterQuery().update(NOT_EXIST_IDL)),
"TargetFilterQuery");
- verifyThrownExceptionBy(
- () -> targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), NOT_EXIST_IDL),
+
+ verifyThrownExceptionBy(() -> targetFilterQueryManagement.updateAutoAssignDS(
+ entityFactory.targetFilterQuery().updateAutoAssign(targetFilterQuery.getId()).ds(NOT_EXIST_IDL)),
"DistributionSet");
- verifyThrownExceptionBy(() -> targetFilterQueryManagement.updateAutoAssignDS(NOT_EXIST_IDL, set.getId()),
- "TargetFilterQuery");
+
verifyThrownExceptionBy(
- () -> targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), NOT_EXIST_IDL),
+ () -> targetFilterQueryManagement.updateAutoAssignDS(
+ entityFactory.targetFilterQuery().updateAutoAssign(NOT_EXIST_IDL).ds(set.getId())),
+ "TargetFilterQuery");
+
+ verifyThrownExceptionBy(() -> targetFilterQueryManagement.updateAutoAssignDS(
+ entityFactory.targetFilterQuery().updateAutoAssign(targetFilterQuery.getId()).ds(NOT_EXIST_IDL)),
"DistributionSet");
}
@@ -208,23 +218,26 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
@Step
private void verifyAutoAssignmentWithDefaultActionType(final String filterName,
final TargetFilterQuery targetFilterQuery, final DistributionSet distributionSet) {
- targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), distributionSet.getId());
+ targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
+ .updateAutoAssign(targetFilterQuery.getId()).ds(distributionSet.getId()));
verifyAutoAssignDsAndActionType(filterName, distributionSet, ActionType.FORCED);
}
@Step
private void verifyAutoAssignmentWithSoftActionType(final String filterName,
final TargetFilterQuery targetFilterQuery, final DistributionSet distributionSet) {
- targetFilterQueryManagement.updateAutoAssignDSWithActionType(targetFilterQuery.getId(), distributionSet.getId(),
- ActionType.SOFT);
+ targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
+ .updateAutoAssign(targetFilterQuery.getId()).ds(distributionSet.getId()).actionType(ActionType.SOFT));
verifyAutoAssignDsAndActionType(filterName, distributionSet, ActionType.SOFT);
}
@Step
private void verifyAutoAssignmentWithDownloadOnlyActionType(final String filterName,
final TargetFilterQuery targetFilterQuery, final DistributionSet distributionSet) {
- targetFilterQueryManagement.updateAutoAssignDSWithActionType(targetFilterQuery.getId(), distributionSet.getId(),
- ActionType.DOWNLOAD_ONLY);
+ targetFilterQueryManagement
+ .updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(targetFilterQuery.getId())
+ .ds(distributionSet.getId()).actionType(ActionType.DOWNLOAD_ONLY));
+
verifyAutoAssignDsAndActionType(filterName, distributionSet, ActionType.DOWNLOAD_ONLY);
}
@@ -233,9 +246,11 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
final DistributionSet distributionSet) {
// assigning a distribution set with TIMEFORCED action is supposed to
// fail as only FORCED and SOFT action types are allowed
- assertThatExceptionOfType(InvalidAutoAssignActionTypeException.class).isThrownBy(
- () -> targetFilterQueryManagement.updateAutoAssignDSWithActionType(targetFilterQuery.getId(),
- distributionSet.getId(), ActionType.TIMEFORCED));
+
+ assertThatExceptionOfType(InvalidAutoAssignActionTypeException.class)
+ .isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(
+ entityFactory.targetFilterQuery().updateAutoAssign(targetFilterQuery.getId())
+ .ds(distributionSet.getId()).actionType(ActionType.TIMEFORCED)));
}
@Step
@@ -245,8 +260,8 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
.type(testdataFactory.findOrCreateDefaultTestDsType()));
assertThatExceptionOfType(InvalidAutoAssignDistributionSetException.class)
- .isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(),
- incompleteDistributionSet.getId()));
+ .isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
+ .updateAutoAssign(targetFilterQuery.getId()).ds(incompleteDistributionSet.getId())));
}
@Step
@@ -255,8 +270,9 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
assignDistributionSet(softDeletedDs, testdataFactory.createTarget("forSoftDeletedDs"));
distributionSetManagement.delete(softDeletedDs.getId());
- assertThatExceptionOfType(InvalidAutoAssignDistributionSetException.class).isThrownBy(
- () -> targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), softDeletedDs.getId()));
+ assertThatExceptionOfType(InvalidAutoAssignDistributionSetException.class)
+ .isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
+ .updateAutoAssign(targetFilterQuery.getId()).ds(softDeletedDs.getId())));
}
private void verifyAutoAssignDsAndActionType(final String filterName, final DistributionSet distributionSet,
@@ -282,8 +298,10 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
// assigning a distribution set is supposed to fail as the query
// addresses too many targets
- assertThatExceptionOfType(AssignmentQuotaExceededException.class).isThrownBy(() -> targetFilterQueryManagement
- .updateAutoAssignDS(targetFilterQuery.getId(), distributionSet.getId()));
+
+ assertThatExceptionOfType(AssignmentQuotaExceededException.class)
+ .isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
+ .updateAutoAssign(targetFilterQuery.getId()).ds(distributionSet.getId())));
}
@Test
@@ -313,7 +331,8 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
- targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), distributionSet.getId());
+ targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
+ .updateAutoAssign(targetFilterQuery.getId()).ds(distributionSet.getId()));
// Check if target filter query is there
TargetFilterQuery tfq = targetFilterQueryManagement.getByName(filterName).get();
@@ -340,9 +359,11 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
// later step
assignDistributionSet(distributionSet.getId(), target.getControllerId());
- targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement
+ final Long filterId = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name(filterName).query("name==PendingTargets001"))
- .getId(), distributionSet.getId());
+ .getId();
+ targetFilterQueryManagement.updateAutoAssignDS(
+ entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(distributionSet.getId()));
// Check if target filter query is there with the distribution set
TargetFilterQuery tfq = targetFilterQueryManagement.getByName(filterName).get();
@@ -372,20 +393,18 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
final DistributionSet distributionSet2 = testdataFactory.createDistributionSet("2");
- final TargetFilterQuery tfq = targetFilterQueryManagement.updateAutoAssignDSWithActionType(
- targetFilterQueryManagement
- .create(entityFactory.targetFilterQuery().create().name("c").query("name==x")).getId(),
- distributionSet.getId(), ActionType.SOFT);
- final TargetFilterQuery tfq2 = targetFilterQueryManagement.updateAutoAssignDS(
- targetFilterQueryManagement
- .create(entityFactory.targetFilterQuery().create().name(filterName).query("name==z*")).getId(),
- distributionSet2.getId());
+ final TargetFilterQuery tfq = targetFilterQueryManagement
+ .create(entityFactory.targetFilterQuery().create().name("c").query("name==x")
+ .autoAssignDistributionSet(distributionSet).autoAssignActionType(ActionType.SOFT));
+ final TargetFilterQuery tfq2 = targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create()
+ .name(filterName).query("name==z*").autoAssignDistributionSet(distributionSet2));
assertEquals(4L, targetFilterQueryManagement.count());
// check if find works
verifyFindByDistributionSetAndRsql(distributionSet, null, tfq);
- targetFilterQueryManagement.updateAutoAssignDS(tfq2.getId(), distributionSet.getId());
+ targetFilterQueryManagement.updateAutoAssignDS(
+ entityFactory.targetFilterQuery().updateAutoAssign(tfq2.getId()).ds(distributionSet.getId()));
// check if find works for two
verifyFindByDistributionSetAndRsql(distributionSet, null, tfq, tfq2);
@@ -421,4 +440,73 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
verifyExpectedFilterQueriesInList(tfqList, expectedFilterQueries);
}
+
+ @Test
+ @Description("Creating or updating a target filter query with autoassignment and no-value weight when multi assignment in enabled.")
+ public void weightNotRequiredInMultiAssignmentMode() {
+ enableMultiAssignments();
+ final DistributionSet ds = testdataFactory.createDistributionSet();
+ final Long filterId = targetFilterQueryManagement
+ .create(entityFactory.targetFilterQuery().create().name("a").query("name==*")).getId();
+
+ targetFilterQueryManagement.create(
+ entityFactory.targetFilterQuery().create().name("b").query("name==*").autoAssignDistributionSet(ds));
+ targetFilterQueryManagement
+ .updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(ds.getId()));
+ }
+
+ @Test
+ @Description("Creating or updating a target filter query with autoassignment with a weight causes an error when multi assignment in disabled.")
+ public void weightNotAllowedWhenMultiAssignmentModeNotEnabled() {
+ final DistributionSet ds = testdataFactory.createDistributionSet();
+ final Long filterId = targetFilterQueryManagement
+ .create(entityFactory.targetFilterQuery().create().name("a").query("name==*")).getId();
+
+ Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class)
+ .isThrownBy(() -> targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create()
+ .name("b").query("name==*").autoAssignDistributionSet(ds).autoAssignWeight(342)));
+ Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class)
+ .isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(
+ entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(ds.getId()).weight(343)));
+ }
+
+ @Test
+ @Description("Auto assignment can be removed from filter when multi assignment in enabled.")
+ public void removeDsFromFilterWhenMultiAssignmentModeNotEnabled() {
+ enableMultiAssignments();
+ final DistributionSet ds = testdataFactory.createDistributionSet();
+ final Long filterId = targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("a")
+ .query("name==*").autoAssignDistributionSet(ds).autoAssignWeight(23)).getId();
+ targetFilterQueryManagement
+ .updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(null).weight(null));
+ }
+
+ @Test
+ @Description("Weight is validated and saved to the Filter.")
+ public void weightValidatedAndSaved() {
+ enableMultiAssignments();
+ final DistributionSet ds = testdataFactory.createDistributionSet();
+
+ Assertions.assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
+ () -> targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("a")
+ .query("name==*").autoAssignDistributionSet(ds).autoAssignWeight(Action.WEIGHT_MAX + 1)));
+
+ final Long filterId = targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("a")
+ .query("name==*").autoAssignDistributionSet(ds).autoAssignWeight(Action.WEIGHT_MAX)).getId();
+ assertThat(targetFilterQueryManagement.get(filterId).get().getAutoAssignWeight().get())
+ .isEqualTo(Action.WEIGHT_MAX);
+
+ Assertions.assertThatExceptionOfType(ConstraintViolationException.class)
+ .isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
+ .updateAutoAssign(filterId).ds(ds.getId()).weight(Action.WEIGHT_MAX + 1)));
+ Assertions.assertThatExceptionOfType(ConstraintViolationException.class)
+ .isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
+ .updateAutoAssign(filterId).ds(ds.getId()).weight(Action.WEIGHT_MIN - 1)));
+ targetFilterQueryManagement.updateAutoAssignDS(
+ entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(ds.getId()).weight(Action.WEIGHT_MAX));
+ targetFilterQueryManagement.updateAutoAssignDS(
+ entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(ds.getId()).weight(Action.WEIGHT_MIN));
+ assertThat(targetFilterQueryManagement.get(filterId).get().getAutoAssignWeight().get())
+ .isEqualTo(Action.WEIGHT_MIN);
+ }
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java
index 466437030..b64476c61 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java
@@ -131,7 +131,8 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
// try to find several targets with different filter settings
verifyThat1TargetHasNameAndId("targ-A-special", targSpecialName.getControllerId());
verifyThat1TargetHasAttributeValue("%c-attribute%", targAttribute.getControllerId());
- verifyThat1TargetHasAttributeValue("%" + targAttributeId.getControllerId() + "%", targAttributeId.getControllerId());
+ verifyThat1TargetHasAttributeValue("%" + targAttributeId.getControllerId() + "%",
+ targAttributeId.getControllerId());
verifyThatRepositoryContains400Targets();
verifyThat200TargetsHaveTagD(targTagW, concat(targBs, targCs));
verifyThat100TargetsContainsGivenTextAndHaveTagAssigned(targTagY, targTagW, targBs);
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java
index 207763790..9327c15ea 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java
@@ -63,8 +63,8 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
// target filter query that matches all targets
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*"));
- targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), secondDistributionSet.getId());
-
+ targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
+ .updateAutoAssign(targetFilterQuery.getId()).ds(secondDistributionSet.getId()));
// Run the check
autoAssignChecker.check();
@@ -101,7 +101,8 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
// target filter query that matches all targets
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*"));
- targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), setA.getId());
+ targetFilterQueryManagement.updateAutoAssignDS(
+ entityFactory.targetFilterQuery().updateAutoAssign(targetFilterQuery.getId()).ds(setA.getId()));
final String targetDsAIdPref = "targ";
final List targets = testdataFactory.createTargets(100, targetDsAIdPref,
@@ -150,18 +151,16 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
// target filter query that matches first bunch of targets, that should
// fail
- assertThatExceptionOfType(
- InvalidAutoAssignDistributionSetException.class)
- .isThrownBy(
- () -> targetFilterQueryManagement.updateAutoAssignDS(
- targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create()
- .name("filterA").query("id==" + targetDsFIdPref + "*")).getId(),
- setF.getId()));
-
+ assertThatExceptionOfType(InvalidAutoAssignDistributionSetException.class).isThrownBy(() -> {
+ final Long filterId = targetFilterQueryManagement.create(
+ entityFactory.targetFilterQuery().create().name("filterA").query("id==" + targetDsFIdPref + "*"))
+ .getId();
+ targetFilterQueryManagement
+ .updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(setF.getId()));
+ });
// target filter query that matches failed bunch of targets
- targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement.create(
- entityFactory.targetFilterQuery().create().name("filterB").query("id==" + targetDsAIdPref + "*"))
- .getId(), setA.getId());
+ targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filterB")
+ .query("id==" + targetDsAIdPref + "*").autoAssignDistributionSet(setA.getId()));
final List targetsF = testdataFactory.createTargets(10, targetDsFIdPref,
targetDsFIdPref.concat(" description"));
@@ -243,12 +242,10 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
final List targets = testdataFactory.createTargets(targetCount, "target" + prefix,
prefix.concat(" description"));
+ targetFilterQueryManagement.create(
+ entityFactory.targetFilterQuery().create().name("filter" + prefix).query("id==target" + prefix + "*")
+ .autoAssignDistributionSet(distributionSet).autoAssignActionType(actionType));
- targetFilterQueryManagement
- .updateAutoAssignDSWithActionType(
- targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create()
- .name("filter" + prefix).query("id==target" + prefix + "*")).getId(),
- distributionSet.getId(), actionType);
return targets;
}
@@ -262,4 +259,38 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
assertThat(actions).allMatch(action -> action.getActionType().equals(actionType));
}
+ @Test
+ @Description("An auto assignment target filter with weight creats actions with weights")
+ public void actionsWithWeightAreCreated() throws Exception {
+ final int amountOfTargets = 5;
+ final DistributionSet ds = testdataFactory.createDistributionSet();
+ final int weight = 32;
+ enableMultiAssignments();
+
+ targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("a").query("name==*")
+ .autoAssignDistributionSet(ds).autoAssignWeight(weight));
+ testdataFactory.createTargets(amountOfTargets);
+ autoAssignChecker.check();
+
+ final List actions = deploymentManagement.findActionsAll(PAGE).getContent();
+ assertThat(actions).hasSize(amountOfTargets);
+ assertThat(actions).allMatch(action -> action.getWeight().get() == weight);
+ }
+
+ @Test
+ @Description("An auto assignment target filter without weight still works after multi assignment is enabled")
+ public void filterWithoutWeightWorksInMultiAssignmentMode() throws Exception {
+ final int amountOfTargets = 5;
+ final DistributionSet ds = testdataFactory.createDistributionSet();
+ targetFilterQueryManagement.create(
+ entityFactory.targetFilterQuery().create().name("a").query("name==*").autoAssignDistributionSet(ds));
+ enableMultiAssignments();
+
+ testdataFactory.createTargets(amountOfTargets);
+ autoAssignChecker.check();
+
+ final List actions = deploymentManagement.findActionsAll(PAGE).getContent();
+ assertThat(actions).hasSize(amountOfTargets);
+ assertThat(actions).allMatch(action -> !action.getWeight().isPresent());
+ }
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java
index 919534b0e..de3918da4 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java
@@ -46,6 +46,7 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest {
action.setDistributionSet(dsA);
action.setTarget(target);
action.setStatus(Status.RUNNING);
+ action.setWeight(45);
target.addAction(action);
actionRepository.save(action);
@@ -56,6 +57,7 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest {
newAction.setActive(i % 2 == 0);
newAction.setStatus(Status.RUNNING);
newAction.setTarget(target);
+ newAction.setWeight(45);
actionRepository.save(newAction);
target.addAction(newAction);
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLTargetFilterQueryFieldsTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLTargetFilterQueryFieldsTest.java
index 17d9dacdc..1490998ef 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLTargetFilterQueryFieldsTest.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLTargetFilterQueryFieldsTest.java
@@ -41,14 +41,10 @@ public class RSQLTargetFilterQueryFieldsTest extends AbstractJpaIntegrationTest
final DistributionSet ds1 = testdataFactory.createDistributionSet("AutoAssignedDs_1");
final DistributionSet ds2 = testdataFactory.createDistributionSet("AutoAssignedDs_2");
- filter1 = targetFilterQueryManagement.updateAutoAssignDSWithActionType(
- targetFilterQueryManagement
- .create(entityFactory.targetFilterQuery().create().name(filterName1).query("name==*")).getId(),
- ds1.getId(), ActionType.SOFT);
- filter2 = targetFilterQueryManagement.updateAutoAssignDS(
- targetFilterQueryManagement
- .create(entityFactory.targetFilterQuery().create().name(filterName2).query("name==*")).getId(),
- ds2.getId());
+ filter1 = targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name(filterName1)
+ .query("name==*").autoAssignDistributionSet(ds1).autoAssignActionType(ActionType.SOFT));
+ filter2 = targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name(filterName2)
+ .query("name==*").autoAssignDistributionSet(ds2));
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name(filterName3).query("name==*"));
diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java
index 4b02fcc99..e2c74b99d 100644
--- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java
+++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java
@@ -16,9 +16,7 @@ import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.time.ZonedDateTime;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -118,6 +116,8 @@ public abstract class AbstractIntegrationTest {
protected static final URI LOCALHOST = URI.create("http://127.0.0.1");
+ protected static final int DEFAULT_TEST_WEIGHT = 500;
+
/**
* Number of {@link DistributionSetType}s that exist in every test case. One
* generated by using
@@ -250,9 +250,14 @@ public abstract class AbstractIntegrationTest {
protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final List controllerIds,
final ActionType actionType, final long forcedTime) {
+ return assignDistributionSet(dsID, controllerIds, actionType, forcedTime, null);
+ }
+
+ protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final List controllerIds,
+ final ActionType actionType, final long forcedTime, final Integer weight) {
final List deploymentRequests = controllerIds.stream()
.map(id -> DeploymentManagement.deploymentRequest(id, dsID).setActionType(actionType)
- .setForceTime(forcedTime).build())
+ .setForceTime(forcedTime).setWeight(weight).build())
.collect(Collectors.toList());
final List results = deploymentManagement
.assignDistributionSets(deploymentRequests);
@@ -266,7 +271,13 @@ public abstract class AbstractIntegrationTest {
return assignDistributionSet(ds.getId(), targetIds, ActionType.FORCED);
}
- private DistributionSetAssignmentResult makeAssignment(final DeploymentRequest request) {
+ protected DistributionSetAssignmentResult assignDistributionSet(final Long dsId, final List targetIds,
+ final int weight) {
+ return assignDistributionSet(dsId, targetIds, ActionType.FORCED, RepositoryModelConstants.NO_FORCE_TIME,
+ weight);
+ }
+
+ protected DistributionSetAssignmentResult makeAssignment(final DeploymentRequest request) {
final List results = deploymentManagement
.assignDistributionSets(Collections.singletonList(request));
assertThat(results).hasSize(1);
@@ -302,35 +313,21 @@ public abstract class AbstractIntegrationTest {
protected DistributionSetAssignmentResult assignDistributionSetWithMaintenanceWindow(final long dsID,
final String controllerId, final String maintenanceWindowSchedule, final String maintenanceWindowDuration,
final String maintenanceWindowTimeZone) {
-
+
return makeAssignment(DeploymentManagement.deploymentRequest(controllerId, dsID)
.setMaintenance(maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone)
.build());
}
- protected DistributionSetAssignmentResult assignDistributionSetWithMaintenanceWindow(final long dsID,
- final String controllerId, final ActionType type, final String maintenanceWindowSchedule,
- final String maintenanceWindowDuration,
- final String maintenanceWindowTimeZone) {
- return makeAssignment(DeploymentManagement.deploymentRequest(controllerId, dsID).setActionType(type)
- .setMaintenance(maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone)
- .build());
- }
-
- protected List createAssignmentRequests(final Collection distributionSets,
- final Collection targets) {
- final List deploymentRequests = new ArrayList<>();
- distributionSets.forEach(ds -> targets.forEach(
- target -> deploymentRequests
- .add(DeploymentManagement.deploymentRequest(target.getControllerId(), ds.getId()).build()))
- );
- return deploymentRequests;
- }
-
protected DistributionSetAssignmentResult assignDistributionSet(final DistributionSet pset, final Target target) {
return assignDistributionSet(pset, Arrays.asList(target));
}
+ protected DistributionSetAssignmentResult assignDistributionSet(final long dsId, final String targetId,
+ final int weight) {
+ return assignDistributionSet(dsId, Collections.singletonList(targetId), weight);
+ }
+
protected void enableMultiAssignments() {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, true);
}
@@ -477,12 +474,14 @@ public abstract class AbstractIntegrationTest {
return randomStringBuilder.toString();
}
- protected static Action getFirstAssignedAction(final DistributionSetAssignmentResult distributionSetAssignmentResult) {
+ protected static Action getFirstAssignedAction(
+ final DistributionSetAssignmentResult distributionSetAssignmentResult) {
return distributionSetAssignmentResult.getAssignedEntity().stream().findFirst()
.orElseThrow(() -> new IllegalStateException("expected one assigned action, found none"));
}
- protected static Long getFirstAssignedActionId(final DistributionSetAssignmentResult distributionSetAssignmentResult) {
+ protected static Long getFirstAssignedActionId(
+ final DistributionSetAssignmentResult distributionSetAssignmentResult) {
return getFirstAssignedAction(distributionSetAssignmentResult).getId();
}
diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java
index 5d1a38a46..389f3f358 100644
--- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java
+++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java
@@ -1,6 +1,6 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
- *
+ *
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -19,6 +19,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -117,8 +118,8 @@ public class TestdataFactory {
public static final String SM_TYPE_RT = "runtime";
/**
- * Key of test "application" {@link SoftwareModuleType} : optional software in
- * {@link #DS_TYPE_DEFAULT}.
+ * Key of test "application" {@link SoftwareModuleType} : optional software
+ * in {@link #DS_TYPE_DEFAULT}.
*/
public static final String SM_TYPE_APP = "application";
@@ -160,13 +161,14 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
- * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
- * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
+ * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
+ * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} false.
- *
+ *
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
+ *
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final String prefix) {
@@ -175,24 +177,25 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
- * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
- * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
+ * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
+ * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} false.
- *
+ *
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet() {
- return createDistributionSet("", DEFAULT_VERSION, false);
+ return createDistributionSet(UUID.randomUUID().toString(), DEFAULT_VERSION, false);
}
/**
* Creates {@link DistributionSet} in repository including three
- * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
- * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
+ * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
+ * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} false.
- *
+ *
* @param modules
* of {@link DistributionSet#getModules()}
+ *
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final Collection modules) {
@@ -201,15 +204,16 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
- * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
- * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
+ * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
+ * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} false.
- *
+ *
* @param modules
* of {@link DistributionSet#getModules()}
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
+ *
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final Collection modules, final String prefix) {
@@ -218,14 +222,15 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
- * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
- * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION}.
- *
+ * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
+ * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION}.
+ *
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
* @param isRequiredMigrationStep
* for {@link DistributionSet#isRequiredMigrationStep()}
+ *
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final String prefix, final boolean isRequiredMigrationStep) {
@@ -234,15 +239,16 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
- * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
- * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
+ * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
+ * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} false.
- *
+ *
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
* @param tags
* {@link DistributionSet#getTags()}
+ *
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final String prefix, final Collection tags) {
@@ -251,17 +257,19 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
- * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
- * {@link #SM_TYPE_APP}.
- *
+ * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
+ * , {@link #SM_TYPE_APP}.
+ *
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
* @param version
* {@link DistributionSet#getVersion()} and
- * {@link SoftwareModule#getVersion()} extended by a random number.
+ * {@link SoftwareModule#getVersion()} extended by a random
+ * number.
* @param isRequiredMigrationStep
* for {@link DistributionSet#isRequiredMigrationStep()}
+ *
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final String prefix, final String version,
@@ -295,7 +303,7 @@ public class TestdataFactory {
* {@link SoftwareModuleMetadata#isTargetVisible()} and
* {@link #INVISIBLE_SM_MD_KEY}, {@link #INVISIBLE_SM_MD_VALUE} without
* {@link SoftwareModuleMetadata#isTargetVisible()}
- *
+ *
* @param set
* to add metadata to
*/
@@ -313,17 +321,19 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository.
- *
+ *
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
* @param version
* {@link DistributionSet#getVersion()} and
- * {@link SoftwareModule#getVersion()} extended by a random number.
+ * {@link SoftwareModule#getVersion()} extended by a random
+ * number.
* @param isRequiredMigrationStep
* for {@link DistributionSet#isRequiredMigrationStep()}
* @param modules
* for {@link DistributionSet#getModules()}
+ *
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final String prefix, final String version,
@@ -338,9 +348,9 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
- * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
- * {@link #SM_TYPE_APP}.
- *
+ * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
+ * , {@link #SM_TYPE_APP}.
+ *
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
@@ -350,6 +360,7 @@ public class TestdataFactory {
* number.updat
* @param tags
* {@link DistributionSet#getTags()}
+ *
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final String prefix, final String version,
@@ -365,13 +376,14 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet}s in repository including three
- * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
- * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an iterative
- * number and {@link DistributionSet#isRequiredMigrationStep()}
+ * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
+ * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an
+ * iterative number and {@link DistributionSet#isRequiredMigrationStep()}
* false.
- *
+ *
* @param number
* of {@link DistributionSet}s to create
+ *
* @return {@link List} of {@link DistributionSet} entities
*/
public List createDistributionSets(final int number) {
@@ -380,8 +392,9 @@ public class TestdataFactory {
}
/**
- * Create a list of {@link DistributionSet}s without modules, i.e. incomplete.
- *
+ * Create a list of {@link DistributionSet}s without modules, i.e.
+ * incomplete.
+ *
* @param number
* of {@link DistributionSet}s to create
* @return {@link List} of {@link DistributionSet} entities
@@ -400,16 +413,17 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet}s in repository including three
- * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
- * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an iterative
- * number and {@link DistributionSet#isRequiredMigrationStep()}
+ * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
+ * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an
+ * iterative number and {@link DistributionSet#isRequiredMigrationStep()}
* false.
- *
+ *
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
* @param number
* of {@link DistributionSet}s to create
+ *
* @return {@link List} of {@link DistributionSet} entities
*/
public List createDistributionSets(final String prefix, final int number) {
@@ -426,11 +440,12 @@ public class TestdataFactory {
* Creates {@link DistributionSet}s in repository with
* {@link #DEFAULT_DESCRIPTION} and
* {@link DistributionSet#isRequiredMigrationStep()} false.
- *
+ *
* @param name
* {@link DistributionSet#getName()}
* @param version
* {@link DistributionSet#getVersion()}
+ *
* @return {@link DistributionSet} entity
*/
public DistributionSet createDistributionSetWithNoSoftwareModules(final String name, final String version) {
@@ -440,11 +455,12 @@ public class TestdataFactory {
}
/**
- * Creates {@link Artifact}s for given {@link SoftwareModule} with a small text
- * payload.
- *
+ * Creates {@link Artifact}s for given {@link SoftwareModule} with a small
+ * text payload.
+ *
* @param moduleId
* the {@link Artifact}s belong to.
+ *
* @return {@link Artifact} entity.
*/
public List createArtifacts(final Long moduleId) {
@@ -458,15 +474,18 @@ public class TestdataFactory {
}
/**
- * Create an {@link Artifact} for given {@link SoftwareModule} with a small text
- * payload.
- *
+ * Create an {@link Artifact} for given {@link SoftwareModule} with a small
+ * text payload.
+ *
* @param artifactData
* the {@link Artifact} Inputstream
+ *
* @param moduleId
* the {@link Artifact} belongs to
+ *
* @param filename
* that was provided during upload.
+ *
* @return {@link Artifact} entity.
*/
public Artifact createArtifact(final String artifactData, final Long moduleId, final String filename) {
@@ -476,17 +495,21 @@ public class TestdataFactory {
}
/**
- * Create an {@link Artifact} for given {@link SoftwareModule} with a small text
- * payload.
+ * Create an {@link Artifact} for given {@link SoftwareModule} with a small
+ * text payload.
*
* @param artifactData
* the {@link Artifact} Inputstream
+ *
* @param moduleId
* the {@link Artifact} belongs to
+ *
* @param filename
* that was provided during upload.
+ *
* @param fileSize
* the file size
+ *
* @return {@link Artifact} entity.
*/
public Artifact createArtifact(final byte[] artifactData, final Long moduleId, final String filename,
@@ -497,11 +520,12 @@ public class TestdataFactory {
/**
* Creates {@link SoftwareModule} with {@link #DEFAULT_VENDOR} and
- * {@link #DEFAULT_VERSION} and random generated {@link Target#getDescription()}
- * in the repository.
- *
+ * {@link #DEFAULT_VERSION} and random generated
+ * {@link Target#getDescription()} in the repository.
+ *
* @param typeKey
* of the {@link SoftwareModuleType}
+ *
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModule(final String typeKey) {
@@ -509,10 +533,12 @@ public class TestdataFactory {
}
/**
- * Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_APP_KEY}
- * with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random
- * generated {@link Target#getDescription()} in the repository.
- *
+ * Creates {@link SoftwareModule} of type
+ * {@value Constants#SMT_DEFAULT_APP_KEY} with {@link #DEFAULT_VENDOR} and
+ * {@link #DEFAULT_VERSION} and random generated
+ * {@link Target#getDescription()} in the repository.
+ *
+ *
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModuleApp() {
@@ -520,12 +546,15 @@ public class TestdataFactory {
}
/**
- * Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_APP_KEY}
- * with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random
- * generated {@link Target#getDescription()} in the repository.
- *
+ * Creates {@link SoftwareModule} of type
+ * {@value Constants#SMT_DEFAULT_APP_KEY} with {@link #DEFAULT_VENDOR} and
+ * {@link #DEFAULT_VERSION} and random generated
+ * {@link Target#getDescription()} in the repository.
+ *
* @param prefix
* added to name and version
+ *
+ *
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModuleApp(final String prefix) {
@@ -533,10 +562,12 @@ public class TestdataFactory {
}
/**
- * Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_OS_KEY}
- * with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random
- * generated {@link Target#getDescription()} in the repository.
- *
+ * Creates {@link SoftwareModule} of type
+ * {@value Constants#SMT_DEFAULT_OS_KEY} with {@link #DEFAULT_VENDOR} and
+ * {@link #DEFAULT_VERSION} and random generated
+ * {@link Target#getDescription()} in the repository.
+ *
+ *
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModuleOs() {
@@ -544,12 +575,15 @@ public class TestdataFactory {
}
/**
- * Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_OS_KEY}
- * with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random
- * generated {@link Target#getDescription()} in the repository.
- *
+ * Creates {@link SoftwareModule} of type
+ * {@value Constants#SMT_DEFAULT_OS_KEY} with {@link #DEFAULT_VENDOR} and
+ * {@link #DEFAULT_VERSION} and random generated
+ * {@link Target#getDescription()} in the repository.
+ *
* @param prefix
* added to name and version
+ *
+ *
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModuleOs(final String prefix) {
@@ -558,13 +592,14 @@ public class TestdataFactory {
/**
* Creates {@link SoftwareModule} with {@link #DEFAULT_VENDOR} and
- * {@link #DEFAULT_VERSION} and random generated {@link Target#getDescription()}
- * in the repository.
- *
+ * {@link #DEFAULT_VERSION} and random generated
+ * {@link Target#getDescription()} in the repository.
+ *
* @param typeKey
* of the {@link SoftwareModuleType}
* @param prefix
* added to name and version
+ *
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModule(final String typeKey, final String prefix) {
@@ -603,7 +638,7 @@ public class TestdataFactory {
return target;
}
- private void assertTargetProperlyCreated(Target target) {
+ private void assertTargetProperlyCreated(final Target target) {
assertThat(target.getCreatedBy()).isNotNull();
assertThat(target.getCreatedAt()).isNotNull();
assertThat(target.getLastModifiedBy()).isNotNull();
@@ -614,15 +649,16 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet}s in repository including three
- * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
- * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an iterative
- * number and {@link DistributionSet#isRequiredMigrationStep()}
+ * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
+ * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an
+ * iterative number and {@link DistributionSet#isRequiredMigrationStep()}
* false.
*
* In addition it updates the created {@link DistributionSet}s and
- * {@link SoftwareModule}s to ensure that {@link BaseEntity#getLastModifiedAt()}
- * and {@link BaseEntity#getLastModifiedBy()} is filled.
- *
+ * {@link SoftwareModule}s to ensure that
+ * {@link BaseEntity#getLastModifiedAt()} and
+ * {@link BaseEntity#getLastModifiedBy()} is filled.
+ *
* @return persisted {@link DistributionSet}.
*/
public DistributionSet createUpdatedDistributionSet() {
@@ -639,8 +675,8 @@ public class TestdataFactory {
/**
* @return {@link DistributionSetType} with key {@link #DS_TYPE_DEFAULT} and
- * {@link SoftwareModuleType}s {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
- * , {@link #SM_TYPE_APP}.
+ * {@link SoftwareModuleType}s {@link #SM_TYPE_OS},
+ * {@link #SM_TYPE_RT} , {@link #SM_TYPE_APP}.
*/
public DistributionSetType findOrCreateDefaultTestDsType() {
final List mand = new ArrayList<>();
@@ -656,11 +692,12 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSetType} in repository.
- *
+ *
* @param dsTypeKey
* {@link DistributionSetType#getKey()}
* @param dsTypeName
* {@link DistributionSetType#getName()}
+ *
* @return persisted {@link DistributionSetType}
*/
public DistributionSetType findOrCreateDistributionSetType(final String dsTypeKey, final String dsTypeName) {
@@ -672,7 +709,7 @@ public class TestdataFactory {
/**
* Finds {@link DistributionSetType} in repository with given
* {@link DistributionSetType#getKey()} or creates if it does not exist yet.
- *
+ *
* @param dsTypeKey
* {@link DistributionSetType#getKey()}
* @param dsTypeName
@@ -681,6 +718,7 @@ public class TestdataFactory {
* {@link DistributionSetType#getMandatoryModuleTypes()}
* @param optional
* {@link DistributionSetType#getOptionalModuleTypes()}
+ *
* @return persisted {@link DistributionSetType}
*/
public DistributionSetType findOrCreateDistributionSetType(final String dsTypeKey, final String dsTypeName,
@@ -694,11 +732,12 @@ public class TestdataFactory {
/**
* Finds {@link SoftwareModuleType} in repository with given
- * {@link SoftwareModuleType#getKey()} or creates if it does not exist yet with
- * {@link SoftwareModuleType#getMaxAssignments()} = 1.
- *
+ * {@link SoftwareModuleType#getKey()} or creates if it does not exist yet
+ * with {@link SoftwareModuleType#getMaxAssignments()} = 1.
+ *
* @param key
* {@link SoftwareModuleType#getKey()}
+ *
* @return persisted {@link SoftwareModuleType}
*/
public SoftwareModuleType findOrCreateSoftwareModuleType(final String key) {
@@ -708,11 +747,12 @@ public class TestdataFactory {
/**
* Finds {@link SoftwareModuleType} in repository with given
* {@link SoftwareModuleType#getKey()} or creates if it does not exist yet.
- *
+ *
* @param key
* {@link SoftwareModuleType#getKey()}
* @param maxAssignments
* {@link SoftwareModuleType#getMaxAssignments()}
+ *
* @return persisted {@link SoftwareModuleType}
*/
public SoftwareModuleType findOrCreateSoftwareModuleType(final String key, final int maxAssignments) {
@@ -732,6 +772,7 @@ public class TestdataFactory {
* {@link DistributionSet#getType()}
* @param modules
* {@link DistributionSet#getModules()}
+ *
* @return the created {@link DistributionSet}
*/
public DistributionSet createDistributionSet(final String name, final String version,
@@ -754,6 +795,7 @@ public class TestdataFactory {
* {@link DistributionSet#getModules()}
* @param requiredMigrationStep
* {@link DistributionSet#isRequiredMigrationStep()}
+ *
* @return the created {@link DistributionSet}
*/
public DistributionSet generateDistributionSet(final String name, final String version,
@@ -775,6 +817,7 @@ public class TestdataFactory {
* {@link DistributionSet#getType()}
* @param modules
* {@link DistributionSet#getModules()}
+ *
* @return the created {@link DistributionSet}
*/
public DistributionSet generateDistributionSet(final String name, final String version,
@@ -787,6 +830,7 @@ public class TestdataFactory {
*
* @param name
* {@link DistributionSet#getName()}
+ *
* @return the generated {@link DistributionSet}
*/
public DistributionSet generateDistributionSet(final String name) {
@@ -795,11 +839,13 @@ public class TestdataFactory {
}
/**
- * Creates {@link Target}s in repository and with {@link #DEFAULT_CONTROLLER_ID}
- * as prefix for {@link Target#getControllerId()}.
- *
+ * Creates {@link Target}s in repository and with
+ * {@link #DEFAULT_CONTROLLER_ID} as prefix for
+ * {@link Target#getControllerId()}.
+ *
* @param number
* of {@link Target}s to create
+ *
* @return {@link List} of {@link Target} entities
*/
public List createTargets(final int number) {
@@ -815,7 +861,7 @@ public class TestdataFactory {
/**
* Builds {@link Target} objects with given prefix for
* {@link Target#getControllerId()} followed by a number suffix.
- *
+ *
* @param start
* value for the controllerId suffix
* @param numberOfTargets
@@ -835,8 +881,9 @@ public class TestdataFactory {
/**
* Builds {@link Target} objects with given prefix for
- * {@link Target#getControllerId()} followed by a number suffix starting with 0.
- *
+ * {@link Target#getControllerId()} followed by a number suffix starting
+ * with 0.
+ *
* @param numberOfTargets
* of {@link Target}s to generate
* @param controllerIdPrefix
@@ -926,9 +973,10 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSetTag}s in repository.
- *
+ *
* @param number
* of {@link DistributionSetTag}s
+ *
* @return the persisted {@link DistributionSetTag}s
*/
public List createDistributionSetTags(final int number) {
@@ -950,14 +998,16 @@ public class TestdataFactory {
}
/**
- * Append {@link ActionStatus} to all {@link Action}s of given {@link Target}s.
- *
+ * Append {@link ActionStatus} to all {@link Action}s of given
+ * {@link Target}s.
+ *
* @param targets
* to add {@link ActionStatus}
* @param status
* to add
* @param message
* to add
+ *
* @return updated {@link Action}.
*/
public List sendUpdateActionStatusToTargets(final Collection targets, final Status status,
@@ -966,14 +1016,16 @@ public class TestdataFactory {
}
/**
- * Append {@link ActionStatus} to all {@link Action}s of given {@link Target}s.
- *
+ * Append {@link ActionStatus} to all {@link Action}s of given
+ * {@link Target}s.
+ *
* @param targets
* to add {@link ActionStatus}
* @param status
* to add
* @param msgs
* to add
+ *
* @return updated {@link Action}.
*/
public List sendUpdateActionStatusToTargets(final Collection targets, final Status status,
@@ -991,7 +1043,7 @@ public class TestdataFactory {
/**
* Creates rollout based on given parameters.
- *
+ *
* @param rolloutName
* of the {@link Rollout}
* @param rolloutDescription
@@ -1012,7 +1064,7 @@ public class TestdataFactory {
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
final String successCondition, final String errorCondition) {
return createRolloutByVariables(rolloutName, rolloutDescription, groupSize, filterQuery, distributionSet,
- successCondition, errorCondition, Action.ActionType.FORCED);
+ successCondition, errorCondition, Action.ActionType.FORCED, null);
}
/**
@@ -1034,11 +1086,14 @@ public class TestdataFactory {
* to switch to next group
* @param actionType
* the type of the Rollout
+ * @param weight
+ * weight of the Rollout
* @return created {@link Rollout}
*/
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
- final String successCondition, final String errorCondition, final Action.ActionType actionType) {
+ final String successCondition, final String errorCondition, final Action.ActionType actionType,
+ final Integer weight) {
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, successCondition)
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition)
@@ -1046,7 +1101,7 @@ public class TestdataFactory {
final Rollout rollout = rolloutManagement.create(
entityFactory.rollout().create().name(rolloutName).description(rolloutDescription)
- .targetFilterQuery(filterQuery).set(distributionSet).actionType(actionType),
+ .targetFilterQuery(filterQuery).set(distributionSet).actionType(actionType).weight(weight),
groupSize, conditions);
// Run here, because Scheduler is disabled during tests
@@ -1058,10 +1113,10 @@ public class TestdataFactory {
/**
* Create {@link Rollout} with a new {@link DistributionSet} and
* {@link Target}s.
- *
+ *
* @param prefix
- * for rollouts name, description, {@link Target#getControllerId()}
- * filter
+ * for rollouts name, description,
+ * {@link Target#getControllerId()} filter
* @return created {@link Rollout}
*/
public Rollout createRollout(final String prefix) {
@@ -1071,12 +1126,12 @@ public class TestdataFactory {
}
/**
- * Create the soft deleted {@link Rollout} with a new {@link DistributionSet}
- * and {@link Target}s.
- *
+ * Create the soft deleted {@link Rollout} with a new
+ * {@link DistributionSet} and {@link Target}s.
+ *
* @param prefix
- * for rollouts name, description, {@link Target#getControllerId()}
- * filter
+ * for rollouts name, description,
+ * {@link Target#getControllerId()} filter
* @return created {@link Rollout}
*/
public Rollout createSoftDeletedRollout(final String prefix) {
diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java b/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java
index d3c48b5a4..8367bc904 100644
--- a/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java
+++ b/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java
@@ -149,7 +149,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, IpUtil
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
- final Action action = controllerManagement.findOldestActiveActionByTarget(controllerId).orElse(null);
+ final Action action = controllerManagement.findActiveActionWithHighestWeight(controllerId).orElse(null);
checkAndCancelExpiredAction(action);
diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java
index 057c2501f..50666258e 100644
--- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java
+++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java
@@ -105,12 +105,12 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that targets cannot be created e.g. in plug'n play scenarios when tenant does not exists but can be created if the tenant exists.")
- @WithUser(tenantId = "tenantDoesNotExists", allSpPermissions = true, authorities = {CONTROLLER_ROLE,
- SYSTEM_ROLE}, autoCreateTenant = false)
- @ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
+ @WithUser(tenantId = "tenantDoesNotExists", allSpPermissions = true, authorities = { CONTROLLER_ROLE,
+ SYSTEM_ROLE }, autoCreateTenant = false)
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1),
@Expect(type = DistributionSetTypeCreatedEvent.class, count = 3),
- @Expect(type = SoftwareModuleTypeCreatedEvent.class, count = 2)})
+ @Expect(type = SoftwareModuleTypeCreatedEvent.class, count = 2) })
public void targetCannotBeRegisteredIfTenantDoesNotExistsButWhenExists() throws Exception {
mvc.perform(get("/default-tenant/", tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
@@ -131,10 +131,10 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that target poll request does not change audit data on the entity.")
- @WithUser(principal = "knownPrincipal", authorities = {SpPermission.READ_TARGET, SpPermission.UPDATE_TARGET,
- SpPermission.CREATE_TARGET}, allSpPermissions = false)
- @ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
- @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1)})
+ @WithUser(principal = "knownPrincipal", authorities = { SpPermission.READ_TARGET, SpPermission.UPDATE_TARGET,
+ SpPermission.CREATE_TARGET }, allSpPermissions = false)
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
+ @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void targetPollDoesNotModifyAuditData() throws Exception {
// create target first with "knownPrincipal" user and audit data
final String knownTargetControllerId = "target1";
@@ -162,15 +162,15 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that server returns a not found response in case of empty controlloer ID.")
- @ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 0)})
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void rootRsWithoutId() throws Exception {
mvc.perform(get("/controller/v1/")).andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
}
@Test
@Description("Ensures that the system creates a new target in plug and play manner, i.e. target is authenticated but does not exist yet.")
- @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 rootRsPlugAndPlay() throws Exception {
final long current = System.currentTimeMillis();
@@ -198,8 +198,8 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that tenant specific polling time, which is saved in the db, is delivered to the controller.")
@WithUser(principal = "knownpricipal", allSpPermissions = false)
- @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 pollWithModifiedGloablPollingTime() throws Exception {
securityRule.runAs(WithSpringAuthorityRule.withUser("tenantadmin", HAS_AUTH_TENANT_CONFIGURATION), () -> {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.POLLING_TIME_INTERVAL,
@@ -218,14 +218,14 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that etag check results in not modified response if provided etag by client is identical to entity in repository.")
- @ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 6),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 3), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = ActionCreatedEvent.class, count = 2),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
- @Expect(type = SoftwareModuleCreatedEvent.class, count = 6)})
+ @Expect(type = SoftwareModuleCreatedEvent.class, count = 6) })
public void rootRsNotModified() throws Exception {
final String etag = mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
@@ -289,8 +289,8 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that the target state machine of a precomissioned target switches from "
+ "UNKNOWN to REGISTERED when the target polls for the first time.")
- @ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
- @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1)})
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
+ @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsPrecommissioned() throws Exception {
testdataFactory.createTarget("4711");
@@ -314,8 +314,8 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that the source IP address of the polling target is correctly stored in repository")
- @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 rootRsPlugAndPlayIpAddress() throws Exception {
// test
final String knownControllerId1 = "0815";
@@ -341,8 +341,8 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that the source IP address of the polling target is not stored in repository if disabled")
- @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 rootRsIpAddressNotStoredIfDisabled() throws Exception {
securityProperties.getClients().setTrackRemoteIp(false);
@@ -360,12 +360,12 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Controller trys 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 = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
- @Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
+ @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void tryToFinishAnUpdateProcessAfterItHasBeenFinished() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = testdataFactory.createTarget("911");
@@ -384,13 +384,13 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Controller sends attribute update request after device successfully closed software update.")
- @ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 2),
@Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 6), @Expect(type = TargetPollEvent.class, count = 4),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
- @Expect(type = TargetAttributesRequestedEvent.class, count = 1)})
+ @Expect(type = TargetAttributesRequestedEvent.class, count = 1) })
public void attributeUpdateRequestSendingAfterSuccessfulDeployment() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("1");
final Target savedTarget = testdataFactory.createTarget("922");
@@ -463,13 +463,13 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Test to verify that only a specific count of messages are returned based on the input actionHistory for getControllerDeploymentActionFeedback endpoint.")
- @ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
- @Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
+ @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void testActionHistoryCount() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = testdataFactory.createTarget("911");
@@ -498,13 +498,13 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Test to verify that a zero input value of actionHistory results in no action history appended for getControllerDeploymentActionFeedback endpoint.")
- @ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
- @Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
+ @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void testActionHistoryZeroInput() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = testdataFactory.createTarget("911");
@@ -529,13 +529,13 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Test to verify that entire action history is returned if the input value for actionHistory is -1, for getControllerDeploymentActionFeedback endpoint.")
- @ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
+ @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
- @Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
+ @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void testActionHistoryNegativeInput() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = testdataFactory.createTarget("911");
@@ -654,12 +654,13 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Assign multiple DS in multi-assignment mode. The earliest active Action is exposed to the controller.")
public void earliestActionIsExposedToControllerInMultiAssignMode() throws Exception {
- setMultiAssignmentsEnabled();
+ enableMultiAssignments();
final Target target = testdataFactory.createTarget();
final DistributionSet ds1 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final DistributionSet ds2 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
- final Action action1 = getFirstAssignedAction(assignDistributionSet(ds1, target));
- final Long action2Id = getFirstAssignedActionId(assignDistributionSet(ds2, target));
+ final Action action1 = getFirstAssignedAction(assignDistributionSet(ds1.getId(), target.getControllerId(), 56));
+ final Long action2Id = getFirstAssignedActionId(
+ assignDistributionSet(ds2.getId(), target.getControllerId(), 34));
assertDeploymentActionIsExposedToTarget(target.getControllerId(), action1.getId());
sendDeploymentActionFeedback(target, action1, "closed", "success");
@@ -670,7 +671,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("The system should not create a new target because of a too long controller id.")
public void rootRsWithInvalidControllerId() throws Exception {
- String invalidControllerId = RandomStringUtils.randomAlphabetic(Target.CONTROLLER_ID_MAX_SIZE + 1);
+ final String invalidControllerId = RandomStringUtils.randomAlphabetic(Target.CONTROLLER_ID_MAX_SIZE + 1);
mvc.perform(get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(), invalidControllerId))
.andExpect(status().isBadRequest());
}
@@ -684,8 +685,4 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
.andExpect(jsonPath("$._links.deploymentBase.href", containsString(expectedDeploymentBaseLink)));
}
-
- private void setMultiAssignmentsEnabled() {
- tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, true);
- }
}
diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtAction.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtAction.java
index df9977ffc..0941758fc 100644
--- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtAction.java
+++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/action/MgmtAction.java
@@ -57,9 +57,12 @@ public class MgmtAction extends MgmtBaseEntity {
@JsonProperty
private Long forceTime;
- @JsonProperty(value="forceType")
+ @JsonProperty(value = "forceType")
private MgmtActionType actionType;
+ @JsonProperty
+ private Integer weight;
+
@JsonProperty
private MgmtMaintenanceWindow maintenanceWindow;
@@ -79,6 +82,14 @@ public class MgmtAction extends MgmtBaseEntity {
this.forceTime = forceTime;
}
+ public Integer getWeight() {
+ return weight;
+ }
+
+ public void setWeight(final Integer weight) {
+ this.weight = weight;
+ }
+
public MgmtActionType getActionType() {
return actionType;
}
@@ -110,5 +121,4 @@ public class MgmtAction extends MgmtBaseEntity {
public void setType(final String type) {
this.type = type;
}
-
}
diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/distributionset/MgmtTargetAssignmentRequestBody.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/distributionset/MgmtTargetAssignmentRequestBody.java
index c9cc2421b..68c8a2fd6 100644
--- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/distributionset/MgmtTargetAssignmentRequestBody.java
+++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/distributionset/MgmtTargetAssignmentRequestBody.java
@@ -25,6 +25,7 @@ public class MgmtTargetAssignmentRequestBody {
private long forcetime;
private MgmtActionType type;
private MgmtMaintenanceWindowRequestBody maintenanceWindow;
+ private Integer weight;
/**
* JsonCreator Constructor
@@ -61,6 +62,14 @@ public class MgmtTargetAssignmentRequestBody {
this.forcetime = forcetime;
}
+ public Integer getWeight() {
+ return weight;
+ }
+
+ public void setWeight(final Integer weight) {
+ this.weight = weight;
+ }
+
public MgmtMaintenanceWindowRequestBody getMaintenanceWindow() {
return maintenanceWindow;
}
@@ -68,5 +77,4 @@ public class MgmtTargetAssignmentRequestBody {
public void setMaintenanceWindow(final MgmtMaintenanceWindowRequestBody maintenanceWindow) {
this.maintenanceWindow = maintenanceWindow;
}
-
}
diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutResponseBody.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutResponseBody.java
index 454bfc994..21c41eb3d 100644
--- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutResponseBody.java
+++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutResponseBody.java
@@ -12,12 +12,12 @@ import java.util.HashMap;
import java.util.Map;
import org.eclipse.hawkbit.mgmt.json.model.MgmtNamedEntity;
+import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
-import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
/**
*
@@ -47,6 +47,9 @@ public class MgmtRolloutResponseBody extends MgmtNamedEntity {
@JsonProperty
private MgmtActionType type;
+ @JsonProperty
+ private Integer weight;
+
public boolean isDeleted() {
return deleted;
}
@@ -114,4 +117,12 @@ public class MgmtRolloutResponseBody extends MgmtNamedEntity {
public MgmtActionType getType() {
return type;
}
+
+ public void setWeight(final Integer weight) {
+ this.weight = weight;
+ }
+
+ public Integer getWeight() {
+ return weight;
+ }
}
diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutRestRequestBody.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutRestRequestBody.java
index 9711b31b0..0c98c7561 100644
--- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutRestRequestBody.java
+++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/rollout/MgmtRolloutRestRequestBody.java
@@ -16,6 +16,7 @@ import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroup;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Model for request containing a rollout body e.g. in a POST request of
@@ -34,6 +35,9 @@ public class MgmtRolloutRestRequestBody extends AbstractMgmtRolloutConditionsEnt
private Long startAt;
+ @JsonProperty(required = false)
+ private Integer weight;
+
private MgmtActionType type;
private List groups;
@@ -124,7 +128,7 @@ public class MgmtRolloutRestRequestBody extends AbstractMgmtRolloutConditionsEnt
* @param groups
* List of {@link MgmtRolloutGroup}
*/
- public void setGroups(List groups) {
+ public void setGroups(final List groups) {
this.groups = groups;
}
@@ -139,7 +143,22 @@ public class MgmtRolloutRestRequestBody extends AbstractMgmtRolloutConditionsEnt
* @param startAt
* the start at timestamp in millis or null
*/
- public void setStartAt(Long startAt) {
+ public void setStartAt(final Long startAt) {
this.startAt = startAt;
}
+
+ /**
+ * @return the priority of {@link Rollout}
+ */
+ public Integer getWeight() {
+ return weight;
+ }
+
+ /**
+ * @param weight
+ * the priority of {@link Rollout}
+ */
+ public void setWeight(final Integer weight) {
+ this.weight = weight;
+ }
}
diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/target/MgmtDistributionSetAssignment.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/target/MgmtDistributionSetAssignment.java
index 85b0b1a97..fff7b3b07 100644
--- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/target/MgmtDistributionSetAssignment.java
+++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/target/MgmtDistributionSetAssignment.java
@@ -17,6 +17,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
public class MgmtDistributionSetAssignment extends MgmtId {
private long forcetime;
+ @JsonProperty(required = false)
+ private Integer weight;
private MgmtActionType type;
private MgmtMaintenanceWindowRequestBody maintenanceWindow;
@@ -47,6 +49,14 @@ public class MgmtDistributionSetAssignment extends MgmtId {
this.forcetime = forcetime;
}
+ public Integer getWeight() {
+ return weight;
+ }
+
+ public void setWeight(final int weight) {
+ this.weight = weight;
+ }
+
/**
* Returns {@link MgmtMaintenanceWindowRequestBody} for distribution set
* assignment.
diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtDistributionSetAutoAssignment.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtDistributionSetAutoAssignment.java
index ce0d8494c..ce643277c 100644
--- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtDistributionSetAutoAssignment.java
+++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtDistributionSetAutoAssignment.java
@@ -22,6 +22,9 @@ public class MgmtDistributionSetAutoAssignment extends MgmtId {
@JsonProperty(required = false)
private MgmtActionType type;
+ @JsonProperty(required = false)
+ private Integer weight;
+
public MgmtActionType getType() {
return type;
}
@@ -29,4 +32,12 @@ public class MgmtDistributionSetAutoAssignment extends MgmtId {
public void setType(final MgmtActionType type) {
this.type = type;
}
+
+ public Integer getWeight() {
+ return weight;
+ }
+
+ public void setWeight(final Integer weight) {
+ this.weight = weight;
+ }
}
diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java
index e32ad2c8b..33077b53f 100644
--- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java
+++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQuery.java
@@ -40,6 +40,9 @@ public class MgmtTargetFilterQuery extends MgmtBaseEntity {
@JsonProperty
private MgmtActionType autoAssignActionType;
+ @JsonProperty
+ private Integer autoAssignWeight;
+
public Long getFilterId() {
return filterId;
}
@@ -80,4 +83,11 @@ public class MgmtTargetFilterQuery extends MgmtBaseEntity {
this.autoAssignActionType = actionType;
}
+ public Integer getAutoAssignWeight() {
+ return autoAssignWeight;
+ }
+
+ public void setAutoAssignWeight(final Integer autoAssignWeight) {
+ this.autoAssignWeight = autoAssignWeight;
+ }
}
diff --git a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java
index ecfe1291b..c0c25ba88 100644
--- a/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java
+++ b/hawkbit-rest/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/targetfilter/MgmtTargetFilterQueryRequestBody.java
@@ -33,8 +33,7 @@ public class MgmtTargetFilterQueryRequestBody {
return query;
}
- public void setQuery(String query) {
+ public void setQuery(final String query) {
this.query = query;
}
-
}
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDeploymentRequestMapper.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDeploymentRequestMapper.java
index 73c545a24..108e0e740 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDeploymentRequestMapper.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDeploymentRequestMapper.java
@@ -38,7 +38,7 @@ public final class MgmtDeploymentRequestMapper {
final String targetId) {
return createAssignmentRequest(targetId, dsAssignment.getId(), dsAssignment.getType(),
- dsAssignment.getForcetime(), dsAssignment.getMaintenanceWindow());
+ dsAssignment.getForcetime(), dsAssignment.getWeight(), dsAssignment.getMaintenanceWindow());
}
/**
@@ -54,13 +54,14 @@ public final class MgmtDeploymentRequestMapper {
final Long dsId) {
return createAssignmentRequest(targetAssignment.getId(), dsId, targetAssignment.getType(),
- targetAssignment.getForcetime(), targetAssignment.getMaintenanceWindow());
+ targetAssignment.getForcetime(), targetAssignment.getWeight(), targetAssignment.getMaintenanceWindow());
}
private static DeploymentRequest createAssignmentRequest(final String targetId, final Long dsId,
- final MgmtActionType type, final long forcetime, final MgmtMaintenanceWindowRequestBody maintenanceWindow) {
+ final MgmtActionType type, final long forcetime, final Integer weight,
+ final MgmtMaintenanceWindowRequestBody maintenanceWindow) {
final DeploymentRequestBuilder request = DeploymentManagement.deploymentRequest(targetId, dsId)
- .setActionType(MgmtRestModelMapper.convertActionType(type)).setForceTime(forcetime);
+ .setActionType(MgmtRestModelMapper.convertActionType(type)).setForceTime(forcetime).setWeight(weight);
if (maintenanceWindow != null) {
final String cronSchedule = maintenanceWindow.getSchedule();
final String duration = maintenanceWindow.getDuration();
@@ -70,5 +71,4 @@ public final class MgmtDeploymentRequestMapper {
}
return request.build();
}
-
}
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java
index f61fd4c36..03d4184d7 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java
@@ -266,7 +266,6 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
final List assignmentResults = deployManagament
.assignDistributionSets(deploymentRequests);
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponse(assignmentResults));
-
}
@Override
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutMapper.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutMapper.java
index ea06d5811..cd4c6cb28 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutMapper.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutMapper.java
@@ -79,6 +79,7 @@ final class MgmtRolloutMapper {
body.setTotalTargets(rollout.getTotalTargets());
body.setDeleted(rollout.isDeleted());
body.setType(MgmtRestModelMapper.convertActionType(rollout.getActionType()));
+ rollout.getWeight().ifPresent(body::setWeight);
if (withDetails) {
for (final TotalTargetCountStatus.Status status : TotalTargetCountStatus.Status.values()) {
@@ -106,7 +107,8 @@ final class MgmtRolloutMapper {
return entityFactory.rollout().create().name(restRequest.getName()).description(restRequest.getDescription())
.set(distributionSet).targetFilterQuery(restRequest.getTargetFilterQuery())
.actionType(MgmtRestModelMapper.convertActionType(restRequest.getType()))
- .forcedTime(restRequest.getForcetime()).startAt(restRequest.getStartAt());
+ .forcedTime(restRequest.getForcetime()).startAt(restRequest.getStartAt())
+ .weight(restRequest.getWeight());
}
static RolloutGroupCreate fromRequest(final EntityFactory entityFactory, final MgmtRolloutGroup restRequest) {
@@ -249,5 +251,4 @@ final class MgmtRolloutMapper {
private static String createIllegalArgumentLiteral(final Condition condition) {
return "Condition " + condition + NOT_SUPPORTED;
}
-
}
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java
index 0309b8552..935e727d6 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryMapper.java
@@ -15,11 +15,14 @@ import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
+import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtDistributionSetAutoAssignment;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetFilterQueryRestApi;
import org.eclipse.hawkbit.repository.EntityFactory;
+import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
+import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.util.CollectionUtils;
@@ -58,6 +61,7 @@ public final class MgmtTargetFilterQueryMapper {
if (distributionSet != null) {
targetRest.setAutoAssignDistributionSet(distributionSet.getId());
targetRest.setAutoAssignActionType(MgmtRestModelMapper.convertActionType(filter.getAutoAssignActionType()));
+ filter.getAutoAssignWeight().ifPresent(targetRest::setAutoAssignWeight);
}
targetRest.add(linkTo(methodOn(MgmtTargetFilterQueryRestApi.class).getFilter(filter.getId())).withSelfRel());
@@ -76,4 +80,12 @@ public final class MgmtTargetFilterQueryMapper {
return entityFactory.targetFilterQuery().create().name(filterRest.getName()).query(filterRest.getQuery());
}
+ static AutoAssignDistributionSetUpdate fromRequest(final EntityFactory entityFactory, final long filterId,
+ final MgmtDistributionSetAutoAssignment assignRest) {
+ final ActionType type = MgmtRestModelMapper.convertActionType(assignRest.getType());
+
+ return entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(assignRest.getId()).actionType(type)
+ .weight(assignRest.getWeight());
+ }
+
}
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java
index 17006fdcd..082b19fff 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResource.java
@@ -108,8 +108,9 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA
@RequestBody final MgmtTargetFilterQueryRequestBody targetFilterRest) {
LOG.debug("updating target filter query {}", filterId);
- final TargetFilterQuery updateFilter = filterManagement.update(entityFactory.targetFilterQuery()
- .update(filterId).name(targetFilterRest.getName()).query(targetFilterRest.getQuery()));
+ final TargetFilterQuery updateFilter = filterManagement
+ .update(entityFactory.targetFilterQuery().update(filterId).name(targetFilterRest.getName())
+ .query(targetFilterRest.getQuery()));
final MgmtTargetFilterQuery response = MgmtTargetFilterQueryMapper.toResponse(updateFilter);
MgmtTargetFilterQueryMapper.addLinks(response);
@@ -127,10 +128,10 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA
@Override
public ResponseEntity postAssignedDistributionSet(
@PathVariable("filterId") final Long filterId,
- @RequestBody final MgmtDistributionSetAutoAssignment dsIdWithActionType) {
+ @RequestBody final MgmtDistributionSetAutoAssignment autoAssignRequest) {
- final TargetFilterQuery updateFilter = filterManagement.updateAutoAssignDSWithActionType(filterId,
- dsIdWithActionType.getId(), MgmtRestModelMapper.convertActionType(dsIdWithActionType.getType()));
+ final TargetFilterQuery updateFilter = filterManagement.updateAutoAssignDS(
+ MgmtTargetFilterQueryMapper.fromRequest(entityFactory, filterId, autoAssignRequest));
final MgmtTargetFilterQuery response = MgmtTargetFilterQueryMapper.toResponse(updateFilter);
MgmtTargetFilterQueryMapper.addLinks(response);
@@ -156,7 +157,7 @@ public class MgmtTargetFilterQueryResource implements MgmtTargetFilterQueryRestA
@Override
public ResponseEntity deleteAssignedDistributionSet(@PathVariable("filterId") final Long filterId) {
- filterManagement.updateAutoAssignDS(filterId, null);
+ filterManagement.updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(null));
return ResponseEntity.noContent().build();
}
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java
index edf727b57..8c046c68d 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java
@@ -206,6 +206,7 @@ public final class MgmtTargetMapper {
if (ActionType.TIMEFORCED == action.getActionType()) {
result.setForceTime(action.getForcedTime());
}
+ action.getWeight().ifPresent(result::setWeight);
result.setActionType(MgmtRestModelMapper.convertActionType(action.getActionType()));
if (action.isActive()) {
@@ -289,5 +290,4 @@ public final class MgmtTargetMapper {
static List toResponseTargetMetadata(final List metadata) {
return metadata.stream().map(MgmtTargetMapper::toResponseTargetMetadata).collect(Collectors.toList());
}
-
}
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/AbstractManagementApiIntegrationTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/AbstractManagementApiIntegrationTest.java
index 91bf72f35..309315707 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/AbstractManagementApiIntegrationTest.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/AbstractManagementApiIntegrationTest.java
@@ -12,6 +12,7 @@ import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.NamedEntity;
@@ -21,6 +22,8 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.test.TestConfiguration;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.eclipse.hawkbit.rest.RestConfiguration;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
@@ -197,4 +200,24 @@ public abstract class AbstractManagementApiIntegrationTest extends AbstractRestI
};
}
+ protected static JSONObject getAssignmentObject(final Object id, final MgmtActionType type) {
+ final JSONObject obj = new JSONObject();
+ try {
+ obj.put("id", id);
+ obj.put("type", type.getName());
+ } catch (final JSONException e) {
+ e.printStackTrace();
+ }
+ return obj;
+ }
+
+ protected static JSONObject getAssignmentObject(final Object id, final MgmtActionType type, final int weight) {
+ try {
+ return getAssignmentObject(id, type).put("weight", weight);
+ } catch (final JSONException e) {
+ e.printStackTrace();
+ }
+ return new JSONObject();
+ }
+
}
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java
index e8ef170fb..25df64d78 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResourceTest.java
@@ -555,9 +555,8 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final Set createDistributionSetsAlphabetical = createDistributionSetsAlphabetical(1);
final DistributionSet createdDs = createDistributionSetsAlphabetical.iterator().next();
- targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement
- .create(entityFactory.targetFilterQuery().create().name(knownFilterName).query("name==y")).getId(),
- createdDs.getId());
+ targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name(knownFilterName)
+ .query("name==y").autoAssignDistributionSet(createdDs.getId()));
// create some dummy target filter queries
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("b").query("name==y"));
@@ -615,12 +614,10 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
private void prepareTestFilters(final String filterNamePrefix, final DistributionSet createdDs) {
// create target filter queries that should be found
- targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement
- .create(entityFactory.targetFilterQuery().create().name(filterNamePrefix + "1").query("name==y"))
- .getId(), createdDs.getId());
- targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement
- .create(entityFactory.targetFilterQuery().create().name(filterNamePrefix + "2").query("name==y"))
- .getId(), createdDs.getId());
+ targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name(filterNamePrefix + "1")
+ .query("name==y").autoAssignDistributionSet(createdDs.getId()));
+ targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name(filterNamePrefix + "2")
+ .query("name==y").autoAssignDistributionSet(createdDs.getId()));
// create some dummy target filter queries
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name(filterNamePrefix + "b").query("name==y"));
@@ -1247,8 +1244,8 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
}
@Test
- @Description("Ensures that multi target assignment through API is reflected by the repository in the case of " +
- "DOWNLOAD_ONLY.")
+ @Description("Ensures that multi target assignment through API is reflected by the repository in the case of "
+ + "DOWNLOAD_ONLY.")
public void assignMultipleTargetsToDistributionSetAsDownloadOnly() throws Exception {
final DistributionSet createdDs = testdataFactory.createDistributionSet();
@@ -1263,8 +1260,8 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
assignDistributionSet(createdDs.getId(), knownTargetIds[0], Action.ActionType.DOWNLOAD_ONLY);
mvc.perform(post("/rest/v1/distributionsets/{ds}/assignedTargets", createdDs.getId())
- .contentType(MediaType.APPLICATION_JSON).content(list.toString()))
- .andExpect(status().isOk()).andExpect(jsonPath("$.assigned", equalTo(knownTargetIds.length - 1)))
+ .contentType(MediaType.APPLICATION_JSON).content(list.toString())).andExpect(status().isOk())
+ .andExpect(jsonPath("$.assigned", equalTo(knownTargetIds.length - 1)))
.andExpect(jsonPath("$.alreadyAssigned", equalTo(1)))
.andExpect(jsonPath("$.total", equalTo(knownTargetIds.length)));
@@ -1310,10 +1307,10 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final Long dsId = testdataFactory.createDistributionSet().getId();
final JSONArray body = new JSONArray();
- body.put(getAssignmentObject(targetIds.get(0), MgmtActionType.FORCED));
- body.put(getAssignmentObject(targetIds.get(0), MgmtActionType.FORCED));
- body.put(getAssignmentObject(targetIds.get(1), MgmtActionType.FORCED));
- body.put(getAssignmentObject(targetIds.get(1), MgmtActionType.SOFT));
+ body.put(getAssignmentObject(targetIds.get(0), MgmtActionType.FORCED, 56));
+ body.put(getAssignmentObject(targetIds.get(0), MgmtActionType.FORCED, 78));
+ body.put(getAssignmentObject(targetIds.get(1), MgmtActionType.FORCED, 67));
+ body.put(getAssignmentObject(targetIds.get(1), MgmtActionType.SOFT, 34));
enableMultiAssignments();
mvc.perform(post("/rest/v1/distributionsets/{ds}/assignedTargets", dsId).content(body.toString())
@@ -1321,14 +1318,32 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
.andExpect(jsonPath("total", equalTo(body.length())));
}
+ @Test
+ @Description("An assignment request containing a weight is only accepted when weight is valide and multi assignment is on.")
+ public void weightValidation() throws Exception {
+ final String targetId = testdataFactory.createTarget().getControllerId();
+ final Long dsId = testdataFactory.createDistributionSet().getId();
+ final int weight = 78;
+ final JSONArray bodyValide = new JSONArray().put(getAssignmentObject(targetId, MgmtActionType.FORCED, weight));
+ final JSONArray bodyInvalide = new JSONArray()
+ .put(getAssignmentObject(targetId, MgmtActionType.FORCED, Action.WEIGHT_MIN - 1));
- public static JSONObject getAssignmentObject(final String targetId, final MgmtActionType type)
- throws JSONException {
- final JSONObject obj = new JSONObject();
- obj.put("id", targetId);
- obj.put("type", type.getName());
- return obj;
+ mvc.perform(post("/rest/v1/distributionsets/{ds}/assignedTargets", dsId).content(bodyValide.toString())
+ .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.multiassignmentNotEnabled")));
+ enableMultiAssignments();
+ mvc.perform(post("/rest/v1/distributionsets/{ds}/assignedTargets", dsId).content(bodyInvalide.toString())
+ .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.repo.constraintViolation")));
+ mvc.perform(post("/rest/v1/distributionsets/{ds}/assignedTargets", dsId).content(bodyValide.toString())
+ .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
+ .andExpect(status().isOk());
+
+ final List actions = deploymentManagement.findActionsAll(PAGE).get().collect(Collectors.toList());
+ assertThat(actions).size().isEqualTo(1);
+ assertThat(actions.get(0).getWeight()).get().isEqualTo(weight);
}
-
}
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutResourceTest.java
index d0ca9eb1e..17f2812a4 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutResourceTest.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutResourceTest.java
@@ -916,6 +916,36 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20, Action.ActionType.DOWNLOAD_ONLY);
}
+ @Test
+ @Description("A rollout create request containing a weight is only accepted when weight is valide and multi assignment is on.")
+ public void weightValidation() throws Exception {
+ testdataFactory.createTargets(4, "rollout", "description");
+ final Long dsId = testdataFactory.createDistributionSet().getId();
+ final int weight = 66;
+
+ final String invalideWeightRequest = JsonBuilder.rollout("withWeight", "d", 2, dsId, "id==rollout*",
+ new RolloutGroupConditionBuilder().withDefaults().build(), null, null, Action.WEIGHT_MIN - 1);
+ final String valideWeightRequest = JsonBuilder.rollout("withWeight", "d", 2, dsId, "id==rollout*",
+ new RolloutGroupConditionBuilder().withDefaults().build(), null, null, weight);
+
+ mvc.perform(post("/rest/v1/rollouts").content(valideWeightRequest).contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.multiassignmentNotEnabled")));
+ enableMultiAssignments();
+ mvc.perform(post("/rest/v1/rollouts").content(invalideWeightRequest).contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.repo.constraintViolation")));
+ mvc.perform(post("/rest/v1/rollouts").content(valideWeightRequest).contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
+ .andExpect(status().isCreated());
+
+ final List rollouts = rolloutManagement.findAll(PAGE, false).getContent();
+ assertThat(rollouts).hasSize(1);
+ assertThat(rollouts.get(0).getWeight()).get().isEqualTo(weight);
+ }
+
protected T doWithTimeout(final Callable callable, final SuccessCondition successCondition,
final long timeout, final long pollInterval) throws Exception // NOPMD
{
@@ -955,14 +985,12 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
private void postRollout(final String name, final int groupSize, final Long distributionSetId,
final String targetFilterQuery, final int targets, final Action.ActionType type) throws Exception {
- String actionType = MgmtRestModelMapper.convertActionType(type).getName();
- String rollout = JsonBuilder.rollout(name, "desc", groupSize, distributionSetId, targetFilterQuery,
- new RolloutGroupConditionBuilder().withDefaults().build(), null, actionType);
+ final String actionType = MgmtRestModelMapper.convertActionType(type).getName();
+ final String rollout = JsonBuilder.rollout(name, "desc", groupSize, distributionSetId, targetFilterQuery,
+ new RolloutGroupConditionBuilder().withDefaults().build(), null, actionType, null);
- mvc.perform(post("/rest/v1/rollouts")
- .content(rollout)
- .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
- .andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
+ mvc.perform(post("/rest/v1/rollouts").content(rollout).contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
.andExpect(jsonPath("$.name", equalTo(name))).andExpect(jsonPath("$.status", equalTo("creating")))
.andExpect(jsonPath("$.type", equalTo(actionType)))
.andExpect(jsonPath("$.targetFilterQuery", equalTo(targetFilterQuery)))
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java
index 03d232ad7..fe5175f9c 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java
@@ -20,12 +20,15 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import java.util.List;
+
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
+import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
@@ -104,8 +107,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
public void updateTargetWhichDoesNotExistsLeadsToEntityNotFound() throws Exception {
final String notExistingId = "4395";
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + notExistingId).content("{}")
- .contentType(MediaType.APPLICATION_JSON)).andDo(print())
- .andExpect(status().isNotFound());
+ .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isNotFound());
}
@Test
@@ -166,8 +168,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
createSingleTargetFilterQuery(idB, testQuery);
createSingleTargetFilterQuery(idC, testQuery);
- mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)).andExpect(status().isOk())
- .andDo(print())
+ mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)).andExpect(status().isOk()).andDo(print())
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(knownTargetAmount)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(knownTargetAmount)))
@@ -252,9 +253,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
final String hrefPrefix = "http://localhost" + MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"
+ tfq.getId();
// test
- mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()))
- .andDo(print()).andExpect(status().isOk())
- .andExpect(jsonPath(JSON_PATH_NAME, equalTo(knownName)))
+ mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId())).andDo(print())
+ .andExpect(status().isOk()).andExpect(jsonPath(JSON_PATH_NAME, equalTo(knownName)))
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(knownQuery)))
.andExpect(jsonPath("$._links.self.href", equalTo(hrefPrefix)))
.andExpect(jsonPath("$._links.autoAssignDS.href", equalTo(hrefPrefix + "/autoAssignDS")));
@@ -402,8 +402,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
? "{\"id\":" + set.getId() + ", \"type\":\"" + actionType.getName() + "\"}"
: "{\"id\":" + set.getId() + "}";
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
- .content(payload).contentType(MediaType.APPLICATION_JSON)).andDo(print())
- .andExpect(status().isOk());
+ .content(payload).contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk());
final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(tfq.getId()).get();
final MgmtActionType expectedActionType = actionType != null ? actionType : MgmtActionType.FORCED;
@@ -412,9 +411,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
assertThat(updatedFilterQuery.getAutoAssignActionType())
.isEqualTo(MgmtRestModelMapper.convertActionType(expectedActionType));
- mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()))
- .andDo(print()).andExpect(status().isOk())
- .andExpect(jsonPath(JSON_PATH_NAME, equalTo(tfq.getName())))
+ mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId())).andDo(print())
+ .andExpect(status().isOk()).andExpect(jsonPath(JSON_PATH_NAME, equalTo(tfq.getName())))
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(tfq.getQuery())))
.andExpect(jsonPath(JSON_PATH_AUTO_ASSIGN_DS, equalTo(set.getId().intValue())))
.andExpect(jsonPath(JSON_PATH_AUTO_ASSIGN_ACTION_TYPE, equalTo(expectedActionType.getName())))
@@ -427,8 +425,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
throws Exception {
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
.content("{\"id\":" + set.getId() + ", \"type\":\"" + MgmtActionType.TIMEFORCED.getName() + "\"}")
- .contentType(MediaType.APPLICATION_JSON)).andDo(print())
- .andExpect(status().isBadRequest())
+ .contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isBadRequest())
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
equalTo(InvalidAutoAssignActionTypeException.class.getName())))
.andExpect(jsonPath(JSON_PATH_ERROR_CODE,
@@ -491,7 +488,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
final DistributionSet set = testdataFactory.createDistributionSet(dsName);
final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery);
- targetFilterQueryManagement.updateAutoAssignDS(tfq.getId(), set.getId());
+ targetFilterQueryManagement
+ .updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(tfq.getId()).ds(set.getId()));
final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(tfq.getId()).get();
@@ -514,6 +512,34 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
}
+ @Test
+ @Description("An auto assignment containing a weight is only accepted when weight is valide and multi assignment is on.")
+ public void weightValidation() throws Exception {
+ final Long filterId = createSingleTargetFilterQuery("filter1", "name==*").getId();
+ final Long dsId = testdataFactory.createDistributionSet().getId();
+
+ final String invalideWeightRequest = new JSONObject().put("id", dsId).put("weight", Action.WEIGHT_MIN - 1)
+ .toString();
+ final String valideWeightRequest = new JSONObject().put("id", dsId).put("weight", 45).toString();
+
+ mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/{targetFilterQueryId}/autoAssignDS",
+ filterId).content(valideWeightRequest).contentType(MediaType.APPLICATION_JSON)).andDo(print())
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.multiassignmentNotEnabled")));
+ enableMultiAssignments();
+ mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/{targetFilterQueryId}/autoAssignDS",
+ filterId).content(invalideWeightRequest).contentType(MediaType.APPLICATION_JSON)).andDo(print())
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.repo.constraintViolation")));
+ mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/{targetFilterQueryId}/autoAssignDS",
+ filterId).content(valideWeightRequest).contentType(MediaType.APPLICATION_JSON)).andDo(print())
+ .andExpect(status().isOk());
+
+ final List filters = targetFilterQueryManagement.findAll(PAGE).getContent();
+ assertThat(filters).hasSize(1);
+ assertThat(filters.get(0).getAutoAssignWeight().get()).isEqualTo(45);
+ }
+
private TargetFilterQuery createSingleTargetFilterQuery(final String name, final String query) {
return targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name(name).query(query));
}
diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java
index eafa344ea..6f1e331b3 100644
--- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java
+++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java
@@ -26,7 +26,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import java.net.URISyntaxException;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -40,6 +39,7 @@ import javax.validation.ConstraintViolationException;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.im.authentication.SpPermission;
+import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.ActionFields;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
@@ -61,7 +61,6 @@ import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.eclipse.hawkbit.util.IpUtil;
import org.json.JSONArray;
-import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;
import org.springframework.data.domain.PageRequest;
@@ -1876,7 +1875,8 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
final List dsIds = testdataFactory.createDistributionSets(2).stream().map(DistributionSet::getId)
.collect(Collectors.toList());
- final JSONArray body = getAssignmentBody(dsIds);
+ final JSONArray body = new JSONArray();
+ dsIds.forEach(id -> body.put(getAssignmentObject(id, MgmtActionType.FORCED, 67)));
mvc.perform(post("/rest/v1/targets/{targetId}/assignedDS", targetId).content(body.toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
@@ -1889,8 +1889,7 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
final String targetId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
- final JSONArray body = getAssignmentBody(Collections.singletonList(dsId));
-
+ final JSONArray body = new JSONArray().put(getAssignmentObject(dsId, MgmtActionType.FORCED));
mvc.perform(post("/rest/v1/targets/{targetId}/assignedDS", targetId).content(body.toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
@@ -1902,7 +1901,8 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
final String targetId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
- final JSONArray body = getAssignmentBody(Arrays.asList(dsId, dsId));
+ final JSONObject assignment = getAssignmentObject(dsId, MgmtActionType.FORCED);
+ final JSONArray body = new JSONArray().put(assignment).put(assignment);
mvc.perform(post("/rest/v1/targets/{targetId}/assignedDS", targetId).content(body.toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
@@ -1916,21 +1916,90 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
final List dsIds = testdataFactory.createDistributionSets(2).stream().map(DistributionSet::getId)
.collect(Collectors.toList());
- final JSONArray body = getAssignmentBody(dsIds);
+ final JSONArray body = new JSONArray();
+ dsIds.forEach(id -> body.put(getAssignmentObject(id, MgmtActionType.FORCED, 76)));
enableMultiAssignments();
mvc.perform(post("/rest/v1/targets/{targetId}/assignedDS", targetId).content(body.toString())
- .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
- .andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(2)));
+ .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
+ .andExpect(jsonPath("total", equalTo(2)));
}
- public static JSONArray getAssignmentBody(final Collection dsIds) throws JSONException {
- final JSONArray body = new JSONArray();
- for (final Long id : dsIds) {
- final JSONObject obj = new JSONObject();
- obj.put("id", id);
- body.put(obj);
- }
- return body;
+ @Test
+ @Description("An assignment request containing a weight is only accepted when weight is valide and multi assignment is on.")
+ public void weightValidation() throws Exception {
+ final String targetId = testdataFactory.createTarget().getControllerId();
+ final Long dsId = testdataFactory.createDistributionSet().getId();
+ final int weight = 98;
+
+ final JSONObject bodyValid = getAssignmentObject(dsId, MgmtActionType.FORCED, weight);
+ final JSONObject bodyInvalid = getAssignmentObject(dsId, MgmtActionType.FORCED, Action.WEIGHT_MIN - 1);
+
+ enableMultiAssignments();
+ mvc.perform(post("/rest/v1/targets/{targetId}/assignedDS", targetId).content(bodyInvalid.toString())
+ .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.repo.constraintViolation")));
+ mvc.perform(post("/rest/v1/targets/{targetId}/assignedDS", targetId).content(bodyValid.toString())
+ .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
+ .andExpect(status().isOk());
+
+ final List actions = deploymentManagement.findActionsAll(PAGE).get().collect(Collectors.toList());
+ assertThat(actions).size().isEqualTo(1);
+ assertThat(actions.get(0).getWeight()).get().isEqualTo(weight);
}
+
+ @Test
+ @Description("An assignment request containing a valid weight when multi assignment is off.")
+ public void weightWithSingleAssignment() throws Exception {
+ final String targetId = testdataFactory.createTarget().getControllerId();
+ final Long dsId = testdataFactory.createDistributionSet().getId();
+
+ final JSONObject bodyValid = getAssignmentObject(dsId, MgmtActionType.FORCED, 98);
+
+ mvc.perform(post("/rest/v1/targets/{targetId}/assignedDS", targetId).content(bodyValid.toString())
+ .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.multiassignmentNotEnabled")));
+ }
+
+ @Test
+ @Description("An assignment request containing a valid weight when multi assignment is on.")
+ public void weightWithMultiAssignment() throws Exception {
+ final String targetId = testdataFactory.createTarget().getControllerId();
+ final Long dsId = testdataFactory.createDistributionSet().getId();
+ final int weight = 98;
+
+ final JSONObject bodyValid = getAssignmentObject(dsId, MgmtActionType.FORCED, weight);
+
+ enableMultiAssignments();
+ mvc.perform(post("/rest/v1/targets/{targetId}/assignedDS", targetId).content(bodyValid.toString())
+ .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
+ .andExpect(status().isOk());
+
+ final List actions = deploymentManagement.findActionsAll(PAGE).get().collect(Collectors.toList());
+ assertThat(actions).size().isEqualTo(1);
+ assertThat(actions.get(0).getWeight()).get().isEqualTo(weight);
+ }
+
+ @Test
+ @Description("Get weight of action")
+ public void getActionWeight() throws Exception {
+ final String targetId = testdataFactory.createTarget().getControllerId();
+ final Long dsId = testdataFactory.createDistributionSet().getId();
+ final int customWeightHigh = 800;
+ final int customWeightLow = 300;
+ assignDistributionSet(dsId, targetId);
+ enableMultiAssignments();
+ assignDistributionSet(dsId, targetId, customWeightHigh);
+ assignDistributionSet(dsId, targetId, customWeightLow);
+
+ mvc.perform(get("/rest/v1/targets/{targetId}/actions", targetId)
+ .param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "WEIGHT:ASC")).andDo(MockMvcResultPrinter.print())
+ .andExpect(status().isOk()).andExpect(jsonPath("content.[0].weight").doesNotExist())
+ .andExpect(jsonPath("content.[1].weight", equalTo(customWeightLow)))
+ .andExpect(jsonPath("content.[2].weight", equalTo(customWeightHigh)));
+
+ }
+
}
diff --git a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java
index f35381d83..4030a1fd4 100644
--- a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java
+++ b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java
@@ -83,6 +83,7 @@ public class ResponseExceptionHandler {
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_CONFIGURATION_VALUE_CHANGE_NOT_ALLOWED, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_MULTIASSIGNMENT_NOT_ENABLED, HttpStatus.BAD_REQUEST);
+ ERROR_TO_HTTP_STATUS.put(SpServerError.SP_NO_WEIGHT_PROVIDED_IN_MULTIASSIGNMENT_MODE, HttpStatus.BAD_REQUEST);
}
private static HttpStatus getStatusOrDefault(final SpServerError error) {
diff --git a/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java b/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java
index 598f1f9f7..743d492cc 100644
--- a/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java
+++ b/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java
@@ -449,25 +449,27 @@ public abstract class JsonBuilder {
public static String rollout(final String name, final String description, final int groupSize,
final long distributionSetId, final String targetFilterQuery, final RolloutGroupConditions conditions) {
- return rollout(name, description, groupSize, distributionSetId, targetFilterQuery, conditions, null, null);
+ return rollout(name, description, groupSize, distributionSetId, targetFilterQuery, conditions, null, null,
+ null);
}
public static String rollout(final String name, final String description, final Integer groupSize,
final long distributionSetId, final String targetFilterQuery, final RolloutGroupConditions conditions,
final List groups) {
- return rollout(name, description, groupSize, distributionSetId, targetFilterQuery, conditions, groups, null);
+ return rollout(name, description, groupSize, distributionSetId, targetFilterQuery, conditions, groups, null,
+ null);
}
public static String rollout(final String name, final String description, final Integer groupSize,
final long distributionSetId, final String targetFilterQuery, final RolloutGroupConditions conditions,
final String type) {
- return rollout(name, description, groupSize, distributionSetId, targetFilterQuery, conditions, null, type);
+ return rollout(name, description, groupSize, distributionSetId, targetFilterQuery, conditions, null, type,
+ null);
}
public static String rollout(final String name, final String description, final Integer groupSize,
final long distributionSetId, final String targetFilterQuery, final RolloutGroupConditions conditions,
- final List groups, final String type) {
-
+ final List groups, final String type, final Integer weight) {
final JSONObject json = new JSONObject();
try {
@@ -481,6 +483,10 @@ public abstract class JsonBuilder {
json.put("type", type);
}
+ if (weight != null) {
+ json.put("weight", weight);
+ }
+
if (conditions != null) {
final JSONObject successCondition = new JSONObject();
diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/AbstractApiRestDocumentation.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/AbstractApiRestDocumentation.java
index fa2960899..1303cb2b1 100644
--- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/AbstractApiRestDocumentation.java
+++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/AbstractApiRestDocumentation.java
@@ -25,11 +25,13 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.ddi.rest.resource.DdiApiConfiguration;
import org.eclipse.hawkbit.mgmt.rest.resource.MgmtApiConfiguration;
+import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ArtifactUpload;
+import org.eclipse.hawkbit.repository.model.DeploymentRequestBuilder;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
@@ -37,6 +39,7 @@ import org.eclipse.hawkbit.repository.test.TestConfiguration;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.eclipse.hawkbit.rest.RestConfiguration;
import org.eclipse.hawkbit.rest.util.FilterHttpResponse;
+import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired;
@@ -98,8 +101,13 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
}
public static MyFieldFieldDesc requestFieldWithPath(final String path, final boolean mandatory) {
+ return requestFieldWithPath(path, mandatory, mandatory ? "X" : "");
+ }
+
+ private static MyFieldFieldDesc requestFieldWithPath(final String path, final boolean mandatory,
+ final String mandatoryMessage) {
final MyFieldFieldDesc myFieldDesc = new MyFieldFieldDesc(path);
- myFieldDesc.attributes(key("mandatory").value(mandatory ? "X" : ""));
+ myFieldDesc.attributes(key("mandatory").value(mandatoryMessage));
// defaults
myFieldDesc.attributes(key("value").value(""));
@@ -118,6 +126,10 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
return requestFieldWithPath(path, false);
}
+ public static MyFieldFieldDesc requestFieldWithPathMandatoryInMultiAssignMode(final String path) {
+ return requestFieldWithPath(path, false, "when multi-assignment is enabled");
+ }
+
public static class MyFieldFieldDesc extends SubsectionDescriptor {
/**
@@ -154,11 +166,17 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
final Target savedTarget = targetManagement.create(entityFactory.target().create().controllerId(name)
.status(TargetUpdateStatus.UNKNOWN).address("http://192.168.0.1").description("My name is " + name)
.lastTargetQuery(System.currentTimeMillis()));
-
- final List updatedTargets = maintenanceWindowSchedule == null
- ? assignWithoutMaintenanceWindow(distributionSet, savedTarget, timeforced)
- : assignWithMaintenanceWindow(distributionSet, savedTarget, timeforced, maintenanceWindowSchedule,
- maintenanceWindowDuration, maintenanceWindowTimeZone);
+ final DeploymentRequestBuilder deploymentRequestBuilder = DeploymentManagement
+ .deploymentRequest(savedTarget.getControllerId(), distributionSet.getId())
+ .setMaintenance(maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone);
+ if (timeforced) {
+ deploymentRequestBuilder.setActionType(ActionType.TIMEFORCED);
+ }
+ if (isMultiAssignmentsEnabled()) {
+ deploymentRequestBuilder.setWeight(600);
+ }
+ final List updatedTargets = makeAssignment(deploymentRequestBuilder.build()).getAssignedEntity()
+ .stream().map(Action::getTarget).collect(Collectors.toList());
if (inSync) {
feedbackToByInSync(distributionSet);
@@ -167,28 +185,6 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
return updatedTargets.get(0);
}
- private List assignWithoutMaintenanceWindow(final DistributionSet distributionSet, final Target savedTarget,
- final boolean timeforced) {
- final List actions = timeforced
- ? assignDistributionSet(distributionSet.getId(), savedTarget.getControllerId(), ActionType.TIMEFORCED)
- .getAssignedEntity()
- : assignDistributionSet(distributionSet, savedTarget).getAssignedEntity();
- return actions.stream().map(Action::getTarget).collect(Collectors.toList());
- }
-
- private List assignWithMaintenanceWindow(final DistributionSet distributionSet, final Target savedTarget,
- final boolean timeforced, final String maintenanceWindowSchedule, final String maintenanceWindowDuration,
- final String maintenanceWindowTimeZone) {
- final List actions = timeforced
- ? assignDistributionSetWithMaintenanceWindow(distributionSet.getId(), savedTarget.getControllerId(),
- ActionType.TIMEFORCED, maintenanceWindowSchedule, maintenanceWindowDuration,
- maintenanceWindowTimeZone).getAssignedEntity()
- : assignDistributionSetWithMaintenanceWindow(distributionSet.getId(), savedTarget.getControllerId(),
- maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone)
- .getAssignedEntity();
- return actions.stream().map(Action::getTarget).collect(Collectors.toList());
- }
-
protected DistributionSet createDistributionSet() {
DistributionSet distributionSet = testdataFactory.createDistributionSet("");
distributionSet = distributionSetManagement.update(entityFactory.distributionSet()
@@ -314,4 +310,9 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
parameterWithName("q").description(ApiModelPropertiesGeneric.FIQL));
}
+ protected boolean isMultiAssignmentsEnabled() {
+ return Boolean.TRUE.equals(tenantConfigurationManagement
+ .getConfigurationValue(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue());
+ }
+
}
diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/MgmtApiModelProperties.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/MgmtApiModelProperties.java
index 15a7f3b8b..33339dc53 100644
--- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/MgmtApiModelProperties.java
+++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/MgmtApiModelProperties.java
@@ -109,6 +109,8 @@ public final class MgmtApiModelProperties {
public static final String ROLLOUT_LINKS_GROUPS = "Link to retrieve the groups a rollout";
public static final String ROLLOUT_START_ASYNC = "Start the rollout asynchronous";
+ public static final String RESULTING_ACTIONS_WEIGHT = "Weight of the resulting Actions";
+
public static final String UPDATE_STATUS = "Current update status of the target.";
public static final String TARGET_ATTRIBUTES = "Target attributes.";
@@ -138,6 +140,8 @@ public final class MgmtApiModelProperties {
public static final String ACTION_LIST = "List of actions.";
+ public static final String ACTION_WEIGHT = "Weight of the action showing the importance of the update.";
+
public static final String IP_ADDRESS = "Last known IP address of the target. Only presented if IP address of the target itself is known (connected directly through DDI API).";
public static final String ADDRESS = "The last known address URI of the target. Includes information of the target is connected either directly (DDI) through HTTP or indirectly (DMF) through amqp.";
@@ -188,6 +192,7 @@ public final class MgmtApiModelProperties {
// request parameter
public static final String FORCETIME = "Forcetime in milliseconds.";
public static final String FORCE = "Force as boolean.";
+ public static final String ASSIGNMENT_WEIGHT = "Importance of the assignment.";
public static final String ASSIGNMENT_TYPE = "The type of the assignment.";
public static final String TARGET_ASSIGNED = "The number of targets that have been assigned as part of this operation.";
public static final String TARGET_ASSIGNED_ALREADY = "The number of targets which already had been the assignment.";
diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/DistributionSetsDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/DistributionSetsDocumentationTest.java
index e793f395a..97f50126a 100644
--- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/DistributionSetsDocumentationTest.java
+++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/DistributionSetsDocumentationTest.java
@@ -385,6 +385,9 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
.description(MgmtApiModelProperties.OFFLINE_UPDATE).optional()),
requestFields(
requestFieldWithPath("[].id").description(ApiModelPropertiesGeneric.ITEM_ID),
+ requestFieldWithPathMandatoryInMultiAssignMode("[].weight")
+ .description(MgmtApiModelProperties.ASSIGNMENT_WEIGHT)
+ .type(JsonFieldType.NUMBER).attributes(key("value").value("0 - 1000")),
optionalRequestFieldWithPath("[].forcetime")
.description(MgmtApiModelProperties.FORCETIME),
optionalRequestFieldWithPath("[].maintenanceWindow")
diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/RolloutResourceDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/RolloutResourceDocumentationTest.java
index 329225cb5..ab4167224 100644
--- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/RolloutResourceDocumentationTest.java
+++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/RolloutResourceDocumentationTest.java
@@ -23,10 +23,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import com.fasterxml.jackson.core.JsonProcessingException;
-
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
+import org.eclipse.hawkbit.repository.builder.RolloutCreate;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorAction;
@@ -52,6 +51,8 @@ import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.restdocs.snippet.Snippet;
+import com.fasterxml.jackson.core.JsonProcessingException;
+
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
@@ -80,7 +81,7 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
@Description("Handles the GET request of retrieving all rollouts. Required Permission: "
+ SpPermission.READ_ROLLOUT)
public void getRollouts() throws Exception {
-
+ enableMultiAssignments();
createRolloutEntity();
mockMvc.perform(get(MgmtRestConstants.ROLLOUT_V1_REQUEST_MAPPING).accept(MediaTypes.HAL_JSON_VALUE))
@@ -113,6 +114,8 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "id").description(ApiModelPropertiesGeneric.ITEM_ID));
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "name").description(ApiModelPropertiesGeneric.NAME));
+ allFieldDescriptor.add(fieldWithPath(arrayPrefix + "weight")
+ .description(MgmtApiModelProperties.RESULTING_ACTIONS_WEIGHT).type(JsonFieldType.NUMBER).optional());
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "deleted").description(ApiModelPropertiesGeneric.DELETED));
allFieldDescriptor
.add(fieldWithPath(arrayPrefix + "description").description(ApiModelPropertiesGeneric.DESCRPTION));
@@ -159,7 +162,7 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
@Description("Handles the GET request of retrieving a single rollout. Required Permission: "
+ SpPermission.READ_ROLLOUT)
public void getRollout() throws Exception {
-
+ enableMultiAssignments();
final Rollout rollout = createRolloutEntity();
mockMvc.perform(get(MgmtRestConstants.ROLLOUT_V1_REQUEST_MAPPING + "/{rolloutId}", rollout.getId())
@@ -195,47 +198,47 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
.contentType(MediaTypes.HAL_JSON_UTF8).accept(MediaTypes.HAL_JSON_VALUE))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
.andExpect(content().contentType(MediaTypes.HAL_JSON_UTF8))
- .andDo(this.document.document(
- requestFields(requestFieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
- optionalRequestFieldWithPath("type").description(
- MgmtApiModelProperties.ROLLOUT_TYPE).attributes(
- key("value").value("['soft', 'forced', 'timeforced', 'downloadonly']")),
- requestFieldWithPath("distributionSetId")
- .description(MgmtApiModelProperties.ROLLOUT_DS_ID),
- requestFieldWithPath("targetFilterQuery")
- .description(MgmtApiModelProperties.ROLLOUT_FILTER_QUERY),
- requestFieldWithPath("amountGroups")
- .description(MgmtApiModelProperties.ROLLOUT_AMOUNT_GROUPS),
- optionalRequestFieldWithPath("description")
- .description(ApiModelPropertiesGeneric.DESCRPTION),
- optionalRequestFieldWithPath("successCondition")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION),
- optionalRequestFieldWithPath("successCondition.condition")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_CONDITION)
- .attributes(key("value").value("['threshold']")),
- optionalRequestFieldWithPath("successCondition.expression")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_EXP),
- optionalRequestFieldWithPath("successAction")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION),
- optionalRequestFieldWithPath("successAction.action")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_ACTION)
- .attributes(key("value").value("['nextgroup']")),
- optionalRequestFieldWithPath("successAction.expression")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_EXP),
- optionalRequestFieldWithPath("errorCondition")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION),
- optionalRequestFieldWithPath("errorCondition.condition")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_CONDITION)
- .attributes(key("value").value("['threshold']")),
- optionalRequestFieldWithPath("errorCondition.expression")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_EXP),
- optionalRequestFieldWithPath("errorAction")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION),
- optionalRequestFieldWithPath("errorAction.action")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_ACTION)
- .attributes(key("value").value("['pause']")),
- optionalRequestFieldWithPath("errorAction.expression")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_EXP)),
+ .andDo(this.document.document(requestFields(
+ requestFieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
+ requestFieldWithPathMandatoryInMultiAssignMode("weight").type(JsonFieldType.NUMBER)
+ .description(MgmtApiModelProperties.RESULTING_ACTIONS_WEIGHT)
+ .attributes(key("value").value("0 - 1000")),
+ requestFieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
+ optionalRequestFieldWithPath("type").description(MgmtApiModelProperties.ROLLOUT_TYPE)
+ .attributes(key("value").value("['soft', 'forced', 'timeforced', 'downloadonly']")),
+ requestFieldWithPath("distributionSetId").description(MgmtApiModelProperties.ROLLOUT_DS_ID),
+ requestFieldWithPath("targetFilterQuery")
+ .description(MgmtApiModelProperties.ROLLOUT_FILTER_QUERY),
+ requestFieldWithPath("amountGroups").description(MgmtApiModelProperties.ROLLOUT_AMOUNT_GROUPS),
+ optionalRequestFieldWithPath("description").description(ApiModelPropertiesGeneric.DESCRPTION),
+ optionalRequestFieldWithPath("successCondition")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION),
+ optionalRequestFieldWithPath("successCondition.condition")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_CONDITION)
+ .attributes(key("value").value("['threshold']")),
+ optionalRequestFieldWithPath("successCondition.expression")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_EXP),
+ optionalRequestFieldWithPath("successAction")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION),
+ optionalRequestFieldWithPath("successAction.action")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_ACTION)
+ .attributes(key("value").value("['nextgroup']")),
+ optionalRequestFieldWithPath("successAction.expression")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_EXP),
+ optionalRequestFieldWithPath("errorCondition")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION),
+ optionalRequestFieldWithPath("errorCondition.condition")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_CONDITION)
+ .attributes(key("value").value("['threshold']")),
+ optionalRequestFieldWithPath("errorCondition.expression")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_EXP),
+ optionalRequestFieldWithPath("errorAction")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION),
+ optionalRequestFieldWithPath("errorAction.action")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_ACTION)
+ .attributes(key("value").value("['pause']")),
+ optionalRequestFieldWithPath("errorAction.expression")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_EXP)),
getRolloutResponseFields(false, true)));
@@ -283,77 +286,83 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
.contentType(MediaType.APPLICATION_JSON).accept(MediaTypes.HAL_JSON_VALUE))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
.andExpect(content().contentType(MediaTypes.HAL_JSON_UTF8))
- .andDo(this.document.document(requestFields(
- requestFieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
- requestFieldWithPath("distributionSetId").description(MgmtApiModelProperties.ROLLOUT_DS_ID),
- requestFieldWithPath("targetFilterQuery")
- .description(MgmtApiModelProperties.ROLLOUT_FILTER_QUERY),
- optionalRequestFieldWithPath("description").description(ApiModelPropertiesGeneric.DESCRPTION),
- optionalRequestFieldWithPath("successCondition")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION),
- optionalRequestFieldWithPath("successCondition.condition")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_CONDITION)
- .attributes(key("value").value("['threshold']")),
- optionalRequestFieldWithPath("successCondition.expression")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_EXP),
- optionalRequestFieldWithPath("successAction")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION),
- optionalRequestFieldWithPath("successAction.action")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_ACTION)
- .attributes(key("value").value("['nextgroup']")),
- optionalRequestFieldWithPath("successAction.expression")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_EXP),
- optionalRequestFieldWithPath("errorCondition")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION),
- optionalRequestFieldWithPath("errorCondition.condition")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_CONDITION)
- .attributes(key("value").value("['threshold']")),
- optionalRequestFieldWithPath("errorCondition.expression")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_EXP),
- optionalRequestFieldWithPath("errorAction")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION),
- optionalRequestFieldWithPath("errorAction.action")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_ACTION)
- .attributes(key("value").value("['pause']")),
- optionalRequestFieldWithPath("errorAction.expression")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_EXP),
-
- requestFieldWithPath("groups").description(MgmtApiModelProperties.ROLLOUT_GROUPS),
- requestFieldWithPath("groups[].name").description(ApiModelPropertiesGeneric.NAME),
- requestFieldWithPath("groups[].description").description(ApiModelPropertiesGeneric.DESCRPTION),
- optionalRequestFieldWithPath("groups[].targetFilterQuery")
- .description(MgmtApiModelProperties.ROLLOUT_GROUP_FILTER_QUERY),
- optionalRequestFieldWithPath("groups[].targetPercentage")
- .description(MgmtApiModelProperties.ROLLOUT_GROUP_TARGET_PERCENTAGE)
- .attributes(key("value").value("0..100")),
- optionalRequestFieldWithPath("groups[].successCondition")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION),
- optionalRequestFieldWithPath("groups[].successCondition.condition")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_CONDITION)
- .attributes(key("value").value("['threshold']")),
- optionalRequestFieldWithPath("groups[].successCondition.expression")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_EXP),
- optionalRequestFieldWithPath("groups[].successAction")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION),
- optionalRequestFieldWithPath("groups[].successAction.action")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_ACTION)
- .attributes(key("value").value("['nextgroup']")),
- optionalRequestFieldWithPath("groups[].successAction.expression")
- .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_EXP),
- optionalRequestFieldWithPath("groups[].errorCondition")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION),
- optionalRequestFieldWithPath("groups[].errorCondition.condition")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_CONDITION)
- .attributes(key("value").value("['threshold']")),
- optionalRequestFieldWithPath("groups[].errorCondition.expression")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_EXP),
- optionalRequestFieldWithPath("groups[].errorAction")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION),
- optionalRequestFieldWithPath("groups[].errorAction.action")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_ACTION)
- .attributes(key("value").value("['pause']")),
- optionalRequestFieldWithPath("groups[].errorAction.expression")
- .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_EXP)),
+ .andDo(this.document.document(
+ requestFields(requestFieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
+ requestFieldWithPathMandatoryInMultiAssignMode("weight")
+ .type(JsonFieldType.NUMBER)
+ .description(MgmtApiModelProperties.RESULTING_ACTIONS_WEIGHT)
+ .attributes(key("value").value("0 - 1000")),
+ requestFieldWithPath("distributionSetId")
+ .description(MgmtApiModelProperties.ROLLOUT_DS_ID),
+ requestFieldWithPath("targetFilterQuery")
+ .description(MgmtApiModelProperties.ROLLOUT_FILTER_QUERY),
+ optionalRequestFieldWithPath("description")
+ .description(ApiModelPropertiesGeneric.DESCRPTION),
+ optionalRequestFieldWithPath("successCondition")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION),
+ optionalRequestFieldWithPath("successCondition.condition")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_CONDITION)
+ .attributes(key("value").value("['threshold']")),
+ optionalRequestFieldWithPath("successCondition.expression")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_EXP),
+ optionalRequestFieldWithPath("successAction")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION),
+ optionalRequestFieldWithPath("successAction.action")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_ACTION)
+ .attributes(key("value").value("['nextgroup']")),
+ optionalRequestFieldWithPath("successAction.expression")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_EXP),
+ optionalRequestFieldWithPath("errorCondition")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION),
+ optionalRequestFieldWithPath("errorCondition.condition")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_CONDITION)
+ .attributes(key("value").value("['threshold']")),
+ optionalRequestFieldWithPath("errorCondition.expression")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_EXP),
+ optionalRequestFieldWithPath("errorAction")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION),
+ optionalRequestFieldWithPath("errorAction.action")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_ACTION)
+ .attributes(key("value").value("['pause']")),
+ optionalRequestFieldWithPath("errorAction.expression")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_EXP),
+ requestFieldWithPath("groups").description(MgmtApiModelProperties.ROLLOUT_GROUPS),
+ requestFieldWithPath("groups[].name").description(ApiModelPropertiesGeneric.NAME),
+ requestFieldWithPath("groups[].description")
+ .description(ApiModelPropertiesGeneric.DESCRPTION),
+ optionalRequestFieldWithPath("groups[].targetFilterQuery")
+ .description(MgmtApiModelProperties.ROLLOUT_GROUP_FILTER_QUERY),
+ optionalRequestFieldWithPath("groups[].targetPercentage")
+ .description(MgmtApiModelProperties.ROLLOUT_GROUP_TARGET_PERCENTAGE)
+ .attributes(key("value").value("0..100")),
+ optionalRequestFieldWithPath("groups[].successCondition")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION),
+ optionalRequestFieldWithPath("groups[].successCondition.condition")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_CONDITION)
+ .attributes(key("value").value("['threshold']")),
+ optionalRequestFieldWithPath("groups[].successCondition.expression")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_EXP),
+ optionalRequestFieldWithPath("groups[].successAction")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION),
+ optionalRequestFieldWithPath("groups[].successAction.action")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_ACTION)
+ .attributes(key("value").value("['nextgroup']")),
+ optionalRequestFieldWithPath("groups[].successAction.expression")
+ .description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_EXP),
+ optionalRequestFieldWithPath("groups[].errorCondition")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION),
+ optionalRequestFieldWithPath("groups[].errorCondition.condition")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_CONDITION)
+ .attributes(key("value").value("['threshold']")),
+ optionalRequestFieldWithPath("groups[].errorCondition.expression")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_EXP),
+ optionalRequestFieldWithPath("groups[].errorAction")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION),
+ optionalRequestFieldWithPath("groups[].errorAction.action")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_ACTION)
+ .attributes(key("value").value("['pause']")),
+ optionalRequestFieldWithPath("groups[].errorAction.expression")
+ .description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_EXP)),
getRolloutResponseFields(false, true)));
}
@@ -626,11 +635,13 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
private Rollout createRolloutEntity() {
testdataFactory.createTargets(20, "exampleTarget");
- final Rollout rollout = rolloutManagement.create(
- entityFactory.rollout().create().name("exampleRollout")
- .targetFilterQuery("controllerId==exampleTarget*").set(testdataFactory.createDistributionSet()),
- 10, new RolloutGroupConditionBuilder().withDefaults()
- .successCondition(RolloutGroupSuccessCondition.THRESHOLD, "10").build());
+ final RolloutCreate rolloutCreate = entityFactory.rollout().create().name("exampleRollout")
+ .targetFilterQuery("controllerId==exampleTarget*").set(testdataFactory.createDistributionSet());
+ if (isMultiAssignmentsEnabled()) {
+ rolloutCreate.weight(400);
+ }
+ final Rollout rollout = rolloutManagement.create(rolloutCreate, 10, new RolloutGroupConditionBuilder()
+ .withDefaults().successCondition(RolloutGroupSuccessCondition.THRESHOLD, "10").build());
// Run here, because Scheduler is disabled during tests
rolloutManagement.handleRollouts();
diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetFilterQueriesResourceDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetFilterQueriesResourceDocumentationTest.java
index 85b846fe6..e1f130abb 100644
--- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetFilterQueriesResourceDocumentationTest.java
+++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetFilterQueriesResourceDocumentationTest.java
@@ -31,6 +31,7 @@ import org.eclipse.hawkbit.rest.documentation.AbstractApiRestDocumentation;
import org.eclipse.hawkbit.rest.documentation.ApiModelPropertiesGeneric;
import org.eclipse.hawkbit.rest.documentation.MgmtApiModelProperties;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
+import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.MediaType;
@@ -183,15 +184,16 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
@Test
@Description("Handles the POST request of setting a distribution set for auto assignment within SP. Required Permission: CREATE_TARGET.")
public void postAutoAssignDS() throws Exception {
+ enableMultiAssignments();
final TargetFilterQuery tfq = createTargetFilterQuery();
final DistributionSet distributionSet = createDistributionSet();
- final String filterByDistSet = "{\"id\":\"" + distributionSet.getId() + "\", \"type\":\""
- + MgmtActionType.SOFT.getName() + "\"}";
+ final String autoAssignBody = new JSONObject().put("id", distributionSet.getId())
+ .put("type", MgmtActionType.SOFT.getName()).put("weight", 200).toString();
this.mockMvc
.perform(
post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/{targetFilterQueryId}/autoAssignDS",
- tfq.getId()).contentType(MediaType.APPLICATION_JSON).content(filterByDistSet))
+ tfq.getId()).contentType(MediaType.APPLICATION_JSON).content(autoAssignBody.toString()))
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andDo(this.document.document(
pathParameters(parameterWithName("targetFilterQueryId")
@@ -199,7 +201,10 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
requestFields(requestFieldWithPath("id").description(MgmtApiModelProperties.DS_ID),
optionalRequestFieldWithPath("type")
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE)
- .attributes(key("value").value("['forced', 'soft', 'downloadonly']"))),
+ .attributes(key("value").value("['forced', 'soft', 'downloadonly']")),
+ requestFieldWithPathMandatoryInMultiAssignMode("weight")
+ .description(MgmtApiModelProperties.RESULTING_ACTIONS_WEIGHT)
+ .attributes(key("value").value("0 - 1000"))),
getResponseFieldTargetFilterQuery(false)));
}
@@ -227,6 +232,9 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
fieldWithPath(arrayPrefix + "autoAssignActionType")
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE).type(JsonFieldType.STRING.toString())
.attributes(key("value").value("['forced', 'soft', 'downloadonly']")),
+ fieldWithPath(arrayPrefix + "autoAssignWeight")
+ .description(MgmtApiModelProperties.RESULTING_ACTIONS_WEIGHT)
+ .type(JsonFieldType.NUMBER.toString()),
fieldWithPath(arrayPrefix + "createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
fieldWithPath(arrayPrefix + "createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
fieldWithPath(arrayPrefix + "lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT),
@@ -235,7 +243,8 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
.description(MgmtApiModelProperties.TARGET_FILTER_QUERY_LINK_AUTO_ASSIGN_DS));
}
- private String createTargetFilterQueryJson(final String name, final String query) throws JsonProcessingException {
+ private String createTargetFilterQueryJson(final String name, final String query)
+ throws JsonProcessingException {
final Map target = new HashMap<>();
target.put("name", name);
target.put("query", query);
@@ -249,6 +258,7 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
private TargetFilterQuery createTargetFilterQueryWithDS(final DistributionSet distributionSet) {
final TargetFilterQuery targetFilterQuery = createTargetFilterQuery();
- return targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), distributionSet.getId());
+ return targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
+ .updateAutoAssign(targetFilterQuery.getId()).ds(distributionSet.getId()));
}
}
diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetResourceDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetResourceDocumentationTest.java
index d9da9d456..189b801ad 100644
--- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetResourceDocumentationTest.java
+++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetResourceDocumentationTest.java
@@ -203,6 +203,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test
@Description("Handles the GET request of retrieving the full action history of a specific target. Required Permission: READ_TARGET.")
public void getActionsFromTarget() throws Exception {
+ enableMultiAssignments();
generateActionForTarget(targetId);
mockMvc.perform(
@@ -229,12 +230,14 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
.description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
.attributes(key("value").value("['finished', 'pending']")),
fieldWithPath("content[]._links").description(MgmtApiModelProperties.LINK_TO_ACTION),
- fieldWithPath("content[].id").description(MgmtApiModelProperties.ACTION_ID))));
+ fieldWithPath("content[].id").description(MgmtApiModelProperties.ACTION_ID),
+ fieldWithPath("content[].weight").description(MgmtApiModelProperties.ACTION_WEIGHT))));
}
@Test
@Description("Handles the GET request of retrieving the full action history of a specific target with maintenance window. Required Permission: READ_TARGET.")
public void getActionsFromTargetWithMaintenanceWindow() throws Exception {
+ enableMultiAssignments();
generateActionForTarget(targetId, true, false, getTestSchedule(2), getTestDuration(1), getTestTimeZone());
mockMvc.perform(
@@ -262,6 +265,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
.attributes(key("value").value("['finished', 'pending']")),
fieldWithPath("content[]._links").description(MgmtApiModelProperties.LINK_TO_ACTION),
fieldWithPath("content[].id").description(MgmtApiModelProperties.ACTION_ID),
+ fieldWithPath("content[].weight").description(MgmtApiModelProperties.ACTION_WEIGHT),
fieldWithPath("content[].maintenanceWindow")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW),
fieldWithPath("content[].maintenanceWindow.schedule")
@@ -317,6 +321,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test
@Description("Handles the GET request of retrieving a specific action on a specific target. Required Permission: READ_TARGET.")
public void getActionFromTarget() throws Exception {
+ enableMultiAssignments();
final Action action = generateActionForTarget(targetId, true, true);
assertThat(deploymentManagement.findAction(action.getId()).get().getActionType())
.isEqualTo(ActionType.TIMEFORCED);
@@ -330,6 +335,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
responseFields(fieldWithPath("createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
fieldWithPath("createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
fieldWithPath("id").description(MgmtApiModelProperties.ACTION_ID),
+ fieldWithPath("weight").description(MgmtApiModelProperties.ACTION_WEIGHT),
fieldWithPath("lastModifiedBy").description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY)
.type("String"),
fieldWithPath("lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT)
@@ -351,6 +357,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test
@Description("Handles the GET request of retrieving a specific action on a specific target. Required Permission: READ_TARGET.")
public void getActionFromTargetWithMaintenanceWindow() throws Exception {
+ enableMultiAssignments();
final Action action = generateActionForTarget(targetId, true, true, getTestSchedule(2), getTestDuration(1),
getTestTimeZone());
@@ -363,6 +370,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
responseFields(fieldWithPath("createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
fieldWithPath("createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
fieldWithPath("id").description(MgmtApiModelProperties.ACTION_ID),
+ fieldWithPath("weight").description(MgmtApiModelProperties.ACTION_WEIGHT),
fieldWithPath("lastModifiedBy").description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY)
.type("String"),
fieldWithPath("lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT)
@@ -514,6 +522,9 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
.description(MgmtApiModelProperties.OFFLINE_UPDATE).optional()),
requestFields(
requestFieldWithPath("id").description(ApiModelPropertiesGeneric.ITEM_ID),
+ requestFieldWithPathMandatoryInMultiAssignMode("weight")
+ .description(MgmtApiModelProperties.ASSIGNMENT_WEIGHT)
+ .type(JsonFieldType.NUMBER).attributes(key("value").value("0 - 1000")),
optionalRequestFieldWithPath("forcetime").description(MgmtApiModelProperties.FORCETIME),
optionalRequestFieldWithPath("maintenanceWindow")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW),
@@ -537,12 +548,12 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
final long forceTime = System.currentTimeMillis();
final JSONArray body = new JSONArray();
- body.put(new JSONObject().put("id", sets.get(1).getId()).put("type", "timeforced")
+ body.put(new JSONObject().put("id", sets.get(1).getId()).put("weight", 500).put("type", "timeforced")
.put("forcetime", forceTime)
.put("maintenanceWindow", new JSONObject().put("schedule", getTestSchedule(100))
.put("duration", getTestDuration(10)).put("timezone", getTestTimeZone())))
.toString();
- body.put(new JSONObject().put("id", sets.get(0).getId()).put("type", "forced"));
+ body.put(new JSONObject().put("id", sets.get(0).getId()).put("type", "forced").put("weight", 800));
enableMultiAssignments();
mockMvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/"
@@ -555,6 +566,9 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
.description(MgmtApiModelProperties.OFFLINE_UPDATE).optional()),
requestFields(
requestFieldWithPath("[].id").description(ApiModelPropertiesGeneric.ITEM_ID),
+ requestFieldWithPathMandatoryInMultiAssignMode("[].weight")
+ .description(MgmtApiModelProperties.ASSIGNMENT_WEIGHT)
+ .attributes(key("value").value("0 - 1000")),
optionalRequestFieldWithPath("[].forcetime")
.description(MgmtApiModelProperties.FORCETIME),
optionalRequestFieldWithPath("[].maintenanceWindow")
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectWindow.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectWindow.java
index ad3a9c59f..23fb6726a 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectWindow.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/DistributionSetSelectWindow.java
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.ui.filtermanagement;
import java.util.function.Consumer;
+import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -54,6 +55,7 @@ public class DistributionSetSelectWindow implements CommonDialogWindow.SaveDialo
private final EventBus.UIEventBus eventBus;
private final TargetManagement targetManagement;
private final TargetFilterQueryManagement targetFilterQueryManagement;
+ private final EntityFactory entityFactory;
private CheckBox checkBox;
private ActionTypeOptionGroupAutoAssignmentLayout actionTypeOptionGroupLayout;
@@ -62,12 +64,13 @@ public class DistributionSetSelectWindow implements CommonDialogWindow.SaveDialo
DistributionSetSelectWindow(final VaadinMessageSource i18n, final UIEventBus eventBus,
final UINotification notification, final TargetManagement targetManagement,
- final TargetFilterQueryManagement targetFilterQueryManagement) {
+ final TargetFilterQueryManagement targetFilterQueryManagement, final EntityFactory entityFactory) {
this.i18n = i18n;
this.notification = notification;
this.eventBus = eventBus;
this.targetManagement = targetManagement;
this.targetFilterQueryManagement = targetFilterQueryManagement;
+ this.entityFactory = entityFactory;
}
private VerticalLayout initView() {
@@ -197,7 +200,8 @@ public class DistributionSetSelectWindow implements CommonDialogWindow.SaveDialo
if (dsId != null) {
confirmWithConsequencesDialog(tfq, dsId, actionType);
} else {
- targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryId, null);
+ targetFilterQueryManagement.updateAutoAssignDS(
+ entityFactory.targetFilterQuery().updateAutoAssign(targetFilterQueryId).ds(null));
eventBus.publish(this, CustomFilterUIEvent.UPDATED_TARGET_FILTER_QUERY);
}
}
@@ -206,7 +210,8 @@ public class DistributionSetSelectWindow implements CommonDialogWindow.SaveDialo
final ActionType actionType) {
final ConfirmConsequencesDialog dialog = new ConfirmConsequencesDialog(tfq, dsId, accepted -> {
if (accepted) {
- targetFilterQueryManagement.updateAutoAssignDSWithActionType(tfq.getId(), dsId, actionType);
+ targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
+ .updateAutoAssign(tfq.getId()).ds(dsId).actionType(actionType));
eventBus.publish(this, CustomFilterUIEvent.UPDATED_TARGET_FILTER_QUERY);
}
});
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterManagementView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterManagementView.java
index 0c1a000ca..0ea2cbbf9 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterManagementView.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterManagementView.java
@@ -70,7 +70,7 @@ public class FilterManagementView extends VerticalLayout implements View {
final AutoCompleteTextFieldComponent queryTextField, final TargetManagement targetManagement) {
this.targetFilterHeader = new TargetFilterHeader(eventBus, filterManagementUIState, permissionChecker, i18n);
this.targetFilterTable = new TargetFilterTable(i18n, notification, eventBus, filterManagementUIState,
- targetFilterQueryManagement, targetManagement, permissionChecker);
+ targetFilterQueryManagement, targetManagement, permissionChecker, entityFactory);
this.createNewFilterHeader = new CreateOrUpdateFilterHeader(i18n, eventBus, filterManagementUIState,
targetFilterQueryManagement, permissionChecker, notification, uiProperties, entityFactory,
queryTextField);
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java
index 1c2e27298..06386ef5a 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java
@@ -14,6 +14,7 @@ import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.im.authentication.SpPermission;
+import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -80,7 +81,7 @@ public class TargetFilterTable extends Table {
public TargetFilterTable(final VaadinMessageSource i18n, final UINotification notification,
final UIEventBus eventBus, final FilterManagementUIState filterManagementUIState,
final TargetFilterQueryManagement targetFilterQueryManagement, final TargetManagement targetManagement,
- final SpPermissionChecker permChecker) {
+ final SpPermissionChecker permChecker, final EntityFactory entityFactory) {
this.i18n = i18n;
this.notification = notification;
this.eventBus = eventBus;
@@ -89,7 +90,7 @@ public class TargetFilterTable extends Table {
this.permChecker = permChecker;
this.dsSelectWindow = new DistributionSetSelectWindow(i18n, eventBus, notification, targetManagement,
- targetFilterQueryManagement);
+ targetFilterQueryManagement, entityFactory);
setStyleName("sp-table");
setSizeFull();
diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/TargetAssignmentOperations.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/TargetAssignmentOperations.java
index 59f101f11..240cf9126 100644
--- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/TargetAssignmentOperations.java
+++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/TargetAssignmentOperations.java
@@ -105,7 +105,7 @@ public final class TargetAssignmentOperations {
final String maintenanceTimeZone = maintenanceWindowLayout.getMaintenanceTimeZone();
final Set dsIds = distributionSets.stream().map(DistributionSet::getId).collect(Collectors.toSet());
- final List deploymentRequests = new ArrayList<>();
+ final List deploymentRequests = new ArrayList<>();
dsIds.forEach(dsId -> targets.forEach(t -> {
final DeploymentRequestBuilder request = DeploymentManagement.deploymentRequest(t.getControllerId(), dsId)
.setActionType(actionType).setForceTime(forcedTimeStamp);