Prioritisation of assignments via mgmt-API (#895)
* Updating the schema for targetfilterquery and rollout * Updating the weight validation logic and tests * Make weight optional * Fix existing multi assignment tests by adding weight, remove weight from TargetFilterQuery * Add weight validation tests, fix tests * Add mgmt api tests for assignment and getting action with weight * Add management layer validation and tests for creating rollouts with weight * Fix amqp test, add repo level validation to resource tests * Add weight to rollout mgmt-api and tests * Add weight to mgmt api target Filter create and update * Add target filter auto assign weight. disable enforcement of setting a weight in multiassign mode * Remove ignored tests, fix api doc * Fix minor findings * Fix findings * Remove hardcoded min weight * Add docu text, fix findings * Fix api documentation * Expose weight via DMF * Expose actions according to weight via ddi * Fix documentation * Add method to get actions ordered by weight to deploymentManagement * Updating the schema for targetfilterquery and rollout * Updated the indentation * Updated the helper class, fixed the randomUID in test factory * Updated the class name with prefix JPA * Adding the missing License for WeightValidationHelper class * Adding documentation to the dmf api on weight * Removed the merger markers * Updated the class name * Removed the redundant method * Addressed final PR comments * Updated the missing testcase with latest default weight value * Reverting the default value of weight back to 1000 and updated tests Signed-off-by: Shruthi Manavalli Ramanna <shruthimanavalli.ramanna@bosch-si.com> Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>
This commit is contained in:
committed by
Stefan Behl
parent
09f2d8a481
commit
9cb5d31396
@@ -364,6 +364,8 @@ type=EVENT <br /> tenant=tenant123 <br /> thingId=abc <br /> 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 <br /> tenant=tenant123 <br /> thingId=abc <br /> topic=MULTI\_ACTI
|
||||
```json
|
||||
[{
|
||||
"topic": "DOWNLOAD_AND_INSTALL",
|
||||
"weight": 600,
|
||||
"action": {
|
||||
"actionId":137,
|
||||
"targetSecurityToken":"bH7XXAprK1ChnLfKSdtlsp7NOlPnZAYY",
|
||||
@@ -486,6 +491,7 @@ type=EVENT <br /> tenant=tenant123 <br /> thingId=abc <br /> topic=MULTI\_ACTI
|
||||
},
|
||||
{
|
||||
"topic": "DOWNLOAD",
|
||||
"weight": 500,
|
||||
"action": {
|
||||
"actionId":138,
|
||||
"targetSecurityToken":"bH7XXAprK1ChnLfKSdtlsp7NOlPnZAYY",
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -21,7 +21,11 @@ public enum ActionFields implements FieldNameProvider, FieldValueConverter<Actio
|
||||
/**
|
||||
* The id field.
|
||||
*/
|
||||
ID("id");
|
||||
ID("id"),
|
||||
/**
|
||||
* The weight field.
|
||||
*/
|
||||
WEIGHT("weight");
|
||||
|
||||
private static final String ACTIVE = "pending";
|
||||
private static final String INACTIVE = "finished";
|
||||
|
||||
@@ -176,8 +176,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
.filter(target -> IpUtil.isAmqpUri(target.getAddress())).forEach(target -> {
|
||||
|
||||
final List<Action> 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,
|
||||
|
||||
@@ -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<Action> actions = controllerManagement
|
||||
.findActiveActionsByTarget(PageRequest.of(0, MAX_ACTION_COUNT), target.getControllerId()).getContent();
|
||||
.findActiveActionsWithHighestWeight(target.getControllerId(), MAX_ACTION_COUNT);
|
||||
|
||||
final Set<DistributionSet> 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<Action> actionOptional = controllerManagement
|
||||
.findOldestActiveActionByTarget(target.getControllerId());
|
||||
.findActiveActionWithHighestWeight(target.getControllerId());
|
||||
|
||||
if (!actionOptional.isPresent()) {
|
||||
return;
|
||||
|
||||
@@ -181,17 +181,19 @@ public class AmqpMessageHandlerServiceTest {
|
||||
@Description("Tests the creation of a target/thing by calling the same method that incoming RabbitMQ messages would access.")
|
||||
public void createThing() {
|
||||
final String knownThingId = "1";
|
||||
final MessageProperties messageProperties = createMessageProperties(MessageType.THING_CREATED);
|
||||
messageProperties.setHeader(MessageHeaderKey.THING_ID, "1");
|
||||
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
|
||||
|
||||
final Target targetMock = mock(Target.class);
|
||||
|
||||
targetIdCaptor = ArgumentCaptor.forClass(String.class);
|
||||
uriCaptor = ArgumentCaptor.forClass(URI.class);
|
||||
final ArgumentCaptor<String> targetIdCaptor = ArgumentCaptor.forClass(String.class);
|
||||
final ArgumentCaptor<URI> uriCaptor = ArgumentCaptor.forClass(URI.class);
|
||||
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotExist(targetIdCaptor.capture(),
|
||||
uriCaptor.capture())).thenReturn(targetMock);
|
||||
when(controllerManagementMock.findOldestActiveActionByTarget(any())).thenReturn(Optional.empty());
|
||||
when(controllerManagementMock.findActiveActionWithHighestWeight(any())).thenReturn(Optional.empty());
|
||||
|
||||
amqpMessageHandlerService.onMessage(createMessage(new byte[0], getThingCreatedMessageProperties(knownThingId)),
|
||||
MessageType.THING_CREATED.name(), TENANT, VIRTUAL_HOST);
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, VIRTUAL_HOST);
|
||||
|
||||
// verify
|
||||
assertThat(targetIdCaptor.getValue()).as("Thing id is wrong").isEqualTo(knownThingId);
|
||||
@@ -214,7 +216,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
|
||||
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotExist(targetIdCaptor.capture(),
|
||||
uriCaptor.capture(), targetNameCaptor.capture())).thenReturn(targetMock);
|
||||
when(controllerManagementMock.findOldestActiveActionByTarget(any())).thenReturn(Optional.empty());
|
||||
when(controllerManagementMock.findActiveActionWithHighestWeight(any())).thenReturn(Optional.empty());
|
||||
|
||||
amqpMessageHandlerService.onMessage(
|
||||
createMessage(targetProperties, getThingCreatedMessageProperties(knownThingId)),
|
||||
@@ -488,7 +490,8 @@ public class AmqpMessageHandlerServiceTest {
|
||||
when(create.status(any())).thenReturn(create);
|
||||
when(entityFactoryMock.actionStatus()).thenReturn(builder);
|
||||
// for the test the same action can be used
|
||||
when(controllerManagementMock.findOldestActiveActionByTarget(any())).thenReturn(Optional.of(action));
|
||||
when(controllerManagementMock.findActiveActionWithHighestWeight(any()))
|
||||
.thenReturn(Optional.of(action));
|
||||
|
||||
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
|
||||
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
||||
|
||||
@@ -188,16 +188,17 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
||||
final DmfDownloadAndUpdateRequest downloadAndUpdateRequest = (DmfDownloadAndUpdateRequest) getDmfClient()
|
||||
.getMessageConverter().fromMessage(replyMessage);
|
||||
|
||||
Assert.assertThat(dsModules,
|
||||
SoftwareModuleJsonMatcher.containsExactly(downloadAndUpdateRequest.getSoftwareModules()));
|
||||
assertDmfDownloadAndUpdateRequest(downloadAndUpdateRequest, dsModules, controllerId);
|
||||
}
|
||||
|
||||
downloadAndUpdateRequest.getSoftwareModules()
|
||||
protected void assertDmfDownloadAndUpdateRequest(final DmfDownloadAndUpdateRequest request,
|
||||
final Set<SoftwareModule> softwareModules, final String controllerId) {
|
||||
Assert.assertThat(softwareModules, SoftwareModuleJsonMatcher.containsExactly(request.getSoftwareModules()));
|
||||
request.getSoftwareModules()
|
||||
.forEach(dmfModule -> assertThat(dmfModule.getMetadata()).containsExactly(
|
||||
new DmfMetadata(TestdataFactory.VISIBLE_SM_MD_KEY, TestdataFactory.VISIBLE_SM_MD_VALUE)));
|
||||
|
||||
final Target updatedTarget = waitUntilIsPresent(() -> targetManagement.getByControllerID(controllerId));
|
||||
|
||||
assertThat(updatedTarget.getSecurityToken()).isEqualTo(downloadAndUpdateRequest.getTargetSecurityToken());
|
||||
assertThat(updatedTarget.getSecurityToken()).isEqualTo(request.getTargetSecurityToken());
|
||||
}
|
||||
|
||||
protected void assertDownloadAndInstallMessage(final Set<SoftwareModule> softwareModules,
|
||||
|
||||
@@ -15,6 +15,7 @@ import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ON
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -26,11 +27,13 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfActionRequest;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadAndUpdateRequest;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfMultiActionRequest;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfMultiActionRequest.DmfMultiActionElement;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.event.remote.MultiActionEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
|
||||
@@ -49,8 +52,10 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedE
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -100,7 +105,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
final String controllerId = TARGET_PREFIX + "sendDownloadStatusBeforeWindowStartTime";
|
||||
|
||||
registerAndAssertTargetWithExistingTenant(controllerId);
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
testdataFactory.addSoftwareModuleMetadata(distributionSet);
|
||||
assignDistributionSetWithMaintenanceWindow(distributionSet.getId(), controllerId, getTestSchedule(2),
|
||||
getTestDuration(10), getTestTimeZone());
|
||||
@@ -122,7 +127,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
final String controllerId = TARGET_PREFIX + "sendDAndIStatusMessageDuringWindow";
|
||||
|
||||
registerAndAssertTargetWithExistingTenant(controllerId);
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
testdataFactory.addSoftwareModuleMetadata(distributionSet);
|
||||
assignDistributionSetWithMaintenanceWindow(distributionSet.getId(), controllerId, getTestSchedule(-5),
|
||||
getTestDuration(10), getTestTimeZone());
|
||||
@@ -145,7 +150,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
final String controllerId = TARGET_PREFIX + "assignDistributionSetMultipleTimes";
|
||||
|
||||
final DistributionSetAssignmentResult assignmentResult = registerTargetAndAssignDistributionSet(controllerId);
|
||||
final DistributionSet distributionSet2 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||
final DistributionSet distributionSet2 = testdataFactory.createDistributionSet();
|
||||
testdataFactory.addSoftwareModuleMetadata(distributionSet2);
|
||||
assignDistributionSet(distributionSet2.getId(), controllerId);
|
||||
assertDownloadAndInstallMessage(distributionSet2.getModules(), controllerId);
|
||||
@@ -171,17 +176,74 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
final String controllerId = TARGET_PREFIX + "assignMultipleDsInMultiAssignMode";
|
||||
registerAndAssertTargetWithExistingTenant(controllerId);
|
||||
|
||||
final Long actionId1 = assignNewDsToTarget(controllerId);
|
||||
final Long actionId1 = assignNewDsToTarget(controllerId, 450);
|
||||
final Entry<Long, EventTopic> action1Install = new SimpleEntry<>(actionId1, EventTopic.DOWNLOAD_AND_INSTALL);
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
|
||||
assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install));
|
||||
|
||||
final Long actionId2 = assignNewDsToTarget(controllerId);
|
||||
final Long actionId2 = assignNewDsToTarget(controllerId, 111);
|
||||
final Entry<Long, EventTopic> action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL);
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
|
||||
assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install, action2Install));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify payload of multi action messages.")
|
||||
public void assertMultiActionMessagePayloads() {
|
||||
final int expectedWeightIfNotSet = 1000;
|
||||
final int weight1 = 600;
|
||||
final String controllerId = UUID.randomUUID().toString();
|
||||
registerAndAssertTargetWithExistingTenant(controllerId);
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
testdataFactory.addSoftwareModuleMetadata(ds);
|
||||
|
||||
final Long installActionId = makeAssignment(DeploymentManagement.deploymentRequest(controllerId, ds.getId())
|
||||
.setActionType(ActionType.FORCED).build()).getAssignedEntity().get(0).getId();
|
||||
enableMultiAssignments();
|
||||
final Long downloadActionId = makeAssignment(DeploymentManagement.deploymentRequest(controllerId, ds.getId())
|
||||
.setActionType(ActionType.DOWNLOAD_ONLY).setWeight(weight1).build()).getAssignedEntity().get(0).getId();
|
||||
final Long cancelActionId = makeAssignment(
|
||||
DeploymentManagement.deploymentRequest(controllerId, ds.getId()).setWeight(DEFAULT_TEST_WEIGHT).build())
|
||||
.getAssignedEntity().get(0).getId();
|
||||
// make sure the latest message in the queue is the one triggered by the
|
||||
// cancellation
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.DOWNLOAD_AND_INSTALL, EventTopic.MULTI_ACTION,
|
||||
EventTopic.MULTI_ACTION);
|
||||
deploymentManagement.cancelAction(cancelActionId);
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
|
||||
|
||||
final List<DmfMultiActionElement> multiActionMessages = getLatestMultiActionMessages(controllerId);
|
||||
assertThat(multiActionMessages).hasSize(3);
|
||||
final DmfMultiActionElement installMessage = multiActionMessages.stream()
|
||||
.filter(message -> message.getTopic().equals(EventTopic.DOWNLOAD_AND_INSTALL)).findFirst().get();
|
||||
final DmfMultiActionElement downloadMessage = multiActionMessages.stream()
|
||||
.filter(message -> message.getTopic().equals(EventTopic.DOWNLOAD)).findFirst().get();
|
||||
final DmfMultiActionElement cancelMessage = multiActionMessages.stream()
|
||||
.filter(message -> message.getTopic().equals(EventTopic.CANCEL_DOWNLOAD)).findFirst().get();
|
||||
assertThat(installMessage.getWeight()).isEqualTo(expectedWeightIfNotSet);
|
||||
assertThat(downloadMessage.getWeight()).isEqualTo(weight1);
|
||||
assertThat(cancelMessage.getWeight()).isEqualTo(DEFAULT_TEST_WEIGHT);
|
||||
|
||||
assertThat(installMessage.getAction()).isExactlyInstanceOf(DmfDownloadAndUpdateRequest.class)
|
||||
.hasFieldOrPropertyWithValue("actionId", installActionId);
|
||||
assertThat(downloadMessage.getAction()).isExactlyInstanceOf(DmfDownloadAndUpdateRequest.class)
|
||||
.hasFieldOrPropertyWithValue("actionId", downloadActionId);
|
||||
assertThat(cancelMessage.getAction()).isExactlyInstanceOf(DmfActionRequest.class)
|
||||
.hasFieldOrPropertyWithValue("actionId", cancelActionId);
|
||||
assertDmfDownloadAndUpdateRequest((DmfDownloadAndUpdateRequest) installMessage.getAction(), ds.getModules(),
|
||||
controllerId);
|
||||
assertDmfDownloadAndUpdateRequest((DmfDownloadAndUpdateRequest) downloadMessage.getAction(), ds.getModules(),
|
||||
controllerId);
|
||||
}
|
||||
|
||||
private List<DmfMultiActionElement> getLatestMultiActionMessages(final String expectedControllerId) {
|
||||
final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION);
|
||||
assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID))
|
||||
.isEqualTo(expectedControllerId);
|
||||
return ((DmfMultiActionRequest) getDmfClient().getMessageConverter().fromMessage(multiactionMessage))
|
||||
.getElements();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Handle cancelation process of an action in multi assignment mode.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@@ -197,8 +259,8 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
final String controllerId = TARGET_PREFIX + "cancelActionInMultiAssignMode";
|
||||
registerAndAssertTargetWithExistingTenant(controllerId);
|
||||
|
||||
final long actionId1 = assignNewDsToTarget(controllerId);
|
||||
final long actionId2 = assignNewDsToTarget(controllerId);
|
||||
final long actionId1 = assignNewDsToTarget(controllerId, 675);
|
||||
final long actionId2 = assignNewDsToTarget(controllerId, 343);
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION);
|
||||
deploymentManagement.cancelAction(actionId1);
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
|
||||
@@ -229,8 +291,8 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
final String controllerId = TARGET_PREFIX + "finishActionInMultiAssignMode";
|
||||
registerAndAssertTargetWithExistingTenant(controllerId);
|
||||
|
||||
final long actionId1 = assignNewDsToTarget(controllerId);
|
||||
final long actionId2 = assignNewDsToTarget(controllerId);
|
||||
final long actionId1 = assignNewDsToTarget(controllerId, 66);
|
||||
final long actionId2 = assignNewDsToTarget(controllerId, 767);
|
||||
final Entry<Long, EventTopic> action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL);
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION);
|
||||
|
||||
@@ -254,11 +316,11 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
enableMultiAssignments();
|
||||
final String controllerId = TARGET_PREFIX + "assignDsMultipleTimesInMultiAssignMode";
|
||||
registerAndAssertTargetWithExistingTenant(controllerId);
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||
final Long dsId = testdataFactory.createDistributionSet().getId();
|
||||
|
||||
final Long actionId1 = getFirstAssignedActionId(assignDistributionSet(ds.getId(), controllerId));
|
||||
final Long actionId1 = getFirstAssignedAction(assignDistributionSet(dsId, controllerId, 344)).getId();
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
|
||||
final Long actionId2 = getFirstAssignedActionId(assignDistributionSet(ds.getId(), controllerId));
|
||||
final Long actionId2 = getFirstAssignedAction(assignDistributionSet(dsId, controllerId, 775)).getId();
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
|
||||
|
||||
final Entry<Long, EventTopic> action1Install = new SimpleEntry<>(actionId1, EventTopic.DOWNLOAD_AND_INSTALL);
|
||||
@@ -272,8 +334,14 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
}
|
||||
|
||||
private Long assignNewDsToTarget(final String controllerId) {
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||
final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds.getId(), controllerId));
|
||||
return assignNewDsToTarget(controllerId, null);
|
||||
}
|
||||
|
||||
private Long assignNewDsToTarget(final String controllerId, final Integer weight) {
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
final Long actionId = getFirstAssignedActionId(
|
||||
assignDistributionSet(ds.getId(), Collections.singletonList(controllerId), ActionType.FORCED,
|
||||
RepositoryModelConstants.NO_FORCE_TIME, weight));
|
||||
waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.PENDING);
|
||||
return actionId;
|
||||
}
|
||||
@@ -296,15 +364,15 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
final String controllerId = TARGET_PREFIX + "startRolloutsWithSameDsInMultiAssignMode";
|
||||
|
||||
registerAndAssertTargetWithExistingTenant(controllerId);
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
final Set<Long> smIds = getSoftwareModuleIds(ds);
|
||||
final String filterQuery = "controllerId==" + controllerId;
|
||||
|
||||
createAndStartRollout(ds, filterQuery);
|
||||
createAndStartRollout(ds, filterQuery, 122);
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
|
||||
assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds));
|
||||
|
||||
createAndStartRollout(ds, filterQuery);
|
||||
createAndStartRollout(ds, filterQuery, 43);
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
|
||||
assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds, smIds));
|
||||
}
|
||||
@@ -327,15 +395,15 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
|
||||
registerAndAssertTargetWithExistingTenant(controllerId);
|
||||
final String filterQuery = "controllerId==" + controllerId;
|
||||
final DistributionSet ds1 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||
final DistributionSet ds1 = testdataFactory.createDistributionSet();
|
||||
final Set<Long> smIds1 = getSoftwareModuleIds(ds1);
|
||||
final DistributionSet ds2 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||
final DistributionSet ds2 = testdataFactory.createDistributionSet();
|
||||
final Set<Long> smIds2 = getSoftwareModuleIds(ds2);
|
||||
|
||||
createAndStartRollout(ds1, filterQuery);
|
||||
createAndStartRollout(ds2, filterQuery);
|
||||
createAndStartRollout(ds1, filterQuery, 12);
|
||||
createAndStartRollout(ds2, filterQuery, 45);
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION);
|
||||
createAndStartRollout(ds1, filterQuery);
|
||||
createAndStartRollout(ds1, filterQuery, 65);
|
||||
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
|
||||
assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds1, smIds2, smIds1));
|
||||
|
||||
@@ -357,8 +425,12 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
}
|
||||
|
||||
private Rollout createAndStartRollout(final DistributionSet ds, final String filterQuery) {
|
||||
return createAndStartRollout(ds, filterQuery, null);
|
||||
}
|
||||
|
||||
private Rollout createAndStartRollout(final DistributionSet ds, final String filterQuery, final Integer weight) {
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables(UUID.randomUUID().toString(), "", 1,
|
||||
filterQuery, ds, "50", "5");
|
||||
filterQuery, ds, "50", "5", ActionType.FORCED, weight);
|
||||
rolloutManagement.start(rollout.getId());
|
||||
rolloutManagement.handleRollouts();
|
||||
return rollout;
|
||||
@@ -483,11 +555,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
}
|
||||
|
||||
private List<Entry<Long, EventTopic>> getLatestMultiActionMessageActions(final String expectedControllerId) {
|
||||
final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION);
|
||||
assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID))
|
||||
.isEqualTo(expectedControllerId);
|
||||
final List<DmfMultiActionElement> multiActionRequest = ((DmfMultiActionRequest) getDmfClient()
|
||||
.getMessageConverter().fromMessage(multiactionMessage)).getElements();
|
||||
final List<DmfMultiActionElement> multiActionRequest = getLatestMultiActionMessages(expectedControllerId);
|
||||
return multiActionRequest.stream()
|
||||
.map(request -> new SimpleEntry<>(request.getAction().getActionId(), request.getTopic()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Long> 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<Action> findOldestActiveActionByTarget(@NotEmpty String controllerId);
|
||||
Optional<Action> 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<Action> findActiveActionsByTarget(@NotNull Pageable pageable, @NotEmpty String controllerId);
|
||||
List<Action> 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<Action> 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<ActionStatus> 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<Target> 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<Target> 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<String> 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.
|
||||
|
||||
@@ -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<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
@NotEmpty List<DeploymentRequest> deploymentRequests);
|
||||
@Valid @NotEmpty List<DeploymentRequest> 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<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
@NotEmpty List<DeploymentRequest> deploymentRequests, String actionMessage);
|
||||
|
||||
/**
|
||||
@Valid @NotEmpty List<DeploymentRequest> 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<Action> 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<Action> 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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<RolloutGroupCreate> groups,
|
||||
Rollout create(@Valid @NotNull RolloutCreate rollout, @NotNull @Valid List<RolloutGroupCreate> 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.
|
||||
|
||||
@@ -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 <code>null</code> 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 <code>null</code> in order to remove it
|
||||
* together with the auto-assign {@link ActionType}
|
||||
* @param actionType
|
||||
* to be updated or <code>null</code> 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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -32,5 +32,4 @@ public interface TargetFilterQueryUpdate {
|
||||
* @return updated builder instance
|
||||
*/
|
||||
TargetFilterQueryUpdate query(@Size(min = 1, max = TargetFilterQuery.QUERY_MAX_SIZE) String query);
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<Integer> 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
|
||||
*/
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Integer> getWeight();
|
||||
|
||||
/**
|
||||
*
|
||||
* State machine for rollout.
|
||||
@@ -197,5 +203,4 @@ public interface Rollout extends NamedEntity {
|
||||
*/
|
||||
DENIED
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Integer> getAutoAssignWeight();
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<RolloutGroup> 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<T> 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<T> extends AbstractNamedEntity
|
||||
return Optional.ofNullable(forcedTime);
|
||||
}
|
||||
|
||||
public Optional<Integer> getWeight() {
|
||||
return Optional.ofNullable(weight);
|
||||
}
|
||||
|
||||
public Optional<Long> getStartAt() {
|
||||
return Optional.ofNullable(startAt);
|
||||
}
|
||||
|
||||
@@ -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<T> 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<T> 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<Integer> getAutoAssignWeight() {
|
||||
return Optional.ofNullable(weight);
|
||||
}
|
||||
|
||||
public Optional<ActionType> 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<T> 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;
|
||||
|
||||
@@ -16,5 +16,4 @@ public class GenericRolloutUpdate extends AbstractRolloutUpdateCreate<RolloutUpd
|
||||
public GenericRolloutUpdate(final Long id) {
|
||||
super.id = id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -212,6 +212,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
final JpaAction actionForTarget = new JpaAction();
|
||||
actionForTarget.setActionType(targetWithActionType.getActionType());
|
||||
actionForTarget.setForcedTime(targetWithActionType.getForceTime());
|
||||
actionForTarget.setWeight(targetWithActionType.getWeight());
|
||||
actionForTarget.setActive(true);
|
||||
actionForTarget.setTarget(target);
|
||||
actionForTarget.setDistributionSet(set);
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
@@ -101,20 +100,33 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
|
||||
List<Action> 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<Action> findFirstByTargetControllerIdAndActive(Sort sort, String controllerId, boolean active);
|
||||
Page<Action> 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<Action> findByTargetControllerIdAndActiveIsTrueAndWeightIsNullOrderByIdAsc(Pageable pageable,
|
||||
String controllerId);
|
||||
|
||||
/**
|
||||
* Checks if an active action exists for given
|
||||
|
||||
@@ -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<Action> findActiveActionsWithHighestWeightConsideringDefault(final String controllerId,
|
||||
final int maxActionCount) {
|
||||
if (!actionRepository.activeActionExistsForControllerId(controllerId)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<Action> 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<Action> 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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<TargetPoll> 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<Action> 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<Action> findActiveActionWithHighestWeight(final String controllerId) {
|
||||
return findActiveActionsWithHighestWeight(controllerId, 1).stream().findFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Action> findActiveActionsByTarget(final Pageable pageable, final String controllerId) {
|
||||
if (!actionRepository.activeActionExistsForControllerId(controllerId)) {
|
||||
return Page.empty();
|
||||
}
|
||||
return actionRepository.findByActiveAndTarget(pageable, controllerId, true);
|
||||
public List<Action> 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());
|
||||
|
||||
@@ -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<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
final List<DeploymentRequest> 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<String> targetIds = targetsWithActionType.stream().map(TargetWithActionType::getControllerId).distinct()
|
||||
.collect(Collectors.toList());
|
||||
final List<String> targetIds = targetsWithActionType.stream().map(TargetWithActionType::getControllerId)
|
||||
.distinct().collect(Collectors.toList());
|
||||
|
||||
final List<JpaTarget> targetEntities = assignmentStrategy.findTargetsForAssignment(targetIds,
|
||||
distributionSetEntity.getId());
|
||||
@@ -285,8 +289,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
return allTargetsAlreadyAssignedResult(distributionSetEntity, targetsWithActionType.size());
|
||||
}
|
||||
|
||||
final List<JpaAction> assignedActions = doAssignDistributionSetToTargets(targetsWithActionType,
|
||||
actionMessage, assignmentStrategy, distributionSetEntity, targetEntities);
|
||||
final List<JpaAction> 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<Action> 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 extends Serializable> T getConfigValue(final String key, final Class<T> valueType) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -97,6 +97,8 @@ public class AutoAssignChecker {
|
||||
|
||||
final Page<TargetFilterQuery> 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<DeploymentRequest> 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<DeploymentRequest> createAssignmentRequests(final String targetFilterQuery, final Long dsId,
|
||||
final ActionType type, final int count) {
|
||||
final ActionType type, final Integer weight, final int count) {
|
||||
final Page<Target> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ public class JpaRolloutCreate extends AbstractRolloutUpdateCreate<RolloutCreate>
|
||||
rollout.setDistributionSet(findDistributionSetAndThrowExceptionIfNotFound(set));
|
||||
rollout.setTargetFilterQuery(targetFilterQuery);
|
||||
rollout.setStartAt(startAt);
|
||||
rollout.setWeight(weight);
|
||||
|
||||
if (actionType != null) {
|
||||
rollout.setActionType(actionType);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<Integer> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Integer> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Integer> 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(
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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<DeploymentRequest> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -105,5 +105,4 @@ public class RemoteIdEventTest extends AbstractRemoteEventTest {
|
||||
// gets added because events inherit from of java.util.EventObject
|
||||
assertThat(underTestCreatedEvent).isEqualToIgnoringGivenFields(event, "source");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -98,5 +98,4 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
|
||||
assertThat(actionProperties).isEqualToComparingFieldByField(new ActionProperties(action));
|
||||
assertThat(underTest.getDistributionSetId()).isEqualTo(action.getDistributionSet().getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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> 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();
|
||||
|
||||
@@ -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<Target> targets = testdataFactory.createTargets(10);
|
||||
final List<String> 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<Target> targets = testdataFactory.createTargets(2);
|
||||
final List<DistributionSet> distributionSets = testdataFactory.createDistributionSets(2);
|
||||
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(distributionSets, targets);
|
||||
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(distributionSets, targets, 34);
|
||||
|
||||
enableMultiAssignments();
|
||||
final List<DistributionSetAssignmentResult> 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<DeploymentRequest> createAssignmentRequests(final Collection<DistributionSet> distributionSets,
|
||||
final Collection<Target> targets, final int weight) {
|
||||
final List<DeploymentRequest> 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<DistributionSet> 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<DeploymentRequest> 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<DeploymentRequest> 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<DeploymentRequest> 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<Long> getUndeployedTargetIDs() {
|
||||
return undeployedTargetIDs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<TotalTargetCountStatus.Status, Long> 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<Action> 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<Action> 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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<Target> 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<Target> targetsF = testdataFactory.createTargets(10, targetDsFIdPref,
|
||||
targetDsFIdPref.concat(" description"));
|
||||
@@ -243,12 +242,10 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
final List<Target> 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<Action> 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<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
|
||||
assertThat(actions).hasSize(amountOfTargets);
|
||||
assertThat(actions).allMatch(action -> !action.getWeight().isPresent());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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==*"));
|
||||
|
||||
|
||||
@@ -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<String> controllerIds,
|
||||
final ActionType actionType, final long forcedTime) {
|
||||
return assignDistributionSet(dsID, controllerIds, actionType, forcedTime, null);
|
||||
}
|
||||
|
||||
protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final List<String> controllerIds,
|
||||
final ActionType actionType, final long forcedTime, final Integer weight) {
|
||||
final List<DeploymentRequest> deploymentRequests = controllerIds.stream()
|
||||
.map(id -> DeploymentManagement.deploymentRequest(id, dsID).setActionType(actionType)
|
||||
.setForceTime(forcedTime).build())
|
||||
.setForceTime(forcedTime).setWeight(weight).build())
|
||||
.collect(Collectors.toList());
|
||||
final List<DistributionSetAssignmentResult> 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<String> targetIds,
|
||||
final int weight) {
|
||||
return assignDistributionSet(dsId, targetIds, ActionType.FORCED, RepositoryModelConstants.NO_FORCE_TIME,
|
||||
weight);
|
||||
}
|
||||
|
||||
protected DistributionSetAssignmentResult makeAssignment(final DeploymentRequest request) {
|
||||
final List<DistributionSetAssignmentResult> 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<DeploymentRequest> createAssignmentRequests(final Collection<DistributionSet> distributionSets,
|
||||
final Collection<Target> targets) {
|
||||
final List<DeploymentRequest> 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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()} <code>false</code>.
|
||||
*
|
||||
*
|
||||
* @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()} <code>false</code>.
|
||||
*
|
||||
*
|
||||
* @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()} <code>false</code>.
|
||||
*
|
||||
*
|
||||
* @param modules
|
||||
* of {@link DistributionSet#getModules()}
|
||||
*
|
||||
* @return {@link DistributionSet} entity.
|
||||
*/
|
||||
public DistributionSet createDistributionSet(final Collection<SoftwareModule> 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()} <code>false</code>.
|
||||
*
|
||||
*
|
||||
* @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<SoftwareModule> 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()} <code>false</code>.
|
||||
*
|
||||
*
|
||||
* @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<DistributionSetTag> 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()}
|
||||
* <code>false</code>.
|
||||
*
|
||||
*
|
||||
* @param number
|
||||
* of {@link DistributionSet}s to create
|
||||
*
|
||||
* @return {@link List} of {@link DistributionSet} entities
|
||||
*/
|
||||
public List<DistributionSet> 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()}
|
||||
* <code>false</code>.
|
||||
*
|
||||
*
|
||||
* @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<DistributionSet> 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()} <code>false</code>.
|
||||
*
|
||||
*
|
||||
* @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<Artifact> 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()}
|
||||
* <code>false</code>.
|
||||
*
|
||||
* 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<SoftwareModuleType> 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<Target> 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<DistributionSetTag> 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<Action> sendUpdateActionStatusToTargets(final Collection<Target> 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<Action> sendUpdateActionStatusToTargets(final Collection<Target> 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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<MgmtRolloutGroup> groups;
|
||||
@@ -124,7 +128,7 @@ public class MgmtRolloutRestRequestBody extends AbstractMgmtRolloutConditionsEnt
|
||||
* @param groups
|
||||
* List of {@link MgmtRolloutGroup}
|
||||
*/
|
||||
public void setGroups(List<MgmtRolloutGroup> groups) {
|
||||
public void setGroups(final List<MgmtRolloutGroup> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,7 @@ public class MgmtTargetFilterQueryRequestBody {
|
||||
return query;
|
||||
}
|
||||
|
||||
public void setQuery(String query) {
|
||||
public void setQuery(final String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -266,7 +266,6 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
|
||||
final List<DistributionSetAssignmentResult> assignmentResults = deployManagament
|
||||
.assignDistributionSets(deploymentRequests);
|
||||
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponse(assignmentResults));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<MgmtTargetFilterQuery> 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<Void> deleteAssignedDistributionSet(@PathVariable("filterId") final Long filterId) {
|
||||
filterManagement.updateAutoAssignDS(filterId, null);
|
||||
filterManagement.updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(null));
|
||||
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@@ -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<MgmtMetadata> toResponseTargetMetadata(final List<TargetMetadata> metadata) {
|
||||
return metadata.stream().map(MgmtTargetMapper::toResponseTargetMetadata).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -555,9 +555,8 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
|
||||
final Set<DistributionSet> 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<Action> actions = deploymentManagement.findActionsAll(PAGE).get().collect(Collectors.toList());
|
||||
assertThat(actions).size().isEqualTo(1);
|
||||
assertThat(actions.get(0).getWeight()).get().isEqualTo(weight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Rollout> rollouts = rolloutManagement.findAll(PAGE, false).getContent();
|
||||
assertThat(rollouts).hasSize(1);
|
||||
assertThat(rollouts.get(0).getWeight()).get().isEqualTo(weight);
|
||||
}
|
||||
|
||||
protected <T> T doWithTimeout(final Callable<T> callable, final SuccessCondition<T> 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)))
|
||||
|
||||
@@ -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<TargetFilterQuery> 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));
|
||||
}
|
||||
|
||||
@@ -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<Long> 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<Long> 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<Long> 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<Action> 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<Action> 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)));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<RolloutGroup> 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<RolloutGroup> groups, final String type) {
|
||||
|
||||
final List<RolloutGroup> 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();
|
||||
|
||||
|
||||
@@ -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<Target> 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<Target> 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<Target> assignWithoutMaintenanceWindow(final DistributionSet distributionSet, final Target savedTarget,
|
||||
final boolean timeforced) {
|
||||
final List<Action> 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<Target> assignWithMaintenanceWindow(final DistributionSet distributionSet, final Target savedTarget,
|
||||
final boolean timeforced, final String maintenanceWindowSchedule, final String maintenanceWindowDuration,
|
||||
final String maintenanceWindowTimeZone) {
|
||||
final List<Action> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.";
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<String, Object> 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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -105,7 +105,7 @@ public final class TargetAssignmentOperations {
|
||||
final String maintenanceTimeZone = maintenanceWindowLayout.getMaintenanceTimeZone();
|
||||
|
||||
final Set<Long> dsIds = distributionSets.stream().map(DistributionSet::getId).collect(Collectors.toSet());
|
||||
final List<DeploymentRequest> deploymentRequests = new ArrayList<>();
|
||||
final List<DeploymentRequest> deploymentRequests = new ArrayList<>();
|
||||
dsIds.forEach(dsId -> targets.forEach(t -> {
|
||||
final DeploymentRequestBuilder request = DeploymentManagement.deploymentRequest(t.getControllerId(), dsId)
|
||||
.setActionType(actionType).setForceTime(forcedTimeStamp);
|
||||
|
||||
Reference in New Issue
Block a user