Feature download only (#810)
* Added initial version of DOWNLOAD_ONLY * Added DOWNLOAD_ONLY option to ActionTypeOptionGroupLayout * Removed DOWNLOAD_ONLY checkbox, added Download Only UI option * Mark actions that finished with DOWNLOADED as finished * initial changes to realize downoadOnly in UI * Changed method of disabling maintenanceWindow into smarter solution * Added new icon for download only option * Set DistributionSet as unassigned when DOWNLOAD_ONLY * Enabled update action status for DOWNLOAD_ONLY after download * Current state of abstraction task * Assign DistributionSet to target if target installs it after downloading * Abstracted class redundant methods * Added tests * Fixed Rollout finish status for DWONLOAD_ONLY Rollouts * Added Rollout type json property in test documentation * Added DOWNLOAD_ONLY test for target assignment * Added event listener also to DistributionTable * Fixed event listener problem * Change column name to "Type" and added also DownloadOnly icon to that column. * Cleanup * Center aligned the icons in type column * Fixed DistributionSet already assigned but not installed * Rename download_only to downloadonly * Further changes regarding center aligned the icons * Fixed target assign status in Rollout view when download_only * Fixed SonarQube issues * Fixed SonarQube issues + code formatting * Fixed Tests * Marked squid:S128 as suppressed - irrelevant * Adapting rollouts view by additional column (not finished by now) * Putted type column on proper position * Trying to display icons in new type column in rollouts view * Added icon also for soft, icon might change -> just change * createOptionGroup method in ActionTypeOptionGroupLayout class * added first draft of type column in rollouts view * increase visibility of sendUpdateMessageToTarget method * Ground functionality of new type column in deployment view is now implemented * Type column implementation in rollouts view is finished for now * Rebased on master * Fixed DurationControl change on ScheduleControl change. * (Re)Added Soft deployment Icon * Fixed SonarQube issues * Fixed SonarQube issues * Fixed failing test * Fixes + added missing header * Added message to the fail() instruction * Fixed copyright header * Apply suggestions from code review * Fixed TotalTargetCountStatus.java * Removed unused method from TotalTargetCountStatus.java * add id to rollout create and update UI popup * Added download_only tests for MgmtTargetResourceTest.java * added missing header in TotalTargetCountStatusTest.java * Rename because of newest changes * added Download_Only dmf integration tests * Renamed MgmtAction.forcedType to actionType * renamed actionType to forceType for Mgmt API * added missing javadocs for public methods * Added Download Only support for AutoAssignment Signed-off-by: Ahmed Sayed <ahmed.sayed@bosch-si.com> Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>
This commit is contained in:
@@ -142,7 +142,7 @@ name==CCU* and updatestatus==pending
|
|||||||

|

|
||||||
|
|
||||||
### Auto assignment
|
### Auto assignment
|
||||||
It is possible to assign some distribution set with different action types (_forced_ or _soft_) to all targets that belong to the corresponding custom target filter, including the ones, that are registered later on.
|
It is possible to assign some distribution set with different action types (_forced_, _soft_, or _download only_) to all targets that belong to the corresponding custom target filter, including the ones, that are registered later on.
|
||||||
|
|
||||||
In order to activate the auto-assignment, one should first click on _Auto assignment_ cell in Custom Filters table, and then check the corresponding checkbox. After that, the action type and distribution set for auto-assignment should be selected and confirmed.
|
In order to activate the auto-assignment, one should first click on _Auto assignment_ cell in Custom Filters table, and then check the corresponding checkbox. After that, the action type and distribution set for auto-assignment should be selected and confirmed.
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 94 KiB |
@@ -225,7 +225,7 @@ public enum SpServerError {
|
|||||||
* invalid.
|
* invalid.
|
||||||
*/
|
*/
|
||||||
SP_AUTO_ASSIGN_ACTION_TYPE_INVALID("hawkbit.server.error.repo.invalidAutoAssignActionType",
|
SP_AUTO_ASSIGN_ACTION_TYPE_INVALID("hawkbit.server.error.repo.invalidAutoAssignActionType",
|
||||||
"The given action type for auto-assignment is invalid: allowed values are FORCED and SOFT"),
|
"The given action type for auto-assignment is invalid: allowed values are ['forced', 'soft', 'downloadonly']"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error message informing that the distribution set for auto-assignment is
|
* Error message informing that the distribution set for auto-assignment is
|
||||||
|
|||||||
@@ -133,7 +133,6 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
|
|||||||
checkByTargetId(sha1Hash, secruityToken.getTargetId());
|
checkByTargetId(sha1Hash, secruityToken.getTargetId());
|
||||||
} else {
|
} else {
|
||||||
LOG.info("anonymous download no authentication check for artifact {}", sha1Hash);
|
LOG.info("anonymous download no authentication check for artifact {}", sha1Hash);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,6 +270,12 @@ public class AmqpConfiguration {
|
|||||||
return new DefaultAmqpMessageSenderService(rabbitTemplate());
|
return new DefaultAmqpMessageSenderService(rabbitTemplate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create RabbitListenerContainerFactory bean if no listenerContainerFactory
|
||||||
|
* bean found
|
||||||
|
*
|
||||||
|
* @return RabbitListenerContainerFactory bean
|
||||||
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(name = "listenerContainerFactory")
|
@ConditionalOnMissingBean(name = "listenerContainerFactory")
|
||||||
public RabbitListenerContainerFactory<SimpleMessageListenerContainer> listenerContainerFactory(
|
public RabbitListenerContainerFactory<SimpleMessageListenerContainer> listenerContainerFactory(
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEv
|
|||||||
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
|
import org.eclipse.hawkbit.repository.model.ActionProperties;
|
||||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||||
@@ -143,28 +145,28 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
|||||||
PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT), module.getId())
|
PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT), module.getId())
|
||||||
.getContent()));
|
.getContent()));
|
||||||
|
|
||||||
targetManagement.getByControllerID(assignedEvent.getActions().keySet())
|
targetManagement.getByControllerID(assignedEvent.getActions().keySet()).forEach(
|
||||||
.forEach(target -> sendUpdateMessageToTarget(assignedEvent.getTenant(), target,
|
target -> sendUpdateMessageToTarget(assignedEvent.getActions().get(target.getControllerId()),
|
||||||
assignedEvent.getActions().get(target.getControllerId()), modules,
|
target, modules));
|
||||||
assignedEvent.isMaintenanceWindowAvailable()));
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to get the type of event depending on whether the action has a
|
* Method to get the type of event depending on whether the action is a
|
||||||
* valid maintenance window available or not based on defined maintenance
|
* DOWNLOAD_ONLY action or if it has a valid maintenance window available
|
||||||
* schedule. In case of no maintenance schedule or if there is a valid
|
* or not based on defined maintenance schedule. In case of no maintenance
|
||||||
* window available, the topic {@link EventTopic#DOWNLOAD_AND_INSTALL} is
|
* schedule or if there is a valid window available, the topic {@link EventTopic#DOWNLOAD_AND_INSTALL} is
|
||||||
* returned else {@link EventTopic#DOWNLOAD} is returned.
|
* returned else {@link EventTopic#DOWNLOAD} is returned.
|
||||||
*
|
*
|
||||||
* @param maintenanceWindowAvailable
|
* @param action
|
||||||
* valid maintenance window or not.
|
* current action properties.
|
||||||
*
|
*
|
||||||
* @return {@link EventTopic} to use for message.
|
* @return {@link EventTopic} to use for message.
|
||||||
*/
|
*/
|
||||||
private static EventTopic getEventTypeForTarget(final boolean maintenanceWindowAvailable) {
|
private static EventTopic getEventTypeForTarget(final ActionProperties action) {
|
||||||
return maintenanceWindowAvailable ? EventTopic.DOWNLOAD_AND_INSTALL : EventTopic.DOWNLOAD;
|
return (Action.ActionType.DOWNLOAD_ONLY.equals(action.getActionType()) ||
|
||||||
|
!action.isMaintenanceWindowAvailable()) ? EventTopic.DOWNLOAD : EventTopic.DOWNLOAD_AND_INSTALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -206,8 +208,10 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
|||||||
updateAttributesEvent.getTargetAddress());
|
updateAttributesEvent.getTargetAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void sendUpdateMessageToTarget(final String tenant, final Target target, final Long actionId,
|
protected void sendUpdateMessageToTarget(final ActionProperties action, final Target target,
|
||||||
final Map<SoftwareModule, List<SoftwareModuleMetadata>> modules, final boolean maintenanceWindowAvailable) {
|
final Map<SoftwareModule, List<SoftwareModuleMetadata>> modules) {
|
||||||
|
|
||||||
|
String tenant = action.getTenant();
|
||||||
|
|
||||||
final URI targetAdress = target.getAddress();
|
final URI targetAdress = target.getAddress();
|
||||||
if (!IpUtil.isAmqpUri(targetAdress)) {
|
if (!IpUtil.isAmqpUri(targetAdress)) {
|
||||||
@@ -215,7 +219,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final DmfDownloadAndUpdateRequest downloadAndUpdateRequest = new DmfDownloadAndUpdateRequest();
|
final DmfDownloadAndUpdateRequest downloadAndUpdateRequest = new DmfDownloadAndUpdateRequest();
|
||||||
downloadAndUpdateRequest.setActionId(actionId);
|
downloadAndUpdateRequest.setActionId(action.getId());
|
||||||
|
|
||||||
final String targetSecurityToken = systemSecurityContext.runAsSystem(target::getSecurityToken);
|
final String targetSecurityToken = systemSecurityContext.runAsSystem(target::getSecurityToken);
|
||||||
downloadAndUpdateRequest.setTargetSecurityToken(targetSecurityToken);
|
downloadAndUpdateRequest.setTargetSecurityToken(targetSecurityToken);
|
||||||
@@ -227,8 +231,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
final Message message = getMessageConverter().toMessage(downloadAndUpdateRequest,
|
final Message message = getMessageConverter().toMessage(downloadAndUpdateRequest,
|
||||||
createConnectorMessagePropertiesEvent(tenant, target.getControllerId(),
|
createConnectorMessagePropertiesEvent(tenant, target.getControllerId(), getEventTypeForTarget(action)));
|
||||||
getEventTypeForTarget(maintenanceWindowAvailable)));
|
|
||||||
amqpSenderService.sendMessage(message, targetAdress);
|
amqpSenderService.sendMessage(message, targetAdress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.eclipse.hawkbit.repository.UpdateMode;
|
|||||||
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
|
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
|
import org.eclipse.hawkbit.repository.model.ActionProperties;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
@@ -218,8 +219,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
|
|
||||||
action.getDistributionSet().getModules().forEach(module -> modules.put(module, metadata.get(module.getId())));
|
action.getDistributionSet().getModules().forEach(module -> modules.put(module, metadata.get(module.getId())));
|
||||||
|
|
||||||
amqpMessageDispatcherService.sendUpdateMessageToTarget(action.getTenant(), action.getTarget(), action.getId(),
|
amqpMessageDispatcherService.sendUpdateMessageToTarget(new ActionProperties(action), action.getTarget(),
|
||||||
modules, action.isMaintenanceWindowAvailable());
|
modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.amqp;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
@@ -46,6 +45,7 @@ import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
|
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
|
import org.eclipse.hawkbit.repository.model.ActionProperties;
|
||||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
@@ -301,7 +301,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
|
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
|
||||||
final Message message = new Message(new byte[0], messageProperties);
|
final Message message = new Message(new byte[0], messageProperties);
|
||||||
try {
|
try {
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
amqpMessageHandlerService.onMessage(message, "unknownMessageType", TENANT, "vHost");
|
||||||
fail("AmqpRejectAndDontRequeueException was excepeted due to unknown message type");
|
fail("AmqpRejectAndDontRequeueException was excepeted due to unknown message type");
|
||||||
} catch (final AmqpRejectAndDontRequeueException e) {
|
} catch (final AmqpRejectAndDontRequeueException e) {
|
||||||
}
|
}
|
||||||
@@ -462,19 +462,16 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
// test
|
// test
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||||
|
|
||||||
final ArgumentCaptor<String> tenantCaptor = ArgumentCaptor.forClass(String.class);
|
final ArgumentCaptor<ActionProperties> actionPropertiesCaptor = ArgumentCaptor.forClass(ActionProperties.class);
|
||||||
final ArgumentCaptor<Target> targetCaptor = ArgumentCaptor.forClass(Target.class);
|
final ArgumentCaptor<Target> targetCaptor = ArgumentCaptor.forClass(Target.class);
|
||||||
final ArgumentCaptor<Long> actionIdCaptor = ArgumentCaptor.forClass(Long.class);
|
|
||||||
|
|
||||||
verify(amqpMessageDispatcherServiceMock, times(1)).sendUpdateMessageToTarget(tenantCaptor.capture(),
|
verify(amqpMessageDispatcherServiceMock, times(1))
|
||||||
targetCaptor.capture(), actionIdCaptor.capture(), any(Map.class), anyBoolean());
|
.sendUpdateMessageToTarget(actionPropertiesCaptor.capture(), targetCaptor.capture(), any(Map.class));
|
||||||
final String tenant = tenantCaptor.getValue();
|
final ActionProperties actionProperties = actionPropertiesCaptor.getValue();
|
||||||
final String controllerId = targetCaptor.getValue().getControllerId();
|
assertThat(actionProperties).isNotNull();
|
||||||
final Long actionId = actionIdCaptor.getValue();
|
assertThat(actionProperties.getTenant()).as("event has tenant").isEqualTo("DEFAULT");
|
||||||
|
assertThat(targetCaptor.getValue().getControllerId()).as("event has wrong controller id").isEqualTo("target1");
|
||||||
assertThat(tenant).as("event has tenant").isEqualTo("DEFAULT");
|
assertThat(actionProperties.getId()).as("event has wrong action id").isEqualTo(22L);
|
||||||
assertThat(controllerId).as("event has wrong controller id").isEqualTo("target1");
|
|
||||||
assertThat(actionId).as("event has wrong action id").isEqualTo(22L);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,6 +513,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
when(actionMock.getId()).thenReturn(targetId);
|
when(actionMock.getId()).thenReturn(targetId);
|
||||||
when(actionMock.getTenant()).thenReturn("DEFAULT");
|
when(actionMock.getTenant()).thenReturn("DEFAULT");
|
||||||
when(actionMock.getTarget()).thenReturn(targetMock);
|
when(actionMock.getTarget()).thenReturn(targetMock);
|
||||||
|
when(actionMock.getActionType()).thenReturn(Action.ActionType.SOFT);
|
||||||
when(targetMock.getControllerId()).thenReturn("target1");
|
when(targetMock.getControllerId()).thenReturn("target1");
|
||||||
return actionMock;
|
return actionMock;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import org.eclipse.hawkbit.matcher.SoftwareModuleJsonMatcher;
|
|||||||
import org.eclipse.hawkbit.rabbitmq.test.AbstractAmqpIntegrationTest;
|
import org.eclipse.hawkbit.rabbitmq.test.AbstractAmqpIntegrationTest;
|
||||||
import org.eclipse.hawkbit.rabbitmq.test.AmqpTestConfiguration;
|
import org.eclipse.hawkbit.rabbitmq.test.AmqpTestConfiguration;
|
||||||
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
|
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
@@ -235,7 +236,7 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
|||||||
assertThat(targetManagement.count()).isEqualTo(expectedTargetsCount);
|
assertThat(targetManagement.count()).isEqualTo(expectedTargetsCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Message assertReplyMessageHeader(final EventTopic eventTopic, final String controllerId) {
|
protected Message assertReplyMessageHeader(final EventTopic eventTopic, final String controllerId) {
|
||||||
verifyReplyToListener();
|
verifyReplyToListener();
|
||||||
final Message replyMessage = replyToListener.getEventTopicMessages().get(eventTopic);
|
final Message replyMessage = replyToListener.getEventTopicMessages().get(eventTopic);
|
||||||
assertAllTargetsCount(1);
|
assertAllTargetsCount(1);
|
||||||
@@ -379,4 +380,16 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
|||||||
return AmqpSettings.DMF_EXCHANGE;
|
return AmqpSettings.DMF_EXCHANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
protected DistributionSet createTargetAndDistributionSetAndAssign(final String controllerId,
|
||||||
|
final Action.ActionType actionType) {
|
||||||
|
registerAndAssertTargetWithExistingTenant(controllerId);
|
||||||
|
|
||||||
|
final DistributionSet distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||||
|
testdataFactory.addSoftwareModuleMetadata(distributionSet);
|
||||||
|
|
||||||
|
assignDistributionSet(distributionSet.getId(), controllerId, actionType);
|
||||||
|
return distributionSet;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.integration;
|
package org.eclipse.hawkbit.integration;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus;
|
import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
|
||||||
@@ -25,6 +27,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedE
|
|||||||
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
@@ -32,12 +35,18 @@ import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
|||||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||||
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
import org.springframework.amqp.core.Message;
|
import org.springframework.amqp.core.Message;
|
||||||
|
|
||||||
import io.qameta.allure.Description;
|
import io.qameta.allure.Description;
|
||||||
import io.qameta.allure.Feature;
|
import io.qameta.allure.Feature;
|
||||||
import io.qameta.allure.Story;
|
import io.qameta.allure.Story;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.eclipse.hawkbit.dmf.amqp.api.EventTopic.DOWNLOAD;
|
||||||
|
import static org.eclipse.hawkbit.dmf.amqp.api.MessageType.EVENT;
|
||||||
|
import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY;
|
||||||
|
|
||||||
@Feature("Component Tests - Device Management Federation API")
|
@Feature("Component Tests - Device Management Federation API")
|
||||||
@Story("Amqp Message Dispatcher Service")
|
@Story("Amqp Message Dispatcher Service")
|
||||||
public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpServiceIntegrationTest {
|
public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpServiceIntegrationTest {
|
||||||
@@ -198,6 +207,37 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
|||||||
assertRequestAttributesUpdateMessage(controllerId);
|
assertRequestAttributesUpdateMessage(controllerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests the download_only assignment: asserts correct dmf Message topic, and assigned DS")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||||
|
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||||
|
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 6),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||||
|
public void downloadOnlyAssignmentSendsDownloadMessageTopic() {
|
||||||
|
final String controllerId = TARGET_PREFIX + "registerTargets_1";
|
||||||
|
final DistributionSet distributionSet = createTargetAndDistributionSetAndAssign(controllerId, DOWNLOAD_ONLY);
|
||||||
|
|
||||||
|
// verify
|
||||||
|
final Message message = assertReplyMessageHeader(EventTopic.DOWNLOAD, controllerId);
|
||||||
|
Mockito.verifyZeroInteractions(getDeadletterListener());
|
||||||
|
|
||||||
|
assertThat(message).isNotNull();
|
||||||
|
final Map<String, Object> headers = message.getMessageProperties().getHeaders();
|
||||||
|
assertThat(headers).containsEntry("thingId", controllerId);
|
||||||
|
assertThat(headers).containsEntry("type", EVENT.toString());
|
||||||
|
assertThat(headers).containsEntry("topic", DOWNLOAD.toString());
|
||||||
|
|
||||||
|
final Optional<Target> target = controllerManagement.getByControllerId(controllerId);
|
||||||
|
assertThat(target).isPresent();
|
||||||
|
|
||||||
|
// verify the DS was assigned to the Target
|
||||||
|
final DistributionSet assignedDistributionSet = ((JpaTarget) target.get()).getAssignedDistributionSet();
|
||||||
|
assertThat(assignedDistributionSet.getId()).isEqualTo(distributionSet.getId());
|
||||||
|
}
|
||||||
|
|
||||||
private void waitUntilTargetHasStatus(final String controllerId, final TargetUpdateStatus status) {
|
private void waitUntilTargetHasStatus(final String controllerId, final TargetUpdateStatus status) {
|
||||||
waitUntil(() -> {
|
waitUntil(() -> {
|
||||||
final Optional<Target> findTargetByControllerID = targetManagement.getByControllerID(controllerId);
|
final Optional<Target> findTargetByControllerID = targetManagement.getByControllerID(controllerId);
|
||||||
|
|||||||
@@ -9,14 +9,20 @@
|
|||||||
package org.eclipse.hawkbit.integration;
|
package org.eclipse.hawkbit.integration;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import org.eclipse.hawkbit.amqp.AmqpProperties;
|
import org.eclipse.hawkbit.amqp.AmqpProperties;
|
||||||
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
|
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
|
||||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
||||||
@@ -37,6 +43,8 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedE
|
|||||||
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
@@ -797,6 +805,99 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
|||||||
verifyNumberOfDeadLetterMessages(3);
|
verifyNumberOfDeadLetterMessages(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests the download_only assignment: tests the handling of a target reporting DOWNLOADED")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||||
|
@Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||||
|
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 6),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||||
|
public void downloadOnlyAssignmentFinishesActionWhenTargetReportsDownloaded()
|
||||||
|
throws IOException {
|
||||||
|
// create target
|
||||||
|
final String controllerId = TARGET_PREFIX + "registerTargets_1";
|
||||||
|
final DistributionSet distributionSet = createTargetAndDistributionSetAndAssign(controllerId, DOWNLOAD_ONLY);
|
||||||
|
|
||||||
|
// verify
|
||||||
|
final Message message = assertReplyMessageHeader(EventTopic.DOWNLOAD, controllerId);
|
||||||
|
Mockito.verifyZeroInteractions(getDeadletterListener());
|
||||||
|
|
||||||
|
// get actionId from Message
|
||||||
|
Long actionId = Long.parseLong(getJsonFieldFromBody(message.getBody(), "actionId"));
|
||||||
|
|
||||||
|
// Send DOWNLOADED message
|
||||||
|
sendActionUpdateStatus(new DmfActionUpdateStatus(actionId, DmfActionStatus.DOWNLOADED));
|
||||||
|
assertAction(actionId, 1, Status.RUNNING, Status.DOWNLOADED);
|
||||||
|
Mockito.verifyZeroInteractions(getDeadletterListener());
|
||||||
|
|
||||||
|
verifyAssignedDsAndInstalledDs(controllerId, distributionSet.getId(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Tests the download_only assignment: tests the handling of a target reporting FINISHED")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||||
|
@Expect(type = ActionUpdatedEvent.class, count = 2),
|
||||||
|
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||||
|
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 6),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetAttributesRequestedEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 3),
|
||||||
|
@Expect(type = TargetPollEvent.class, count = 1) })
|
||||||
|
public void downloadOnlyAssignmentAllowsActionStatusUpdatesWhenTargetReportsFinishedAndUpdatesInstalledDS()
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
// create target
|
||||||
|
final String controllerId = TARGET_PREFIX + "registerTargets_1";
|
||||||
|
final DistributionSet distributionSet = createTargetAndDistributionSetAndAssign(controllerId, DOWNLOAD_ONLY);
|
||||||
|
|
||||||
|
// verify
|
||||||
|
final Message message = assertReplyMessageHeader(EventTopic.DOWNLOAD, controllerId);
|
||||||
|
Mockito.verifyZeroInteractions(getDeadletterListener());
|
||||||
|
|
||||||
|
// get actionId from Message
|
||||||
|
Long actionId = Long.parseLong(getJsonFieldFromBody(message.getBody(), "actionId"));
|
||||||
|
|
||||||
|
// Send DOWNLOADED message, should result in the action being closed
|
||||||
|
sendActionUpdateStatus(new DmfActionUpdateStatus(actionId, DmfActionStatus.DOWNLOADED));
|
||||||
|
assertAction(actionId, 1, Status.RUNNING, Status.DOWNLOADED);
|
||||||
|
Mockito.verifyZeroInteractions(getDeadletterListener());
|
||||||
|
|
||||||
|
verifyAssignedDsAndInstalledDs(controllerId, distributionSet.getId(), null);
|
||||||
|
|
||||||
|
// Send FINISHED message
|
||||||
|
sendActionUpdateStatus(new DmfActionUpdateStatus(actionId, DmfActionStatus.FINISHED));
|
||||||
|
assertAction(actionId, 2, Status.RUNNING, Status.DOWNLOADED, Status.FINISHED);
|
||||||
|
Mockito.verifyZeroInteractions(getDeadletterListener());
|
||||||
|
|
||||||
|
verifyAssignedDsAndInstalledDs(controllerId, distributionSet.getId(), distributionSet.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void verifyAssignedDsAndInstalledDs(final String controllerId, final Long assignedDsId,
|
||||||
|
final Long installedDsId) {
|
||||||
|
final Optional<Target> target = controllerManagement.getByControllerId(controllerId);
|
||||||
|
assertThat(target).isPresent();
|
||||||
|
|
||||||
|
// verify the DS was assigned to the Target
|
||||||
|
final DistributionSet assignedDistributionSet = ((JpaTarget) target.get()).getAssignedDistributionSet();
|
||||||
|
assertThat(assignedDsId).isNotNull();
|
||||||
|
assertThat(assignedDistributionSet.getId()).isEqualTo(assignedDsId);
|
||||||
|
|
||||||
|
// verify that the installed DS was not affected
|
||||||
|
final JpaDistributionSet installedDistributionSet = ((JpaTarget) target.get()).getInstalledDistributionSet();
|
||||||
|
if (installedDsId == null) {
|
||||||
|
assertThat(installedDistributionSet).isNull();
|
||||||
|
} else {
|
||||||
|
assertThat(installedDistributionSet.getId()).isEqualTo(installedDsId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void sendUpdateAttributesMessageWithGivenAttributes(final String target, final String key,
|
private void sendUpdateAttributesMessageWithGivenAttributes(final String target, final String key,
|
||||||
final String value) {
|
final String value) {
|
||||||
final DmfAttributeUpdate controllerAttribute = new DmfAttributeUpdate();
|
final DmfAttributeUpdate controllerAttribute = new DmfAttributeUpdate();
|
||||||
@@ -887,4 +988,11 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
|||||||
createConditionFactory().untilAsserted(() -> Mockito
|
createConditionFactory().untilAsserted(() -> Mockito
|
||||||
.verify(getDeadletterListener(), Mockito.times(numberOfInvocations)).handleMessage(Mockito.any()));
|
.verify(getDeadletterListener(), Mockito.times(numberOfInvocations)).handleMessage(Mockito.any()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getJsonFieldFromBody(final byte[] body, final String fieldName) throws IOException {
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
final ObjectNode node = objectMapper.readValue(new String(body, Charset.defaultCharset()), ObjectNode.class);
|
||||||
|
assertThat(node.has(fieldName)).isTrue();
|
||||||
|
return node.get(fieldName).asText();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ public class DmfActionUpdateStatus {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
private List<String> message;
|
private List<String> message;
|
||||||
|
|
||||||
public DmfActionUpdateStatus(@JsonProperty(value = "actionId", required = true) Long actionId,
|
public DmfActionUpdateStatus(@JsonProperty(value = "actionId", required = true) final Long actionId,
|
||||||
@JsonProperty(value = "actionStatus", required = true) DmfActionStatus actionStatus) {
|
@JsonProperty(value = "actionStatus", required = true) final DmfActionStatus actionStatus) {
|
||||||
this.actionId = actionId;
|
this.actionId = actionId;
|
||||||
this.actionStatus = actionStatus;
|
this.actionStatus = actionStatus;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,12 @@ public class RepositoryProperties {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to <code>true</code> if the repository has to reject
|
* Set to <code>true</code> if the repository has to reject
|
||||||
* {@link ActionStatus} entries for actions that are closed. Note: if this
|
* {@link ActionStatus} entries for actions that are closed. This is
|
||||||
* is enforced you have to make sure that the feedback channel from the
|
* especially useful if the action status feedback channel order from the
|
||||||
* devices i in order.
|
* device cannot be guaranteed.
|
||||||
|
*
|
||||||
|
* Note: if this is enforced you have to make sure that the feedback
|
||||||
|
* channel from the devices is in order.
|
||||||
*/
|
*/
|
||||||
private boolean rejectActionStatusForClosedAction;
|
private boolean rejectActionStatusForClosedAction;
|
||||||
|
|
||||||
|
|||||||
@@ -8,13 +8,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.event.remote;
|
package org.eclipse.hawkbit.repository.event.remote;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
|
import org.eclipse.hawkbit.repository.model.ActionProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TenantAwareEvent that gets sent when a distribution set gets assigned to a
|
* TenantAwareEvent that gets sent when a distribution set gets assigned to a
|
||||||
@@ -28,7 +29,7 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent {
|
|||||||
|
|
||||||
private boolean maintenanceWindowAvailable;
|
private boolean maintenanceWindowAvailable;
|
||||||
|
|
||||||
private final Map<String, Long> actions = new HashMap<>();
|
private final Map<String, ActionProperties> actions = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor.
|
* Default constructor.
|
||||||
@@ -57,12 +58,20 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent {
|
|||||||
this.distributionSetId = distributionSetId;
|
this.distributionSetId = distributionSetId;
|
||||||
this.maintenanceWindowAvailable = maintenanceWindowAvailable;
|
this.maintenanceWindowAvailable = maintenanceWindowAvailable;
|
||||||
actions.putAll(a.stream().filter(action -> action.getDistributionSet().getId().longValue() == distributionSetId)
|
actions.putAll(a.stream().filter(action -> action.getDistributionSet().getId().longValue() == distributionSetId)
|
||||||
.collect(Collectors.toMap(action -> action.getTarget().getControllerId(), Action::getId)));
|
.collect(Collectors.toMap(action -> action.getTarget().getControllerId(), ActionProperties::new)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param action
|
||||||
|
* the action created for this assignment
|
||||||
|
* @param applicationId
|
||||||
|
* the application id
|
||||||
|
*/
|
||||||
public TargetAssignDistributionSetEvent(final Action action, final String applicationId) {
|
public TargetAssignDistributionSetEvent(final Action action, final String applicationId) {
|
||||||
this(action.getTenant(), action.getDistributionSet().getId(), Arrays.asList(action), applicationId,
|
this(action.getTenant(), action.getDistributionSet().getId(), Collections.singletonList(action), applicationId,
|
||||||
action.isMaintenanceWindowAvailable());
|
action.isMaintenanceWindowAvailable());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +83,7 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent {
|
|||||||
return maintenanceWindowAvailable;
|
return maintenanceWindowAvailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Long> getActions() {
|
public Map<String, ActionProperties> getActions() {
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -229,7 +229,12 @@ public interface Action extends TenantAwareBaseEntity {
|
|||||||
* {@link Action#isHitAutoForceTime(long)} is reached, {@link #FORCED}
|
* {@link Action#isHitAutoForceTime(long)} is reached, {@link #FORCED}
|
||||||
* after that.
|
* after that.
|
||||||
*/
|
*/
|
||||||
TIMEFORCED;
|
TIMEFORCED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target is only advised to download, but not install
|
||||||
|
*/
|
||||||
|
DOWNLOAD_ONLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* 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.model;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds properties for {@link Action}
|
||||||
|
*/
|
||||||
|
public class ActionProperties implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Action.ActionType actionType;
|
||||||
|
private String tenant;
|
||||||
|
private boolean maintenanceWindowAvailable;
|
||||||
|
|
||||||
|
public ActionProperties() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param action
|
||||||
|
* the action to populate the properties from
|
||||||
|
*/
|
||||||
|
public ActionProperties(final Action action) {
|
||||||
|
this.id = action.getId();
|
||||||
|
this.actionType = action.getActionType();
|
||||||
|
this.tenant = action.getTenant();
|
||||||
|
this.maintenanceWindowAvailable = action.isMaintenanceWindowAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(final Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTenant(final String tenant) {
|
||||||
|
this.tenant = tenant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTenant() {
|
||||||
|
return tenant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaintenanceWindowAvailable(final boolean maintenanceWindowAvailable) {
|
||||||
|
this.maintenanceWindowAvailable = maintenanceWindowAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMaintenanceWindowAvailable() {
|
||||||
|
return maintenanceWindowAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action.ActionType getActionType() {
|
||||||
|
return actionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActionType(final Action.ActionType actionType) {
|
||||||
|
this.actionType = actionType;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,7 +48,7 @@ public interface TargetFilterQuery extends TenantAwareBaseEntity {
|
|||||||
* Allowed values for auto-assign action type
|
* Allowed values for auto-assign action type
|
||||||
*/
|
*/
|
||||||
Set<ActionType> ALLOWED_AUTO_ASSIGN_ACTION_TYPES = Collections
|
Set<ActionType> ALLOWED_AUTO_ASSIGN_ACTION_TYPES = Collections
|
||||||
.unmodifiableSet(EnumSet.of(ActionType.FORCED, ActionType.SOFT));
|
.unmodifiableSet(EnumSet.of(ActionType.FORCED, ActionType.SOFT, ActionType.DOWNLOAD_ONLY));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return name of the {@link TargetFilterQuery}.
|
* @return name of the {@link TargetFilterQuery}.
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ public class TotalTargetCountStatus {
|
|||||||
|
|
||||||
private final Map<Status, Long> statusTotalCountMap = new EnumMap<>(Status.class);
|
private final Map<Status, Long> statusTotalCountMap = new EnumMap<>(Status.class);
|
||||||
private final Long totalTargetCount;
|
private final Long totalTargetCount;
|
||||||
|
private final Action.ActionType rolloutType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new states map with the target count for each state.
|
* Create a new states map with the target count for each state.
|
||||||
@@ -66,11 +67,14 @@ public class TotalTargetCountStatus {
|
|||||||
* the action state map
|
* the action state map
|
||||||
* @param totalTargetCount
|
* @param totalTargetCount
|
||||||
* the total target count
|
* the total target count
|
||||||
|
* @param rolloutType
|
||||||
|
* the type of the rollout
|
||||||
*/
|
*/
|
||||||
public TotalTargetCountStatus(final List<TotalTargetCountActionStatus> targetCountActionStatus,
|
public TotalTargetCountStatus(final List<TotalTargetCountActionStatus> targetCountActionStatus,
|
||||||
final Long totalTargetCount) {
|
final Long totalTargetCount, final Action.ActionType rolloutType) {
|
||||||
this.totalTargetCount = totalTargetCount;
|
this.totalTargetCount = totalTargetCount;
|
||||||
mapActionStatusToTotalTargetCountStatus(targetCountActionStatus);
|
this.rolloutType = rolloutType;
|
||||||
|
addToTotalCount(targetCountActionStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,9 +82,11 @@ public class TotalTargetCountStatus {
|
|||||||
*
|
*
|
||||||
* @param totalTargetCount
|
* @param totalTargetCount
|
||||||
* the total target count
|
* the total target count
|
||||||
|
* @param rolloutType
|
||||||
|
* the type of the rollout
|
||||||
*/
|
*/
|
||||||
public TotalTargetCountStatus(final Long totalTargetCount) {
|
public TotalTargetCountStatus(final Long totalTargetCount, final Action.ActionType rolloutType) {
|
||||||
this(Collections.emptyList(), totalTargetCount);
|
this(Collections.emptyList(), totalTargetCount, rolloutType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,8 +125,7 @@ public class TotalTargetCountStatus {
|
|||||||
* @param rolloutStatusCountItems
|
* @param rolloutStatusCountItems
|
||||||
* all target {@link Status} with total count
|
* all target {@link Status} with total count
|
||||||
*/
|
*/
|
||||||
private final void mapActionStatusToTotalTargetCountStatus(
|
private void addToTotalCount(final List<TotalTargetCountActionStatus> targetCountActionStatus) {
|
||||||
final List<TotalTargetCountActionStatus> targetCountActionStatus) {
|
|
||||||
if (targetCountActionStatus == null) {
|
if (targetCountActionStatus == null) {
|
||||||
statusTotalCountMap.put(TotalTargetCountStatus.Status.NOTSTARTED, totalTargetCount);
|
statusTotalCountMap.put(TotalTargetCountStatus.Status.NOTSTARTED, totalTargetCount);
|
||||||
return;
|
return;
|
||||||
@@ -128,40 +133,40 @@ public class TotalTargetCountStatus {
|
|||||||
statusTotalCountMap.put(Status.RUNNING, 0L);
|
statusTotalCountMap.put(Status.RUNNING, 0L);
|
||||||
Long notStartedTargetCount = totalTargetCount;
|
Long notStartedTargetCount = totalTargetCount;
|
||||||
for (final TotalTargetCountActionStatus item : targetCountActionStatus) {
|
for (final TotalTargetCountActionStatus item : targetCountActionStatus) {
|
||||||
convertStatus(item);
|
addToTotalCount(item);
|
||||||
notStartedTargetCount -= item.getCount();
|
notStartedTargetCount -= item.getCount();
|
||||||
}
|
}
|
||||||
statusTotalCountMap.put(TotalTargetCountStatus.Status.NOTSTARTED, notStartedTargetCount);
|
statusTotalCountMap.put(TotalTargetCountStatus.Status.NOTSTARTED, notStartedTargetCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addToTotalCount(final TotalTargetCountActionStatus item) {
|
||||||
|
final Status status = convertStatus(item.getStatus());
|
||||||
|
statusTotalCountMap.merge(status, item.getCount(), Long::sum);
|
||||||
|
}
|
||||||
|
|
||||||
// Exception squid:MethodCyclomaticComplexity - simple state conversion, not
|
// Exception squid:MethodCyclomaticComplexity - simple state conversion, not
|
||||||
// really complex.
|
// really complex.
|
||||||
@SuppressWarnings("squid:MethodCyclomaticComplexity")
|
@SuppressWarnings("squid:MethodCyclomaticComplexity")
|
||||||
private void convertStatus(final TotalTargetCountActionStatus item) {
|
private Status convertStatus(final Action.Status status){
|
||||||
switch (item.getStatus()) {
|
switch (status) {
|
||||||
case SCHEDULED:
|
case SCHEDULED:
|
||||||
statusTotalCountMap.put(Status.SCHEDULED, item.getCount());
|
return Status.SCHEDULED;
|
||||||
break;
|
|
||||||
case ERROR:
|
case ERROR:
|
||||||
statusTotalCountMap.put(Status.ERROR, item.getCount());
|
return Status.ERROR;
|
||||||
break;
|
|
||||||
case FINISHED:
|
case FINISHED:
|
||||||
statusTotalCountMap.put(Status.FINISHED, item.getCount());
|
return Status.FINISHED;
|
||||||
break;
|
case CANCELED:
|
||||||
|
return Status.CANCELLED;
|
||||||
case RETRIEVED:
|
case RETRIEVED:
|
||||||
case RUNNING:
|
case RUNNING:
|
||||||
case WARNING:
|
case WARNING:
|
||||||
case DOWNLOAD:
|
case DOWNLOAD:
|
||||||
case DOWNLOADED:
|
|
||||||
case CANCELING:
|
case CANCELING:
|
||||||
final Long runningItemsCount = statusTotalCountMap.get(Status.RUNNING) + item.getCount();
|
return Status.RUNNING;
|
||||||
statusTotalCountMap.put(Status.RUNNING, runningItemsCount);
|
case DOWNLOADED:
|
||||||
break;
|
return Action.ActionType.DOWNLOAD_ONLY.equals(rolloutType) ? Status.FINISHED : Status.RUNNING;
|
||||||
case CANCELED:
|
|
||||||
statusTotalCountMap.put(Status.CANCELLED, item.getCount());
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("State " + item.getStatus() + "is not valid");
|
throw new IllegalArgumentException("State " + status + "is not valid");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* 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.model;
|
||||||
|
|
||||||
|
import io.qameta.allure.Description;
|
||||||
|
import io.qameta.allure.Feature;
|
||||||
|
import io.qameta.allure.Story;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@Feature("Component Tests - TotalTargetCountStatus")
|
||||||
|
@Story("TotalTargetCountStatus should correctly present finished DOWNLOAD_ONLY actions")
|
||||||
|
public class TotalTargetCountStatusTest {
|
||||||
|
|
||||||
|
private final List<TotalTargetCountActionStatus> targetCountActionStatuses = Arrays.asList(
|
||||||
|
new TotalTargetCountActionStatus(Action.Status.SCHEDULED, 1L),
|
||||||
|
new TotalTargetCountActionStatus(Action.Status.ERROR, 2L),
|
||||||
|
new TotalTargetCountActionStatus(Action.Status.FINISHED, 3L),
|
||||||
|
new TotalTargetCountActionStatus(Action.Status.CANCELED, 4L),
|
||||||
|
new TotalTargetCountActionStatus(Action.Status.RETRIEVED, 5L),
|
||||||
|
new TotalTargetCountActionStatus(Action.Status.RUNNING, 6L),
|
||||||
|
new TotalTargetCountActionStatus(Action.Status.WARNING, 7L),
|
||||||
|
new TotalTargetCountActionStatus(Action.Status.DOWNLOAD, 8L),
|
||||||
|
new TotalTargetCountActionStatus(Action.Status.CANCELING, 9L),
|
||||||
|
new TotalTargetCountActionStatus(Action.Status.DOWNLOADED, 10L));
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Different Action Statuses should be correctly mapped to the corresponding " +
|
||||||
|
"TotalTargetCountStatus.Status")
|
||||||
|
public void shouldCorrectlyMapActionStatuses() {
|
||||||
|
TotalTargetCountStatus status = new TotalTargetCountStatus(targetCountActionStatuses, 55L,
|
||||||
|
Action.ActionType.FORCED);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.SCHEDULED)).isEqualTo(1L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.ERROR)).isEqualTo(2L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.FINISHED)).isEqualTo(3L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.CANCELLED)).isEqualTo(4L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.RUNNING)).isEqualTo(45L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.NOTSTARTED)).isEqualTo(0L);
|
||||||
|
assertThat(status.getFinishedPercent()).isEqualTo((float) 100 * 3 / 55);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("When an empty list is passed to the TotalTargetCountStatus, all actions should be displayed as " +
|
||||||
|
"NOTSTARTED")
|
||||||
|
public void shouldCorrectlyMapActionStatusesToNotStarted() {
|
||||||
|
TotalTargetCountStatus status = new TotalTargetCountStatus(Collections.emptyList(), 55L,
|
||||||
|
Action.ActionType.FORCED);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.SCHEDULED)).isEqualTo(0L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.ERROR)).isEqualTo(0L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.FINISHED)).isEqualTo(0L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.CANCELLED)).isEqualTo(0L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.RUNNING)).isEqualTo(0L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.NOTSTARTED)).isEqualTo(55L);
|
||||||
|
assertThat(status.getFinishedPercent()).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("DownloadOnly actions should be displayed as FINISHED when they have ActionStatus.DOWNLOADED")
|
||||||
|
public void shouldCorrectlyMapActionStatusesInDownloadOnlyCase() {
|
||||||
|
TotalTargetCountStatus status = new TotalTargetCountStatus(targetCountActionStatuses, 55L,
|
||||||
|
Action.ActionType.DOWNLOAD_ONLY);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.SCHEDULED)).isEqualTo(1L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.ERROR)).isEqualTo(2L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.FINISHED)).isEqualTo(13L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.CANCELLED)).isEqualTo(4L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.RUNNING)).isEqualTo(35L);
|
||||||
|
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.NOTSTARTED)).isEqualTo(0L);
|
||||||
|
assertThat(status.getFinishedPercent()).isEqualTo((float) 100 * 13 / 55);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -313,17 +313,13 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
|
|||||||
* the rollout the actions are belong to
|
* the rollout the actions are belong to
|
||||||
* @param rolloutGroup
|
* @param rolloutGroup
|
||||||
* the rolloutgroup the actions are belong to
|
* the rolloutgroup the actions are belong to
|
||||||
* @param notStatus1
|
* @param statuses
|
||||||
* the status the action should not have
|
* the list of statuses the action should not have
|
||||||
* @param notStatus2
|
|
||||||
* the status the action should not have
|
|
||||||
* @param notStatus3
|
|
||||||
* the status the action should not have
|
|
||||||
* @return the count of actions referring the rollout and rolloutgroup and
|
* @return the count of actions referring the rollout and rolloutgroup and
|
||||||
* are not in given states
|
* are not in given states
|
||||||
*/
|
*/
|
||||||
Long countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(JpaRollout rollout,
|
Long countByRolloutAndRolloutGroupAndStatusNotIn(JpaRollout rollout, JpaRolloutGroup rolloutGroup,
|
||||||
JpaRolloutGroup rolloutGroup, Status notStatus1, Status notStatus2, Status notStatus3);
|
List<Status> statuses);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts all actions referring to a given rollout and rolloutgroup.
|
* Counts all actions referring to a given rollout and rolloutgroup.
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa;
|
package org.eclipse.hawkbit.repository.jpa;
|
||||||
|
|
||||||
|
import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY;
|
||||||
|
import static org.eclipse.hawkbit.repository.model.Action.Status.DOWNLOADED;
|
||||||
|
import static org.eclipse.hawkbit.repository.model.Action.Status.FINISHED;
|
||||||
import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_KEY_SIZE;
|
import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_KEY_SIZE;
|
||||||
import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE;
|
import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE;
|
||||||
|
|
||||||
@@ -452,7 +455,7 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
"UPDATE sp_target SET last_target_query = #last_target_query WHERE controller_id IN ("
|
"UPDATE sp_target SET last_target_query = #last_target_query WHERE controller_id IN ("
|
||||||
+ formatQueryInStatementParams(paramMapping.keySet()) + ") AND tenant = #tenant");
|
+ formatQueryInStatementParams(paramMapping.keySet()) + ") AND tenant = #tenant");
|
||||||
|
|
||||||
paramMapping.entrySet().forEach(entry -> updateQuery.setParameter(entry.getKey(), entry.getValue()));
|
paramMapping.forEach(updateQuery::setParameter);
|
||||||
updateQuery.setParameter("last_target_query", currentTimeMillis);
|
updateQuery.setParameter("last_target_query", currentTimeMillis);
|
||||||
updateQuery.setParameter("tenant", tenant);
|
updateQuery.setParameter("tenant", tenant);
|
||||||
|
|
||||||
@@ -561,23 +564,37 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());
|
final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());
|
||||||
final JpaActionStatus actionStatus = create.build();
|
final JpaActionStatus actionStatus = create.build();
|
||||||
|
|
||||||
// if action is already closed we accept further status updates if
|
if (isUpdatingActionStatusAllowed(action, actionStatus)) {
|
||||||
// permitted so by configuration. This is especially useful if the
|
return handleAddUpdateActionStatus(actionStatus, action);
|
||||||
// action status feedback channel order from the device cannot be
|
|
||||||
// guaranteed. However, if an action is closed we do not accept further
|
|
||||||
// close messages.
|
|
||||||
if (actionIsNotActiveButIntermediateFeedbackStillAllowed(actionStatus, action.isActive())) {
|
|
||||||
LOG.debug("Update of actionStatus {} for action {} not possible since action not active anymore.",
|
|
||||||
actionStatus.getStatus(), action.getId());
|
|
||||||
return action;
|
|
||||||
}
|
}
|
||||||
return handleAddUpdateActionStatus(actionStatus, action);
|
|
||||||
|
LOG.debug("Update of actionStatus {} for action {} not possible since action not active anymore.",
|
||||||
|
actionStatus.getStatus(), action.getId());
|
||||||
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean actionIsNotActiveButIntermediateFeedbackStillAllowed(final ActionStatus actionStatus,
|
/**
|
||||||
final boolean actionActive) {
|
* ActionStatus updates are allowed mainly if the action is active. If the
|
||||||
return !actionActive && (repositoryProperties.isRejectActionStatusForClosedAction()
|
* action is not active we accept further status updates if permitted so
|
||||||
|| Status.ERROR.equals(actionStatus.getStatus()) || Status.FINISHED.equals(actionStatus.getStatus()));
|
* by repository configuration. In this case, only the values: Status.ERROR
|
||||||
|
* and Status.FINISHED are allowed. In the case of a DOWNLOAD_ONLY action,
|
||||||
|
* we accept status updates only once.
|
||||||
|
*/
|
||||||
|
private boolean isUpdatingActionStatusAllowed(final JpaAction action, final JpaActionStatus actionStatus) {
|
||||||
|
|
||||||
|
final boolean isIntermediateFeedback = !FINISHED.equals(actionStatus.getStatus())
|
||||||
|
&& !Status.ERROR.equals(actionStatus.getStatus());
|
||||||
|
|
||||||
|
final boolean isAllowedByRepositoryConfiguration = !repositoryProperties.isRejectActionStatusForClosedAction()
|
||||||
|
&& isIntermediateFeedback;
|
||||||
|
|
||||||
|
final boolean isAllowedForDownloadOnlyActions = isDownloadOnly(action) && !isIntermediateFeedback;
|
||||||
|
|
||||||
|
return action.isActive() || isAllowedByRepositoryConfiguration || isAllowedForDownloadOnlyActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isDownloadOnly(final JpaAction action) {
|
||||||
|
return DOWNLOAD_ONLY.equals(action.getActionType());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -588,6 +605,10 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
String controllerId = null;
|
String controllerId = null;
|
||||||
LOG.debug("handleAddUpdateActionStatus for action {}", action.getId());
|
LOG.debug("handleAddUpdateActionStatus for action {}", action.getId());
|
||||||
|
|
||||||
|
// information status entry - check for a potential DOS attack
|
||||||
|
assertActionStatusQuota(action);
|
||||||
|
assertActionStatusMessageQuota(actionStatus);
|
||||||
|
|
||||||
switch (actionStatus.getStatus()) {
|
switch (actionStatus.getStatus()) {
|
||||||
case ERROR:
|
case ERROR:
|
||||||
final JpaTarget target = (JpaTarget) action.getTarget();
|
final JpaTarget target = (JpaTarget) action.getTarget();
|
||||||
@@ -597,10 +618,10 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
case FINISHED:
|
case FINISHED:
|
||||||
controllerId = handleFinishedAndStoreInTargetStatus(action);
|
controllerId = handleFinishedAndStoreInTargetStatus(action);
|
||||||
break;
|
break;
|
||||||
|
case DOWNLOADED:
|
||||||
|
controllerId = handleDownloadedActionStatus(action);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// information status entry - check for a potential DOS attack
|
|
||||||
assertActionStatusQuota(action);
|
|
||||||
assertActionStatusMessageQuota(actionStatus);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,6 +636,20 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
return savedAction;
|
return savedAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String handleDownloadedActionStatus(final JpaAction action) {
|
||||||
|
if(!isDownloadOnly(action)){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
JpaTarget target = (JpaTarget) action.getTarget();
|
||||||
|
action.setActive(false);
|
||||||
|
action.setStatus(DOWNLOADED);
|
||||||
|
target.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
|
||||||
|
targetRepository.save(target);
|
||||||
|
|
||||||
|
return target.getControllerId();
|
||||||
|
}
|
||||||
|
|
||||||
private void requestControllerAttributes(final String controllerId) {
|
private void requestControllerAttributes(final String controllerId) {
|
||||||
final JpaTarget target = (JpaTarget) getByControllerId(controllerId)
|
final JpaTarget target = (JpaTarget) getByControllerId(controllerId)
|
||||||
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
|
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
|
||||||
@@ -648,6 +683,12 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
target.setInstalledDistributionSet(ds);
|
target.setInstalledDistributionSet(ds);
|
||||||
target.setInstallationDate(System.currentTimeMillis());
|
target.setInstallationDate(System.currentTimeMillis());
|
||||||
|
|
||||||
|
// Target reported an installation of a DOWNLOAD_ONLY assignment, the assigned DS has to be adapted
|
||||||
|
// because the currently assigned DS can be unequal to the currently installed DS (the downloadOnly DS)
|
||||||
|
if(isDownloadOnly(action)){
|
||||||
|
target.setAssignedDistributionSet(action.getDistributionSet());
|
||||||
|
}
|
||||||
|
|
||||||
// check if the assigned set is equal to the installed set (not
|
// check if the assigned set is equal to the installed set (not
|
||||||
// necessarily the case as another update might be pending already).
|
// necessarily the case as another update might be pending already).
|
||||||
if (target.getAssignedDistributionSet() != null
|
if (target.getAssignedDistributionSet() != null
|
||||||
|
|||||||
@@ -151,7 +151,8 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
|||||||
|
|
||||||
for (final JpaRolloutGroup rolloutGroup : rolloutGroups) {
|
for (final JpaRolloutGroup rolloutGroup : rolloutGroups) {
|
||||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
||||||
allStatesForRollout.get(rolloutGroup.getId()), Long.valueOf(rolloutGroup.getTotalTargets()));
|
allStatesForRollout.get(rolloutGroup.getId()), Long.valueOf(rolloutGroup.getTotalTargets()),
|
||||||
|
rolloutGroup.getRollout().getActionType());
|
||||||
rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
|
rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +178,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
|
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
|
||||||
Long.valueOf(jpaRolloutGroup.getTotalTargets()));
|
Long.valueOf(jpaRolloutGroup.getTotalTargets()), jpaRolloutGroup.getRollout().getActionType());
|
||||||
jpaRolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
|
jpaRolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
|
||||||
return rolloutGroup;
|
return rolloutGroup;
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import static org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate.addSuccessAndErrorConditionsAndActions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JPA implementation of {@link RolloutManagement}.
|
* JPA implementation of {@link RolloutManagement}.
|
||||||
*/
|
*/
|
||||||
@@ -126,6 +128,12 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
private static final List<RolloutStatus> ACTIVE_ROLLOUTS = Arrays.asList(RolloutStatus.CREATING,
|
private static final List<RolloutStatus> ACTIVE_ROLLOUTS = Arrays.asList(RolloutStatus.CREATING,
|
||||||
RolloutStatus.DELETING, RolloutStatus.STARTING, RolloutStatus.READY, RolloutStatus.RUNNING);
|
RolloutStatus.DELETING, RolloutStatus.STARTING, RolloutStatus.READY, RolloutStatus.RUNNING);
|
||||||
|
|
||||||
|
// In case of DOWNLOAD_ONLY, actions can be finished with DOWNLOADED status.
|
||||||
|
private static final List<Status> DOWNLOAD_ONLY_ACTION_TERMINATION_STATUSES = Arrays.asList(Status.ERROR,
|
||||||
|
Status.FINISHED, Status.CANCELED, Status.DOWNLOADED);
|
||||||
|
private static final List<Status> DEFAULT_ACTION_TERMINATION_STATUSES = Arrays.asList(Status.ERROR, Status.FINISHED,
|
||||||
|
Status.CANCELED);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RolloutRepository rolloutRepository;
|
private RolloutRepository rolloutRepository;
|
||||||
|
|
||||||
@@ -251,17 +259,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
group.setParent(lastSavedGroup);
|
group.setParent(lastSavedGroup);
|
||||||
group.setStatus(RolloutGroupStatus.CREATING);
|
group.setStatus(RolloutGroupStatus.CREATING);
|
||||||
|
|
||||||
group.setSuccessCondition(conditions.getSuccessCondition());
|
addSuccessAndErrorConditionsAndActions(group, conditions);
|
||||||
group.setSuccessConditionExp(conditions.getSuccessConditionExp());
|
|
||||||
|
|
||||||
group.setSuccessAction(conditions.getSuccessAction());
|
|
||||||
group.setSuccessActionExp(conditions.getSuccessActionExp());
|
|
||||||
|
|
||||||
group.setErrorCondition(conditions.getErrorCondition());
|
|
||||||
group.setErrorConditionExp(conditions.getErrorConditionExp());
|
|
||||||
|
|
||||||
group.setErrorAction(conditions.getErrorAction());
|
|
||||||
group.setErrorActionExp(conditions.getErrorActionExp());
|
|
||||||
|
|
||||||
group.setTargetPercentage(1.0F / (amountOfGroups - i) * 100);
|
group.setTargetPercentage(1.0F / (amountOfGroups - i) * 100);
|
||||||
|
|
||||||
@@ -310,17 +308,10 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
group.setTargetFilterQuery("");
|
group.setTargetFilterQuery("");
|
||||||
}
|
}
|
||||||
|
|
||||||
group.setSuccessCondition(srcGroup.getSuccessCondition());
|
addSuccessAndErrorConditionsAndActions(group, srcGroup.getSuccessCondition(),
|
||||||
group.setSuccessConditionExp(srcGroup.getSuccessConditionExp());
|
srcGroup.getSuccessConditionExp(), srcGroup.getSuccessAction(), srcGroup.getSuccessActionExp(),
|
||||||
|
srcGroup.getErrorCondition(), srcGroup.getErrorConditionExp(), srcGroup.getErrorAction(),
|
||||||
group.setSuccessAction(srcGroup.getSuccessAction());
|
srcGroup.getErrorActionExp());
|
||||||
group.setSuccessActionExp(srcGroup.getSuccessActionExp());
|
|
||||||
|
|
||||||
group.setErrorCondition(srcGroup.getErrorCondition());
|
|
||||||
group.setErrorConditionExp(srcGroup.getErrorConditionExp());
|
|
||||||
|
|
||||||
group.setErrorAction(srcGroup.getErrorAction());
|
|
||||||
group.setErrorActionExp(srcGroup.getErrorActionExp());
|
|
||||||
|
|
||||||
lastSavedGroup = rolloutGroupRepository.save(group);
|
lastSavedGroup = rolloutGroupRepository.save(group);
|
||||||
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup, rollout);
|
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup, rollout);
|
||||||
@@ -760,9 +751,11 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isRolloutGroupComplete(final JpaRollout rollout, final JpaRolloutGroup rolloutGroup) {
|
private boolean isRolloutGroupComplete(final JpaRollout rollout, final JpaRolloutGroup rolloutGroup) {
|
||||||
final Long actionsLeftForRollout = actionRepository
|
final Long actionsLeftForRollout = ActionType.DOWNLOAD_ONLY.equals(rollout.getActionType())
|
||||||
.countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(rollout, rolloutGroup,
|
? actionRepository.countByRolloutAndRolloutGroupAndStatusNotIn(rollout, rolloutGroup,
|
||||||
Action.Status.ERROR, Action.Status.FINISHED, Action.Status.CANCELED);
|
DOWNLOAD_ONLY_ACTION_TERMINATION_STATUSES)
|
||||||
|
: actionRepository.countByRolloutAndRolloutGroupAndStatusNotIn(rollout, rolloutGroup,
|
||||||
|
DEFAULT_ACTION_TERMINATION_STATUSES);
|
||||||
return actionsLeftForRollout == 0;
|
return actionsLeftForRollout == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1070,7 +1063,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
|
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
|
||||||
rollout.get().getTotalTargets());
|
rollout.get().getTotalTargets(), rollout.get().getActionType());
|
||||||
((JpaRollout) rollout.get()).setTotalTargetCountStatus(totalTargetCountStatus);
|
((JpaRollout) rollout.get()).setTotalTargetCountStatus(totalTargetCountStatus);
|
||||||
return rollout;
|
return rollout;
|
||||||
}
|
}
|
||||||
@@ -1112,7 +1105,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
if (allStatesForRollout != null) {
|
if (allStatesForRollout != null) {
|
||||||
rollouts.forEach(rollout -> {
|
rollouts.forEach(rollout -> {
|
||||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
||||||
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets());
|
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets(), rollout.getActionType());
|
||||||
rollout.setTotalTargetCountStatus(totalTargetCountStatus);
|
rollout.setTotalTargetCountStatus(totalTargetCountStatus);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ package org.eclipse.hawkbit.repository.jpa.builder;
|
|||||||
import org.eclipse.hawkbit.repository.builder.AbstractRolloutGroupCreate;
|
import org.eclipse.hawkbit.repository.builder.AbstractRolloutGroupCreate;
|
||||||
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||||
|
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||||
|
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
|
||||||
|
|
||||||
public class JpaRolloutGroupCreate extends AbstractRolloutGroupCreate<RolloutGroupCreate>
|
public class JpaRolloutGroupCreate extends AbstractRolloutGroupCreate<RolloutGroupCreate>
|
||||||
implements RolloutGroupCreate {
|
implements RolloutGroupCreate {
|
||||||
@@ -30,20 +32,66 @@ public class JpaRolloutGroupCreate extends AbstractRolloutGroupCreate<RolloutGro
|
|||||||
group.setTargetPercentage(targetPercentage);
|
group.setTargetPercentage(targetPercentage);
|
||||||
|
|
||||||
if (conditions != null) {
|
if (conditions != null) {
|
||||||
group.setSuccessCondition(conditions.getSuccessCondition());
|
addSuccessAndErrorConditionsAndActions(group, conditions);
|
||||||
group.setSuccessConditionExp(conditions.getSuccessConditionExp());
|
|
||||||
|
|
||||||
group.setSuccessAction(conditions.getSuccessAction());
|
|
||||||
group.setSuccessActionExp(conditions.getSuccessActionExp());
|
|
||||||
|
|
||||||
group.setErrorCondition(conditions.getErrorCondition());
|
|
||||||
group.setErrorConditionExp(conditions.getErrorConditionExp());
|
|
||||||
|
|
||||||
group.setErrorAction(conditions.getErrorAction());
|
|
||||||
group.setErrorActionExp(conditions.getErrorActionExp());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Success And Error conditions for the rollout group
|
||||||
|
*
|
||||||
|
* @param group
|
||||||
|
* The Rollout group
|
||||||
|
* @param conditions
|
||||||
|
* The Rollout Success and Error Conditions
|
||||||
|
*/
|
||||||
|
public static void addSuccessAndErrorConditionsAndActions(final JpaRolloutGroup group,
|
||||||
|
final RolloutGroupConditions conditions) {
|
||||||
|
addSuccessAndErrorConditionsAndActions(group, conditions.getSuccessCondition(),
|
||||||
|
conditions.getSuccessConditionExp(), conditions.getSuccessAction(), conditions.getSuccessActionExp(),
|
||||||
|
conditions.getErrorCondition(), conditions.getErrorConditionExp(), conditions.getErrorAction(),
|
||||||
|
conditions.getErrorActionExp());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Success And Error conditions for the rollout group
|
||||||
|
*
|
||||||
|
* @param group
|
||||||
|
* The Rollout group
|
||||||
|
* @param successCondition
|
||||||
|
* The Rollout group success condition
|
||||||
|
* @param successConditionExp
|
||||||
|
* The Rollout group success expression
|
||||||
|
* @param successAction
|
||||||
|
* The Rollout group success action
|
||||||
|
* @param successActionExp
|
||||||
|
* The Rollout group success action expression
|
||||||
|
* @param errorCondition
|
||||||
|
* The Rollout group error condition
|
||||||
|
* @param errorConditionExp
|
||||||
|
* The Rollout group error expression
|
||||||
|
* @param errorAction
|
||||||
|
* The Rollout group error action
|
||||||
|
* @param errorActionExp
|
||||||
|
* The Rollout group error action expression
|
||||||
|
*/
|
||||||
|
public static void addSuccessAndErrorConditionsAndActions(final JpaRolloutGroup group,
|
||||||
|
final RolloutGroup.RolloutGroupSuccessCondition successCondition, final String successConditionExp,
|
||||||
|
final RolloutGroup.RolloutGroupSuccessAction successAction, final String successActionExp,
|
||||||
|
final RolloutGroup.RolloutGroupErrorCondition errorCondition, final String errorConditionExp,
|
||||||
|
final RolloutGroup.RolloutGroupErrorAction errorAction, final String errorActionExp) {
|
||||||
|
group.setSuccessCondition(successCondition);
|
||||||
|
group.setSuccessConditionExp(successConditionExp);
|
||||||
|
|
||||||
|
group.setSuccessAction(successAction);
|
||||||
|
group.setSuccessActionExp(successActionExp);
|
||||||
|
|
||||||
|
group.setErrorCondition(errorCondition);
|
||||||
|
group.setErrorConditionExp(errorConditionExp);
|
||||||
|
|
||||||
|
group.setErrorAction(errorAction);
|
||||||
|
group.setErrorActionExp(errorActionExp);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,8 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
|||||||
@ObjectTypeConverter(name = "actionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
|
@ObjectTypeConverter(name = "actionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
|
||||||
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
|
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
|
||||||
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
|
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
|
||||||
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2") })
|
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2"),
|
||||||
|
@ConversionValue(objectValue = "DOWNLOAD_ONLY", dataValue = "3") })
|
||||||
@Convert("actionType")
|
@Convert("actionType")
|
||||||
@NotNull
|
@NotNull
|
||||||
private ActionType actionType;
|
private ActionType actionType;
|
||||||
|
|||||||
@@ -102,7 +102,8 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
|||||||
@ObjectTypeConverter(name = "actionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
|
@ObjectTypeConverter(name = "actionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
|
||||||
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
|
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
|
||||||
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
|
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
|
||||||
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2") })
|
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2"),
|
||||||
|
@ConversionValue(objectValue = "DOWNLOAD_ONLY", dataValue = "3") })
|
||||||
@Convert("actionType")
|
@Convert("actionType")
|
||||||
@NotNull
|
@NotNull
|
||||||
private ActionType actionType = ActionType.FORCED;
|
private ActionType actionType = ActionType.FORCED;
|
||||||
@@ -224,7 +225,7 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
|||||||
@Override
|
@Override
|
||||||
public TotalTargetCountStatus getTotalTargetCountStatus() {
|
public TotalTargetCountStatus getTotalTargetCountStatus() {
|
||||||
if (totalTargetCountStatus == null) {
|
if (totalTargetCountStatus == null) {
|
||||||
totalTargetCountStatus = new TotalTargetCountStatus(totalTargets);
|
totalTargetCountStatus = new TotalTargetCountStatus(totalTargets, actionType);
|
||||||
}
|
}
|
||||||
return totalTargetCountStatus;
|
return totalTargetCountStatus;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
|
|||||||
@Override
|
@Override
|
||||||
public TotalTargetCountStatus getTotalTargetCountStatus() {
|
public TotalTargetCountStatus getTotalTargetCountStatus() {
|
||||||
if (totalTargetCountStatus == null) {
|
if (totalTargetCountStatus == null) {
|
||||||
totalTargetCountStatus = new TotalTargetCountStatus(Long.valueOf(totalTargets));
|
totalTargetCountStatus = new TotalTargetCountStatus(Long.valueOf(totalTargets), rollout.getActionType());
|
||||||
}
|
}
|
||||||
return totalTargetCountStatus;
|
return totalTargetCountStatus;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,9 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
|
|||||||
@Column(name = "auto_assign_action_type", nullable = true)
|
@Column(name = "auto_assign_action_type", nullable = true)
|
||||||
@ObjectTypeConverter(name = "autoAssignActionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
|
@ObjectTypeConverter(name = "autoAssignActionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
|
||||||
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
|
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
|
||||||
@ConversionValue(objectValue = "SOFT", dataValue = "1") })
|
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
|
||||||
|
// Conversion for 'TIMEFORCED' is disabled because it is not permitted in autoAssignment
|
||||||
|
@ConversionValue(objectValue = "DOWNLOAD_ONLY", dataValue = "3")})
|
||||||
@Convert("autoAssignActionType")
|
@Convert("autoAssignActionType")
|
||||||
private ActionType autoAssignActionType;
|
private ActionType autoAssignActionType;
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,10 @@ public class ThresholdRolloutGroupSuccessCondition implements RolloutGroupCondit
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Action.Status completeActionStatus = Action.ActionType.DOWNLOAD_ONLY.equals(rollout.getActionType()) ?
|
||||||
|
Action.Status.DOWNLOADED : Action.Status.FINISHED;
|
||||||
final long finished = this.actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rollout.getId(),
|
final long finished = this.actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rollout.getId(),
|
||||||
rolloutGroup.getId(), Action.Status.FINISHED);
|
rolloutGroup.getId(), completeActionStatus);
|
||||||
try {
|
try {
|
||||||
final Integer threshold = Integer.valueOf(expression);
|
final Integer threshold = Integer.valueOf(expression);
|
||||||
// calculate threshold
|
// calculate threshold
|
||||||
|
|||||||
@@ -10,14 +10,13 @@ package org.eclipse.hawkbit.repository.event.remote;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import java.util.AbstractMap.SimpleImmutableEntry;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
|
import org.eclipse.hawkbit.repository.model.ActionProperties;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -69,10 +68,10 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
|
|||||||
private void assertTargetAssignDistributionSetEvent(final Action action,
|
private void assertTargetAssignDistributionSetEvent(final Action action,
|
||||||
final TargetAssignDistributionSetEvent underTest) {
|
final TargetAssignDistributionSetEvent underTest) {
|
||||||
|
|
||||||
final Entry<String, Long> entry = new SimpleImmutableEntry(action.getTarget().getControllerId(),
|
assertThat(underTest.getActions().size()).isEqualTo(1);
|
||||||
action.getId());
|
ActionProperties actionProperties = underTest.getActions().get(action.getTarget().getControllerId());
|
||||||
|
assertThat(actionProperties).isNotNull();
|
||||||
assertThat(underTest.getActions()).containsExactly(entry);
|
assertThat(actionProperties).isEqualToComparingFieldByField(new ActionProperties(action));
|
||||||
assertThat(underTest.getDistributionSetId()).isEqualTo(action.getDistributionSet().getId());
|
assertThat(underTest.getDistributionSetId()).isEqualTo(action.getDistributionSet().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ package org.eclipse.hawkbit.repository.jpa;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS;
|
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS;
|
||||||
|
import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY;
|
||||||
|
import static org.eclipse.hawkbit.repository.test.util.TestdataFactory.DEFAULT_CONTROLLER_ID;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@@ -20,6 +22,7 @@ import java.util.Collections;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
@@ -42,19 +45,20 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
|||||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||||
import org.eclipse.hawkbit.repository.exception.InvalidTargetAttributeException;
|
import org.eclipse.hawkbit.repository.exception.InvalidTargetAttributeException;
|
||||||
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||||
import org.eclipse.hawkbit.repository.model.ArtifactUpload;
|
import org.eclipse.hawkbit.repository.model.ArtifactUpload;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
|
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||||
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||||
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
|
|
||||||
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
|
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -148,7 +152,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
||||||
Action.Status.FINISHED, Action.Status.FINISHED, false);
|
Action.Status.FINISHED, Action.Status.FINISHED, false);
|
||||||
|
|
||||||
assertThat(actionStatusRepository.count()).isEqualTo(7);
|
assertThat(actionStatusRepository.count()).isEqualTo(7);
|
||||||
@@ -199,7 +203,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
||||||
Action.Status.FINISHED, Action.Status.FINISHED, false);
|
Action.Status.FINISHED, Action.Status.FINISHED, false);
|
||||||
|
|
||||||
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
||||||
@@ -224,7 +228,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
// expected
|
// expected
|
||||||
}
|
}
|
||||||
|
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.RUNNING, Action.Status.RUNNING, true);
|
Action.Status.RUNNING, Action.Status.RUNNING, true);
|
||||||
|
|
||||||
assertThat(actionStatusRepository.count()).isEqualTo(1);
|
assertThat(actionStatusRepository.count()).isEqualTo(1);
|
||||||
@@ -245,14 +249,14 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
final Long actionId = createTargetAndAssignDs();
|
final Long actionId = createTargetAndAssignDs();
|
||||||
|
|
||||||
deploymentManagement.cancelAction(actionId);
|
deploymentManagement.cancelAction(actionId);
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.CANCELING, Action.Status.CANCELING, true);
|
Action.Status.CANCELING, Action.Status.CANCELING, true);
|
||||||
|
|
||||||
simulateIntermediateStatusOnCancellation(actionId);
|
simulateIntermediateStatusOnCancellation(actionId);
|
||||||
|
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
||||||
Action.Status.CANCELED, Action.Status.FINISHED, false);
|
Action.Status.CANCELED, Action.Status.FINISHED, false);
|
||||||
|
|
||||||
assertThat(actionStatusRepository.count()).isEqualTo(8);
|
assertThat(actionStatusRepository.count()).isEqualTo(8);
|
||||||
@@ -272,14 +276,14 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
final Long actionId = createTargetAndAssignDs();
|
final Long actionId = createTargetAndAssignDs();
|
||||||
|
|
||||||
deploymentManagement.cancelAction(actionId);
|
deploymentManagement.cancelAction(actionId);
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.CANCELING, Action.Status.CANCELING, true);
|
Action.Status.CANCELING, Action.Status.CANCELING, true);
|
||||||
|
|
||||||
simulateIntermediateStatusOnCancellation(actionId);
|
simulateIntermediateStatusOnCancellation(actionId);
|
||||||
|
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.CANCELED));
|
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.CANCELED));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
||||||
Action.Status.CANCELED, Action.Status.CANCELED, false);
|
Action.Status.CANCELED, Action.Status.CANCELED, false);
|
||||||
|
|
||||||
assertThat(actionStatusRepository.count()).isEqualTo(8);
|
assertThat(actionStatusRepository.count()).isEqualTo(8);
|
||||||
@@ -300,14 +304,14 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
final Long actionId = createTargetAndAssignDs();
|
final Long actionId = createTargetAndAssignDs();
|
||||||
|
|
||||||
deploymentManagement.cancelAction(actionId);
|
deploymentManagement.cancelAction(actionId);
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.CANCELING, Action.Status.CANCELING, true);
|
Action.Status.CANCELING, Action.Status.CANCELING, true);
|
||||||
|
|
||||||
simulateIntermediateStatusOnCancellation(actionId);
|
simulateIntermediateStatusOnCancellation(actionId);
|
||||||
|
|
||||||
controllerManagement.addCancelActionStatus(
|
controllerManagement.addCancelActionStatus(
|
||||||
entityFactory.actionStatus().create(actionId).status(Action.Status.CANCEL_REJECTED));
|
entityFactory.actionStatus().create(actionId).status(Action.Status.CANCEL_REJECTED));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.RUNNING, Action.Status.CANCEL_REJECTED, true);
|
Action.Status.RUNNING, Action.Status.CANCEL_REJECTED, true);
|
||||||
|
|
||||||
assertThat(actionStatusRepository.count()).isEqualTo(8);
|
assertThat(actionStatusRepository.count()).isEqualTo(8);
|
||||||
@@ -328,14 +332,14 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
final Long actionId = createTargetAndAssignDs();
|
final Long actionId = createTargetAndAssignDs();
|
||||||
|
|
||||||
deploymentManagement.cancelAction(actionId);
|
deploymentManagement.cancelAction(actionId);
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.CANCELING, Action.Status.CANCELING, true);
|
Action.Status.CANCELING, Action.Status.CANCELING, true);
|
||||||
|
|
||||||
simulateIntermediateStatusOnCancellation(actionId);
|
simulateIntermediateStatusOnCancellation(actionId);
|
||||||
|
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
|
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.RUNNING, Action.Status.ERROR, true);
|
Action.Status.RUNNING, Action.Status.ERROR, true);
|
||||||
|
|
||||||
assertThat(actionStatusRepository.count()).isEqualTo(8);
|
assertThat(actionStatusRepository.count()).isEqualTo(8);
|
||||||
@@ -346,39 +350,64 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
private Long createTargetAndAssignDs() {
|
private Long createTargetAndAssignDs() {
|
||||||
final Long dsId = testdataFactory.createDistributionSet().getId();
|
final Long dsId = testdataFactory.createDistributionSet().getId();
|
||||||
testdataFactory.createTarget();
|
testdataFactory.createTarget();
|
||||||
assignDistributionSet(dsId, TestdataFactory.DEFAULT_CONTROLLER_ID);
|
assignDistributionSet(dsId, DEFAULT_CONTROLLER_ID);
|
||||||
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getUpdateStatus())
|
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getUpdateStatus())
|
||||||
.isEqualTo(TargetUpdateStatus.PENDING);
|
.isEqualTo(TargetUpdateStatus.PENDING);
|
||||||
|
|
||||||
return deploymentManagement.findActiveActionsByTarget(PAGE, TestdataFactory.DEFAULT_CONTROLLER_ID).getContent()
|
return deploymentManagement.findActiveActionsByTarget(PAGE, DEFAULT_CONTROLLER_ID).getContent()
|
||||||
.get(0).getId();
|
.get(0).getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private Long createAndAssignDsAsDownloadOnly(final String dsName, final String defaultControllerId) {
|
||||||
|
final Long dsId = testdataFactory.createDistributionSet(dsName).getId();
|
||||||
|
assignDistributionSet(dsId, defaultControllerId, DOWNLOAD_ONLY);
|
||||||
|
assertThat(targetManagement.getByControllerID(defaultControllerId).get().getUpdateStatus())
|
||||||
|
.isEqualTo(TargetUpdateStatus.PENDING);
|
||||||
|
|
||||||
|
final Long id = deploymentManagement.findActiveActionsByTarget(PAGE, defaultControllerId).getContent()
|
||||||
|
.get(0).getId();
|
||||||
|
assertThat(id).isNotNull();
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private Long assignDs(final Long dsId, final String defaultControllerId, final Action.ActionType actionType) {
|
||||||
|
assignDistributionSet(dsId, defaultControllerId, actionType);
|
||||||
|
assertThat(targetManagement.getByControllerID(defaultControllerId).get().getUpdateStatus())
|
||||||
|
.isEqualTo(TargetUpdateStatus.PENDING);
|
||||||
|
|
||||||
|
final Long id = deploymentManagement.findActiveActionsByTarget(PAGE, defaultControllerId).getContent()
|
||||||
|
.get(0).getId();
|
||||||
|
assertThat(id).isNotNull();
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
@Step
|
@Step
|
||||||
private void simulateIntermediateStatusOnCancellation(final Long actionId) {
|
private void simulateIntermediateStatusOnCancellation(final Long actionId) {
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
|
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.CANCELING, Action.Status.RUNNING, true);
|
Action.Status.CANCELING, Action.Status.RUNNING, true);
|
||||||
|
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOAD));
|
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOAD));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.CANCELING, Action.Status.DOWNLOAD, true);
|
Action.Status.CANCELING, Action.Status.DOWNLOAD, true);
|
||||||
|
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOADED));
|
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOADED));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.CANCELING, Action.Status.DOWNLOADED, true);
|
Action.Status.CANCELING, Action.Status.DOWNLOADED, true);
|
||||||
|
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RETRIEVED));
|
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RETRIEVED));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.CANCELING, Action.Status.RETRIEVED, true);
|
Action.Status.CANCELING, Action.Status.RETRIEVED, true);
|
||||||
|
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING));
|
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.CANCELING, Action.Status.WARNING, true);
|
Action.Status.CANCELING, Action.Status.WARNING, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,26 +415,26 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
private void simulateIntermediateStatusOnUpdate(final Long actionId) {
|
private void simulateIntermediateStatusOnUpdate(final Long actionId) {
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.RUNNING, Action.Status.RUNNING, true);
|
Action.Status.RUNNING, Action.Status.RUNNING, true);
|
||||||
|
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOAD));
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOAD));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.RUNNING, Action.Status.DOWNLOAD, true);
|
Action.Status.RUNNING, Action.Status.DOWNLOAD, true);
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOADED));
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOADED));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.RUNNING, Action.Status.DOWNLOADED, true);
|
Action.Status.RUNNING, Action.Status.DOWNLOADED, true);
|
||||||
|
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RETRIEVED));
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RETRIEVED));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.RUNNING, Action.Status.RETRIEVED, true);
|
Action.Status.RUNNING, Action.Status.RETRIEVED, true);
|
||||||
|
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING));
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.RUNNING, Action.Status.WARNING, true);
|
Action.Status.RUNNING, Action.Status.WARNING, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,7 +538,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Controller trys to finish an update process after it has been finished by an error action status.")
|
@Description("Controller tries to finish an update process after it has been finished by an error action status.")
|
||||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
|
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
|
||||||
@@ -522,12 +551,12 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
// test and verify
|
// test and verify
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
Action.Status.RUNNING, Action.Status.RUNNING, true);
|
Action.Status.RUNNING, Action.Status.RUNNING, true);
|
||||||
|
|
||||||
controllerManagement
|
controllerManagement
|
||||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
|
||||||
Action.Status.ERROR, Action.Status.ERROR, false);
|
Action.Status.ERROR, Action.Status.ERROR, false);
|
||||||
|
|
||||||
// try with disabled late feedback
|
// try with disabled late feedback
|
||||||
@@ -536,7 +565,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||||
|
|
||||||
// test
|
// test
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
|
||||||
Action.Status.ERROR, Action.Status.ERROR, false);
|
Action.Status.ERROR, Action.Status.ERROR, false);
|
||||||
|
|
||||||
// try with enabled late feedback - should not make a difference as it
|
// try with enabled late feedback - should not make a difference as it
|
||||||
@@ -546,7 +575,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||||
|
|
||||||
// test
|
// test
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
|
||||||
Action.Status.ERROR, Action.Status.ERROR, false);
|
Action.Status.ERROR, Action.Status.ERROR, false);
|
||||||
|
|
||||||
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
||||||
@@ -572,7 +601,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||||
|
|
||||||
// test
|
// test
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
||||||
Action.Status.FINISHED, Action.Status.FINISHED, false);
|
Action.Status.FINISHED, Action.Status.FINISHED, false);
|
||||||
|
|
||||||
// try with enabled late feedback - should not make a difference as it
|
// try with enabled late feedback - should not make a difference as it
|
||||||
@@ -582,7 +611,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||||
|
|
||||||
// test
|
// test
|
||||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
||||||
Action.Status.FINISHED, Action.Status.FINISHED, false);
|
Action.Status.FINISHED, Action.Status.FINISHED, false);
|
||||||
|
|
||||||
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
||||||
@@ -609,7 +638,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING));
|
entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING));
|
||||||
|
|
||||||
// nothing changed as "feedback after close" is disabled
|
// nothing changed as "feedback after close" is disabled
|
||||||
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getUpdateStatus())
|
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getUpdateStatus())
|
||||||
.isEqualTo(TargetUpdateStatus.IN_SYNC);
|
.isEqualTo(TargetUpdateStatus.IN_SYNC);
|
||||||
|
|
||||||
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
||||||
@@ -635,7 +664,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING));
|
entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING));
|
||||||
|
|
||||||
// nothing changed as "feedback after close" is disabled
|
// nothing changed as "feedback after close" is disabled
|
||||||
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getUpdateStatus())
|
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getUpdateStatus())
|
||||||
.isEqualTo(TargetUpdateStatus.IN_SYNC);
|
.isEqualTo(TargetUpdateStatus.IN_SYNC);
|
||||||
|
|
||||||
// however, additional action status has been stored
|
// however, additional action status has been stored
|
||||||
@@ -989,4 +1018,312 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that a DOWNLOAD_ONLY action is not marked complete when the controller reports DOWNLOAD")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
|
||||||
|
public void controllerReportsDownloadForDownloadOnlyAction() {
|
||||||
|
testdataFactory.createTarget();
|
||||||
|
final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID);
|
||||||
|
assertThat(actionId).isNotNull();
|
||||||
|
controllerManagement
|
||||||
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.DOWNLOAD));
|
||||||
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||||
|
Action.Status.RUNNING, Action.Status.DOWNLOAD, true);
|
||||||
|
|
||||||
|
assertThat(actionStatusRepository.count()).isEqualTo(2);
|
||||||
|
assertThat(controllerManagement.findActionStatusByAction(PAGE, actionId).getNumberOfElements()).isEqualTo(2);
|
||||||
|
assertThat(actionRepository.activeActionExistsForControllerId(DEFAULT_CONTROLLER_ID))
|
||||||
|
.isEqualTo(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that a DOWNLOAD_ONLY action is marked complete once the controller reports DOWNLOADED")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
|
||||||
|
@Expect(type = ActionUpdatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
|
||||||
|
public void controllerReportsDownloadedForDownloadOnlyAction() {
|
||||||
|
testdataFactory.createTarget();
|
||||||
|
final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID);
|
||||||
|
assertThat(actionId).isNotNull();
|
||||||
|
controllerManagement
|
||||||
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.DOWNLOADED));
|
||||||
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
||||||
|
Action.Status.DOWNLOADED, Action.Status.DOWNLOADED, false);
|
||||||
|
|
||||||
|
assertThat(actionStatusRepository.count()).isEqualTo(2);
|
||||||
|
assertThat(controllerManagement.findActionStatusByAction(PAGE, actionId).getNumberOfElements()).isEqualTo(2);
|
||||||
|
assertThat(actionRepository.activeActionExistsForControllerId(DEFAULT_CONTROLLER_ID))
|
||||||
|
.isEqualTo(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that a controller can report a FINISHED event for a DOWNLOAD_ONLY non-active action.")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 3),
|
||||||
|
@Expect(type = TargetAttributesRequestedEvent.class, count = 2),
|
||||||
|
@Expect(type = ActionUpdatedEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
|
||||||
|
public void controllerReportsActionFinishedForDownloadOnlyActionThatIsNotActive() {
|
||||||
|
testdataFactory.createTarget();
|
||||||
|
final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID);
|
||||||
|
assertThat(actionId).isNotNull();
|
||||||
|
finishDownloadOnlyUpdateAndSendUpdateActionStatus(actionId, Status.FINISHED);
|
||||||
|
|
||||||
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
||||||
|
Action.Status.FINISHED, Action.Status.FINISHED, false);
|
||||||
|
|
||||||
|
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
||||||
|
assertThat(controllerManagement.findActionStatusByAction(PAGE, actionId).getNumberOfElements()).isEqualTo(3);
|
||||||
|
assertThat(actionRepository.activeActionExistsForControllerId(DEFAULT_CONTROLLER_ID))
|
||||||
|
.isEqualTo(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that multiple DOWNLOADED events for a DOWNLOAD_ONLY action are handled.")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetAttributesRequestedEvent.class, count = 3),
|
||||||
|
@Expect(type = ActionUpdatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
|
||||||
|
public void controllerReportsMultipleDownloadedForDownloadOnlyAction() {
|
||||||
|
testdataFactory.createTarget();
|
||||||
|
final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID);
|
||||||
|
assertThat(actionId).isNotNull();
|
||||||
|
IntStream.range(0, 3).forEach(i -> controllerManagement
|
||||||
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.DOWNLOADED)));
|
||||||
|
|
||||||
|
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
||||||
|
Status.DOWNLOADED, Status.DOWNLOADED, false);
|
||||||
|
|
||||||
|
assertThat(actionStatusRepository.count()).isEqualTo(4);
|
||||||
|
assertThat(controllerManagement.findActionStatusByAction(PAGE, actionId).getNumberOfElements()).isEqualTo(4);
|
||||||
|
assertThat(actionRepository.activeActionExistsForControllerId(DEFAULT_CONTROLLER_ID))
|
||||||
|
.isEqualTo(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = QuotaExceededException.class)
|
||||||
|
@Description("Verifies that quota is asserted when a controller reports too many DOWNLOADED events for a " +
|
||||||
|
"DOWNLOAD_ONLY action.")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetAttributesRequestedEvent.class, count = 9),
|
||||||
|
@Expect(type = ActionUpdatedEvent.class, count = 1),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
|
||||||
|
public void quotaExceptionWhencontrollerReportsTooManyDownloadedMessagesForDownloadOnlyAction() {
|
||||||
|
final int maxMessages = quotaManagement.getMaxMessagesPerActionStatus();
|
||||||
|
testdataFactory.createTarget();
|
||||||
|
final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID);
|
||||||
|
assertThat(actionId).isNotNull();
|
||||||
|
|
||||||
|
IntStream.range(0, maxMessages).forEach(i -> controllerManagement
|
||||||
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.DOWNLOADED)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that quota is enforced for UpdateActionStatus events for DOWNLOAD_ONLY assignments.")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetAttributesRequestedEvent.class, count = 9),
|
||||||
|
@Expect(type = ActionUpdatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
|
||||||
|
public void quotaEceededExceptionWhenControllerReportsTooManyUpdateActionStatusMessagesForDownloadOnlyAction() {
|
||||||
|
final int maxMessages = quotaManagement.getMaxMessagesPerActionStatus();
|
||||||
|
testdataFactory.createTarget();
|
||||||
|
final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID);
|
||||||
|
assertThat(actionId).isNotNull();
|
||||||
|
|
||||||
|
try {
|
||||||
|
IntStream.range(0, maxMessages).forEach(i -> controllerManagement
|
||||||
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.DOWNLOADED)));
|
||||||
|
fail("No QuotaExceededException thrown for too many DOWNLOADED updateActionStatus updates");
|
||||||
|
} catch (QuotaExceededException e) { }
|
||||||
|
|
||||||
|
try {
|
||||||
|
IntStream.range(0, maxMessages).forEach(i -> controllerManagement
|
||||||
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.ERROR)));
|
||||||
|
fail("No QuotaExceededException thrown for too many ERROR updateActionStatus updates");
|
||||||
|
} catch (QuotaExceededException e) { }
|
||||||
|
|
||||||
|
try {
|
||||||
|
IntStream.range(0, maxMessages).forEach(i -> controllerManagement
|
||||||
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.FINISHED)));
|
||||||
|
fail("No QuotaExceededException thrown for too many FINISHED updateActionStatus updates");
|
||||||
|
} catch (QuotaExceededException e) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that quota is enforced for UpdateActionStatus events for FORCED assignments.")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
|
||||||
|
public void quotaEceededExceptionWhenControllerReportsTooManyUpdateActionStatusMessagesForForced() {
|
||||||
|
final int maxMessages = quotaManagement.getMaxMessagesPerActionStatus();
|
||||||
|
final Long actionId = createTargetAndAssignDs();
|
||||||
|
assertThat(actionId).isNotNull();
|
||||||
|
|
||||||
|
try {
|
||||||
|
IntStream.range(0, maxMessages).forEach(i -> controllerManagement
|
||||||
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.DOWNLOADED)));
|
||||||
|
fail("No QuotaExceededException thrown for too many DOWNLOADED updateActionStatus updates");
|
||||||
|
} catch (QuotaExceededException e) { }
|
||||||
|
|
||||||
|
try {
|
||||||
|
IntStream.range(0, maxMessages).forEach(i -> controllerManagement
|
||||||
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.ERROR)));
|
||||||
|
fail("No QuotaExceededException thrown for too many ERROR updateActionStatus updates");
|
||||||
|
} catch (QuotaExceededException e) { }
|
||||||
|
|
||||||
|
try {
|
||||||
|
IntStream.range(0, maxMessages).forEach(i -> controllerManagement
|
||||||
|
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.FINISHED)));
|
||||||
|
fail("No QuotaExceededException thrown for too many FINISHED updateActionStatus updates");
|
||||||
|
} catch (QuotaExceededException e) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that a target can report FINISHED/ERROR updates for DOWNLOAD_ONLY assignments regardless of " +
|
||||||
|
"repositoryProperties.rejectActionStatusForClosedAction value.")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 4),
|
||||||
|
@Expect(type = ActionCreatedEvent.class, count = 4),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 12),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 4),
|
||||||
|
@Expect(type = TargetAttributesRequestedEvent.class, count = 6),
|
||||||
|
@Expect(type = ActionUpdatedEvent.class, count = 8),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 12) })
|
||||||
|
public void targetCanAlwaysReportFinishedOrErrorAfterActionIsClosedForDownloadOnlyAssignments() {
|
||||||
|
|
||||||
|
testdataFactory.createTarget();
|
||||||
|
|
||||||
|
// allow actionStatusUpdates for closed actions
|
||||||
|
repositoryProperties.setRejectActionStatusForClosedAction(false);
|
||||||
|
|
||||||
|
final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs1", DEFAULT_CONTROLLER_ID);
|
||||||
|
assertThat(actionId).isNotNull();
|
||||||
|
finishDownloadOnlyUpdateAndSendUpdateActionStatus(actionId, Status.FINISHED);
|
||||||
|
|
||||||
|
final Long actionId2 = createAndAssignDsAsDownloadOnly("downloadOnlyDs2", DEFAULT_CONTROLLER_ID);
|
||||||
|
assertThat(actionId).isNotNull();
|
||||||
|
finishDownloadOnlyUpdateAndSendUpdateActionStatus(actionId2, Status.ERROR);
|
||||||
|
|
||||||
|
// disallow actionStatusUpdates for closed actions
|
||||||
|
repositoryProperties.setRejectActionStatusForClosedAction(true);
|
||||||
|
|
||||||
|
final Long actionId3 = createAndAssignDsAsDownloadOnly("downloadOnlyDs3", DEFAULT_CONTROLLER_ID);
|
||||||
|
assertThat(actionId).isNotNull();
|
||||||
|
finishDownloadOnlyUpdateAndSendUpdateActionStatus(actionId3, Status.FINISHED);
|
||||||
|
|
||||||
|
|
||||||
|
final Long actionId4 = createAndAssignDsAsDownloadOnly("downloadOnlyDs4", DEFAULT_CONTROLLER_ID);
|
||||||
|
assertThat(actionId).isNotNull();
|
||||||
|
finishDownloadOnlyUpdateAndSendUpdateActionStatus(actionId4, Status.ERROR);
|
||||||
|
|
||||||
|
// actionStatusRepository should have 12 ActionStatusUpdates, 3 from each action
|
||||||
|
assertThat(actionStatusRepository.count()).isEqualTo(12L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void finishDownloadOnlyUpdateAndSendUpdateActionStatus(final Long actionId, final Status status) {
|
||||||
|
// finishing action
|
||||||
|
controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(actionId)
|
||||||
|
.status(Status.DOWNLOADED));
|
||||||
|
|
||||||
|
controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(actionId)
|
||||||
|
.status(status));
|
||||||
|
assertThat(actionRepository.activeActionExistsForControllerId(DEFAULT_CONTROLLER_ID))
|
||||||
|
.isEqualTo(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that a controller can report a FINISHED event for a DOWNLOAD_ONLY action after having" +
|
||||||
|
" installed an intermediate update.")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
|
||||||
|
@Expect(type = ActionCreatedEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 5),
|
||||||
|
@Expect(type = TargetAttributesRequestedEvent.class, count = 3),
|
||||||
|
@Expect(type = ActionUpdatedEvent.class, count = 3),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 2),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6) })
|
||||||
|
public void controllerReportsFinishedForOldDownloadOnlyActionAfterSuccessfulForcedAssignment() {
|
||||||
|
|
||||||
|
testdataFactory.createTarget();
|
||||||
|
final DistributionSet downloadOnlyDs = testdataFactory.createDistributionSet("downloadOnlyDs1");
|
||||||
|
|
||||||
|
// assign DOWNLOAD_ONLY Distribution set
|
||||||
|
final Long downloadOnlyActionId = assignDs(downloadOnlyDs.getId(), DEFAULT_CONTROLLER_ID, DOWNLOAD_ONLY);
|
||||||
|
addUpdateActionStatus(downloadOnlyActionId, DEFAULT_CONTROLLER_ID, Status.DOWNLOADED);
|
||||||
|
assertAssignedDistributionSetId(DEFAULT_CONTROLLER_ID, downloadOnlyDs.getId());
|
||||||
|
assertInstalledDistributionSetId(DEFAULT_CONTROLLER_ID, null);
|
||||||
|
assertNoActiveActionsExistsForControllerId(DEFAULT_CONTROLLER_ID);
|
||||||
|
|
||||||
|
// assign distributionSet as FORCED assignment
|
||||||
|
final Long forcedDistributionSetId = testdataFactory.createDistributionSet("forcedDs1").getId();
|
||||||
|
final DistributionSetAssignmentResult assignmentResult =
|
||||||
|
assignDistributionSet(forcedDistributionSetId, DEFAULT_CONTROLLER_ID, Action.ActionType.SOFT);
|
||||||
|
addUpdateActionStatus(assignmentResult.getActions().get(0), DEFAULT_CONTROLLER_ID, Status.FINISHED);
|
||||||
|
assertAssignedDistributionSetId(DEFAULT_CONTROLLER_ID, forcedDistributionSetId);
|
||||||
|
assertInstalledDistributionSetId(DEFAULT_CONTROLLER_ID, forcedDistributionSetId);
|
||||||
|
assertNoActiveActionsExistsForControllerId(DEFAULT_CONTROLLER_ID);
|
||||||
|
|
||||||
|
// report FINISHED for the DOWNLOAD_ONLY action
|
||||||
|
addUpdateActionStatus(downloadOnlyActionId, DEFAULT_CONTROLLER_ID, Status.FINISHED);
|
||||||
|
assertAssignedDistributionSetId(DEFAULT_CONTROLLER_ID, downloadOnlyDs.getId());
|
||||||
|
assertInstalledDistributionSetId(DEFAULT_CONTROLLER_ID, downloadOnlyDs.getId());
|
||||||
|
assertNoActiveActionsExistsForControllerId(DEFAULT_CONTROLLER_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void addUpdateActionStatus(final Long actionId, final String controllerId, final Status actionStatus) {
|
||||||
|
controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(actionStatus));
|
||||||
|
assertActionStatus(actionId, controllerId, TargetUpdateStatus.IN_SYNC, actionStatus, actionStatus, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAssignedDistributionSetId(final String controllerId, final Long dsId) {
|
||||||
|
final Optional<Target> target = controllerManagement.getByControllerId(controllerId);
|
||||||
|
assertThat(target).isPresent();
|
||||||
|
final DistributionSet assignedDistributionSet = ((JpaTarget) target.get()).getAssignedDistributionSet();
|
||||||
|
assertThat(assignedDistributionSet.getId()).isEqualTo(dsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertInstalledDistributionSetId(final String controllerId, final Long dsId) {
|
||||||
|
final Optional<Target> target = controllerManagement.getByControllerId(controllerId);
|
||||||
|
assertThat(target).isPresent();
|
||||||
|
final DistributionSet installedDistributionSet = ((JpaTarget) target.get()).getInstalledDistributionSet();
|
||||||
|
if(dsId == null){
|
||||||
|
assertThat(installedDistributionSet).isNull();
|
||||||
|
} else {
|
||||||
|
assertThat(installedDistributionSet.getId()).isEqualTo(dsId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNoActiveActionsExistsForControllerId(final String controllerId) {
|
||||||
|
assertThat(actionRepository.activeActionExistsForControllerId(controllerId)).isEqualTo(false);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
|||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
|
import org.eclipse.hawkbit.repository.model.ActionProperties;
|
||||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||||
@@ -1086,11 +1087,15 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(event).isNotNull();
|
assertThat(event).isNotNull();
|
||||||
assertThat(event.getDistributionSetId()).isEqualTo(ds.getId());
|
assertThat(event.getDistributionSetId()).isEqualTo(ds.getId());
|
||||||
|
|
||||||
assertThat(event.getActions()).isEqualTo(targets.stream()
|
List<Long> eventActionIds = event.getActions().values().stream().map(ActionProperties::getId)
|
||||||
.map(target -> deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
|
.collect(Collectors.toList());
|
||||||
.getContent())
|
|
||||||
|
List<Long> targetActiveActionIds = targets.stream()
|
||||||
|
.map(t -> deploymentManagement.findActiveActionsByTarget(PAGE, t.getControllerId()).getContent())
|
||||||
.flatMap(List::stream)
|
.flatMap(List::stream)
|
||||||
.collect(Collectors.toMap(action -> action.getTarget().getControllerId(), Action::getId)));
|
.map(Action::getId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assertThat(eventActionIds).containsOnlyElementsOf(targetActiveActionIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DeploymentResult {
|
private class DeploymentResult {
|
||||||
|
|||||||
@@ -590,6 +590,69 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verify that the targets have the right status during a download_only rollout.")
|
||||||
|
public void countCorrectStatusForEachTargetDuringDownloadOnlyRollout() {
|
||||||
|
|
||||||
|
final int amountTargetsForRollout = 8;
|
||||||
|
final int amountOtherTargets = 15;
|
||||||
|
final int amountGroups = 4;
|
||||||
|
final String successCondition = "50";
|
||||||
|
final String errorCondition = "80";
|
||||||
|
final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout,
|
||||||
|
amountOtherTargets, amountGroups, successCondition, errorCondition, ActionType.DOWNLOAD_ONLY);
|
||||||
|
|
||||||
|
// targets have not started
|
||||||
|
Map<TotalTargetCountStatus.Status, Long> validationMap = createInitStatusMap();
|
||||||
|
validationMap.put(TotalTargetCountStatus.Status.NOTSTARTED, 8L);
|
||||||
|
validateRolloutActionStatus(createdRollout.getId(), validationMap);
|
||||||
|
|
||||||
|
rolloutManagement.start(createdRollout.getId());
|
||||||
|
|
||||||
|
// Run here, because scheduler is disabled during tests
|
||||||
|
rolloutManagement.handleRollouts();
|
||||||
|
|
||||||
|
// 6 targets are ready and 2 are running
|
||||||
|
validationMap = createInitStatusMap();
|
||||||
|
validationMap.put(TotalTargetCountStatus.Status.SCHEDULED, 6L);
|
||||||
|
validationMap.put(TotalTargetCountStatus.Status.RUNNING, 2L);
|
||||||
|
validateRolloutActionStatus(createdRollout.getId(), validationMap);
|
||||||
|
|
||||||
|
changeStatusForAllRunningActions(createdRollout, Status.DOWNLOADED);
|
||||||
|
rolloutManagement.handleRollouts();
|
||||||
|
// 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);
|
||||||
|
validationMap.put(TotalTargetCountStatus.Status.RUNNING, 2L);
|
||||||
|
validateRolloutActionStatus(createdRollout.getId(), validationMap);
|
||||||
|
|
||||||
|
changeStatusForAllRunningActions(createdRollout, Status.DOWNLOADED);
|
||||||
|
rolloutManagement.handleRollouts();
|
||||||
|
// 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);
|
||||||
|
validationMap.put(TotalTargetCountStatus.Status.RUNNING, 2L);
|
||||||
|
validateRolloutActionStatus(createdRollout.getId(), validationMap);
|
||||||
|
|
||||||
|
changeStatusForAllRunningActions(createdRollout, Status.DOWNLOADED);
|
||||||
|
rolloutManagement.handleRollouts();
|
||||||
|
// 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);
|
||||||
|
validateRolloutActionStatus(createdRollout.getId(), validationMap);
|
||||||
|
|
||||||
|
changeStatusForAllRunningActions(createdRollout, Status.FINISHED);
|
||||||
|
rolloutManagement.handleRollouts();
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verify that the targets have the right status during the rollout when an error emerges.")
|
@Description("Verify that the targets have the right status during the rollout when an error emerges.")
|
||||||
public void countCorrectStatusForEachTargetDuringRolloutWithError() {
|
public void countCorrectStatusForEachTargetDuringRolloutWithError() {
|
||||||
@@ -1727,12 +1790,19 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
private Rollout createSimpleTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout,
|
private Rollout createSimpleTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout,
|
||||||
final int amountOtherTargets, final int groupSize, final String successCondition,
|
final int amountOtherTargets, final int groupSize, final String successCondition,
|
||||||
final String errorCondition) {
|
final String errorCondition) {
|
||||||
|
return createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, amountOtherTargets,
|
||||||
|
groupSize, successCondition, errorCondition, ActionType.FORCED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Rollout createSimpleTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout,
|
||||||
|
final int amountOtherTargets, final int groupSize, final String successCondition,
|
||||||
|
final String errorCondition, final ActionType actionType) {
|
||||||
final DistributionSet rolloutDS = testdataFactory.createDistributionSet("rolloutDS");
|
final DistributionSet rolloutDS = testdataFactory.createDistributionSet("rolloutDS");
|
||||||
testdataFactory.createTargets(amountTargetsForRollout, "rollout-", "rollout");
|
testdataFactory.createTargets(amountTargetsForRollout, "rollout-", "rollout");
|
||||||
testdataFactory.createTargets(amountOtherTargets, "others-", "rollout");
|
testdataFactory.createTargets(amountOtherTargets, "others-", "rollout");
|
||||||
final String filterQuery = "controllerId==rollout-*";
|
final String filterQuery = "controllerId==rollout-*";
|
||||||
return testdataFactory.createRolloutByVariables("test-rollout-name-1", "test-rollout-description-1", groupSize,
|
return testdataFactory.createRolloutByVariables("test-rollout-name-1", "test-rollout-description-1", groupSize,
|
||||||
filterQuery, rolloutDS, successCondition, errorCondition);
|
filterQuery, rolloutDS, successCondition, errorCondition, actionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Rollout createTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout,
|
private Rollout createTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout,
|
||||||
|
|||||||
@@ -196,6 +196,8 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
|
|||||||
|
|
||||||
verifyAutoAssignmentWithSoftActionType(filterName, targetFilterQuery, distributionSet);
|
verifyAutoAssignmentWithSoftActionType(filterName, targetFilterQuery, distributionSet);
|
||||||
|
|
||||||
|
verifyAutoAssignmentWithDownloadOnlyActionType(filterName, targetFilterQuery, distributionSet);
|
||||||
|
|
||||||
verifyAutoAssignmentWithInvalidActionType(targetFilterQuery, distributionSet);
|
verifyAutoAssignmentWithInvalidActionType(targetFilterQuery, distributionSet);
|
||||||
|
|
||||||
verifyAutoAssignmentWithIncompleteDs(targetFilterQuery);
|
verifyAutoAssignmentWithIncompleteDs(targetFilterQuery);
|
||||||
@@ -218,6 +220,14 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
|
|||||||
verifyAutoAssignDsAndActionType(filterName, distributionSet, 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);
|
||||||
|
verifyAutoAssignDsAndActionType(filterName, distributionSet, ActionType.DOWNLOAD_ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
@Step
|
@Step
|
||||||
private void verifyAutoAssignmentWithInvalidActionType(final TargetFilterQuery targetFilterQuery,
|
private void verifyAutoAssignmentWithInvalidActionType(final TargetFilterQuery targetFilterQuery,
|
||||||
final DistributionSet distributionSet) {
|
final DistributionSet distributionSet) {
|
||||||
|
|||||||
@@ -211,36 +211,46 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Test auto assignment of a distribution set with FORCED and SOFT action types")
|
@Description("Test auto assignment of a distribution set with FORCED, SOFT and DOWNLOAD_ONLY action types")
|
||||||
public void checkAutoAssignWithDifferentActionTypes() {
|
public void checkAutoAssignWithDifferentActionTypes() {
|
||||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||||
final String targetDsAIdPref = "targA";
|
final String targetDsAIdPref = "A";
|
||||||
final String targetDsBIdPref = "targB";
|
final String targetDsBIdPref = "B";
|
||||||
|
final String targetDsCIdPref = "C";
|
||||||
|
|
||||||
final List<Target> targetsA = testdataFactory.createTargets(5, targetDsAIdPref,
|
List<Target> targetsA = createTargetsAndAutoAssignDistSet(targetDsAIdPref, 5, distributionSet,
|
||||||
targetDsAIdPref.concat(" description"));
|
ActionType.FORCED);
|
||||||
final List<Target> targetsB = testdataFactory.createTargets(10, targetDsBIdPref,
|
List<Target> targetsB = createTargetsAndAutoAssignDistSet(targetDsBIdPref, 10, distributionSet,
|
||||||
targetDsBIdPref.concat(" description"));
|
ActionType.SOFT);
|
||||||
final int targetsCount = targetsA.size() + targetsB.size();
|
List<Target> targetsC = createTargetsAndAutoAssignDistSet(targetDsCIdPref, 10, distributionSet,
|
||||||
|
ActionType.DOWNLOAD_ONLY);
|
||||||
|
|
||||||
targetFilterQueryManagement
|
final int targetsCount = targetsA.size() + targetsB.size() + targetsC.size();
|
||||||
.updateAutoAssignDSWithActionType(
|
|
||||||
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filterA")
|
|
||||||
.query("id==" + targetDsAIdPref + "*")).getId(),
|
|
||||||
distributionSet.getId(), ActionType.FORCED);
|
|
||||||
targetFilterQueryManagement
|
|
||||||
.updateAutoAssignDSWithActionType(
|
|
||||||
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filterB")
|
|
||||||
.query("id==" + targetDsBIdPref + "*")).getId(),
|
|
||||||
distributionSet.getId(), ActionType.SOFT);
|
|
||||||
|
|
||||||
autoAssignChecker.check();
|
autoAssignChecker.check();
|
||||||
|
|
||||||
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsA, targetsCount);
|
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsA, targetsCount);
|
||||||
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsB, targetsCount);
|
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsB, targetsCount);
|
||||||
|
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsC, targetsCount);
|
||||||
|
|
||||||
verifyThatTargetsHaveAssignmentActionType(ActionType.FORCED, targetsA);
|
verifyThatTargetsHaveAssignmentActionType(ActionType.FORCED, targetsA);
|
||||||
verifyThatTargetsHaveAssignmentActionType(ActionType.SOFT, targetsB);
|
verifyThatTargetsHaveAssignmentActionType(ActionType.SOFT, targetsB);
|
||||||
|
verifyThatTargetsHaveAssignmentActionType(ActionType.DOWNLOAD_ONLY, targetsC);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private List<Target> createTargetsAndAutoAssignDistSet(final String prefix, final int targetCount,
|
||||||
|
final DistributionSet distributionSet, final ActionType actionType) {
|
||||||
|
|
||||||
|
final List<Target> targets = testdataFactory.createTargets(targetCount, "target" + prefix,
|
||||||
|
prefix.concat(" description"));
|
||||||
|
|
||||||
|
targetFilterQueryManagement
|
||||||
|
.updateAutoAssignDSWithActionType(
|
||||||
|
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create()
|
||||||
|
.name("filter" + prefix).query("id==target" + prefix + "*")).getId(),
|
||||||
|
distributionSet.getId(), actionType);
|
||||||
|
return targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Step
|
@Step
|
||||||
|
|||||||
@@ -225,8 +225,14 @@ public abstract class AbstractIntegrationTest {
|
|||||||
};
|
};
|
||||||
|
|
||||||
protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final String controllerId) {
|
protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final String controllerId) {
|
||||||
return deploymentManagement.assignDistributionSet(dsID, Arrays.asList(
|
return assignDistributionSet(dsID, controllerId, ActionType.FORCED);
|
||||||
new TargetWithActionType(controllerId, ActionType.FORCED, RepositoryModelConstants.NO_FORCE_TIME)));
|
}
|
||||||
|
|
||||||
|
protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final String controllerId,
|
||||||
|
final ActionType actionType) {
|
||||||
|
return deploymentManagement.assignDistributionSet(dsID,
|
||||||
|
Collections.singletonList(new TargetWithActionType(controllerId, actionType,
|
||||||
|
RepositoryModelConstants.NO_FORCE_TIME)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -316,8 +322,8 @@ public abstract class AbstractIntegrationTest {
|
|||||||
final boolean isRequiredMigrationStep) {
|
final boolean isRequiredMigrationStep) {
|
||||||
final DistributionSet ds = testdataFactory.createDistributionSet(distributionSet, isRequiredMigrationStep);
|
final DistributionSet ds = testdataFactory.createDistributionSet(distributionSet, isRequiredMigrationStep);
|
||||||
Target savedTarget = testdataFactory.createTarget(controllerId);
|
Target savedTarget = testdataFactory.createTarget(controllerId);
|
||||||
savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getAssignedEntity().iterator()
|
savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId(), ActionType.FORCED)
|
||||||
.next();
|
.getAssignedEntity().iterator().next();
|
||||||
Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
|
Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
|
||||||
.getContent().get(0);
|
.getContent().get(0);
|
||||||
|
|
||||||
|
|||||||
@@ -1003,14 +1003,41 @@ public class TestdataFactory {
|
|||||||
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
|
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
|
||||||
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
|
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
|
||||||
final String successCondition, final String errorCondition) {
|
final String successCondition, final String errorCondition) {
|
||||||
|
return createRolloutByVariables(rolloutName, rolloutDescription, groupSize, filterQuery, distributionSet, successCondition, errorCondition, Action.ActionType.FORCED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates rollout based on given parameters.
|
||||||
|
*
|
||||||
|
* @param rolloutName
|
||||||
|
* of the {@link Rollout}
|
||||||
|
* @param rolloutDescription
|
||||||
|
* of the {@link Rollout}
|
||||||
|
* @param groupSize
|
||||||
|
* of the {@link Rollout}
|
||||||
|
* @param filterQuery
|
||||||
|
* to identify the {@link Target}s
|
||||||
|
* @param distributionSet
|
||||||
|
* to assign
|
||||||
|
* @param successCondition
|
||||||
|
* to switch to next group
|
||||||
|
* @param errorCondition
|
||||||
|
* to switch to next group
|
||||||
|
* @param actionType
|
||||||
|
* the type 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 RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults()
|
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults()
|
||||||
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, successCondition)
|
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, successCondition)
|
||||||
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition)
|
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition)
|
||||||
.errorAction(RolloutGroupErrorAction.PAUSE, null).build();
|
.errorAction(RolloutGroupErrorAction.PAUSE, null).build();
|
||||||
|
|
||||||
final Rollout rollout = rolloutManagement.create(entityFactory.rollout().create().name(rolloutName)
|
final Rollout rollout = rolloutManagement.create(entityFactory.rollout().create().name(rolloutName)
|
||||||
.description(rolloutDescription).targetFilterQuery(filterQuery).set(distributionSet), groupSize,
|
.description(rolloutDescription).targetFilterQuery(filterQuery).set(distributionSet)
|
||||||
conditions);
|
.actionType(actionType), groupSize, conditions);
|
||||||
|
|
||||||
// Run here, because Scheduler is disabled during tests
|
// Run here, because Scheduler is disabled during tests
|
||||||
rolloutManagement.handleRollouts();
|
rolloutManagement.handleRollouts();
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ public class MgmtAction extends MgmtBaseEntity {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
private Long forceTime;
|
private Long forceTime;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty(value="forceType")
|
||||||
private MgmtActionType forceType;
|
private MgmtActionType actionType;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private MgmtMaintenanceWindow maintenanceWindow;
|
private MgmtMaintenanceWindow maintenanceWindow;
|
||||||
@@ -79,12 +79,12 @@ public class MgmtAction extends MgmtBaseEntity {
|
|||||||
this.forceTime = forceTime;
|
this.forceTime = forceTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MgmtActionType getForceType() {
|
public MgmtActionType getActionType() {
|
||||||
return forceType;
|
return actionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setForceType(final MgmtActionType forceType) {
|
public void setActionType(final MgmtActionType actionType) {
|
||||||
this.forceType = forceType;
|
this.actionType = actionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStatus() {
|
public String getStatus() {
|
||||||
|
|||||||
@@ -18,15 +18,15 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||||||
*/
|
*/
|
||||||
public class MgmtActionRequestBodyPut {
|
public class MgmtActionRequestBodyPut {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty(value="forceType")
|
||||||
private MgmtActionType forceType;
|
private MgmtActionType actionType;
|
||||||
|
|
||||||
public MgmtActionType getForceType() {
|
public MgmtActionType getActionType() {
|
||||||
return forceType;
|
return actionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setForceType(final MgmtActionType forceType) {
|
public void setActionType(final MgmtActionType actionType) {
|
||||||
this.forceType = forceType;
|
this.actionType = actionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,12 @@ public enum MgmtActionType {
|
|||||||
/**
|
/**
|
||||||
* The time forced action type.
|
* The time forced action type.
|
||||||
*/
|
*/
|
||||||
TIMEFORCED("timeforced");
|
TIMEFORCED("timeforced"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Download-Only action type.
|
||||||
|
*/
|
||||||
|
DOWNLOAD_ONLY("downloadonly");
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -43,6 +44,9 @@ public class MgmtRolloutResponseBody extends MgmtNamedEntity {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
private boolean deleted;
|
private boolean deleted;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private MgmtActionType type;
|
||||||
|
|
||||||
public boolean isDeleted() {
|
public boolean isDeleted() {
|
||||||
return deleted;
|
return deleted;
|
||||||
}
|
}
|
||||||
@@ -102,4 +106,12 @@ public class MgmtRolloutResponseBody extends MgmtNamedEntity {
|
|||||||
|
|
||||||
totalTargetsPerStatus.put(status, totalTargetCountByStatus);
|
totalTargetsPerStatus.put(status, totalTargetCountByStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setType(final MgmtActionType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MgmtActionType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,8 @@ public class MgmtAssignedDistributionSetRequestBody {
|
|||||||
return distributionSetId;
|
return distributionSetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MgmtAssignedDistributionSetRequestBody setDistributionSetId(final Long distributionSetId) {
|
public void setDistributionSetId(final Long distributionSetId) {
|
||||||
this.distributionSetId = distributionSetId;
|
this.distributionSetId = distributionSetId;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
|||||||
* back.
|
* back.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
final class MgmtRestModelMapper {
|
public final class MgmtRestModelMapper {
|
||||||
|
|
||||||
// private constructor, utility class
|
// private constructor, utility class
|
||||||
private MgmtRestModelMapper() {
|
private MgmtRestModelMapper() {
|
||||||
@@ -66,6 +66,8 @@ final class MgmtRestModelMapper {
|
|||||||
return ActionType.FORCED;
|
return ActionType.FORCED;
|
||||||
case TIMEFORCED:
|
case TIMEFORCED:
|
||||||
return ActionType.TIMEFORCED;
|
return ActionType.TIMEFORCED;
|
||||||
|
case DOWNLOAD_ONLY:
|
||||||
|
return ActionType.DOWNLOAD_ONLY;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Action Type is not supported");
|
throw new IllegalStateException("Action Type is not supported");
|
||||||
}
|
}
|
||||||
@@ -92,6 +94,8 @@ final class MgmtRestModelMapper {
|
|||||||
return MgmtActionType.FORCED;
|
return MgmtActionType.FORCED;
|
||||||
case TIMEFORCED:
|
case TIMEFORCED:
|
||||||
return MgmtActionType.TIMEFORCED;
|
return MgmtActionType.TIMEFORCED;
|
||||||
|
case DOWNLOAD_ONLY:
|
||||||
|
return MgmtActionType.DOWNLOAD_ONLY;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Action Type is not supported");
|
throw new IllegalStateException("Action Type is not supported");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ final class MgmtRolloutMapper {
|
|||||||
body.setStatus(rollout.getStatus().toString().toLowerCase());
|
body.setStatus(rollout.getStatus().toString().toLowerCase());
|
||||||
body.setTotalTargets(rollout.getTotalTargets());
|
body.setTotalTargets(rollout.getTotalTargets());
|
||||||
body.setDeleted(rollout.isDeleted());
|
body.setDeleted(rollout.isDeleted());
|
||||||
|
body.setType(MgmtRestModelMapper.convertActionType(rollout.getActionType()));
|
||||||
|
|
||||||
if (withDetails) {
|
if (withDetails) {
|
||||||
for (final TotalTargetCountStatus.Status status : TotalTargetCountStatus.Status.values()) {
|
for (final TotalTargetCountStatus.Status status : TotalTargetCountStatus.Status.values()) {
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ public final class MgmtTargetMapper {
|
|||||||
if (ActionType.TIMEFORCED.equals(action.getActionType())) {
|
if (ActionType.TIMEFORCED.equals(action.getActionType())) {
|
||||||
result.setForceTime(action.getForcedTime());
|
result.setForceTime(action.getForcedTime());
|
||||||
}
|
}
|
||||||
result.setForceType(MgmtRestModelMapper.convertActionType(action.getActionType()));
|
result.setActionType(MgmtRestModelMapper.convertActionType(action.getActionType()));
|
||||||
|
|
||||||
if (action.isActive()) {
|
if (action.isActive()) {
|
||||||
result.setStatus(MgmtAction.ACTION_PENDING);
|
result.setStatus(MgmtAction.ACTION_PENDING);
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MgmtActionType.FORCED.equals(actionUpdate.getForceType())) {
|
if (!MgmtActionType.FORCED.equals(actionUpdate.getActionType())) {
|
||||||
throw new ValidationException("Resource supports only switch to FORCED.");
|
throw new ValidationException("Resource supports only switch to FORCED.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import org.apache.commons.lang3.RandomStringUtils;
|
|||||||
import org.eclipse.hawkbit.exception.SpServerError;
|
import org.eclipse.hawkbit.exception.SpServerError;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||||
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||||
@@ -1245,4 +1246,30 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
|
|||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@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();
|
||||||
|
|
||||||
|
// prepare targets
|
||||||
|
final String[] knownTargetIds = new String[] { "1", "2", "3", "4", "5" };
|
||||||
|
final JSONArray list = new JSONArray();
|
||||||
|
for (final String targetId : knownTargetIds) {
|
||||||
|
testdataFactory.createTarget(targetId);
|
||||||
|
list.put(new JSONObject().put("id", Long.valueOf(targetId)));
|
||||||
|
}
|
||||||
|
// assign already one target to DS
|
||||||
|
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)))
|
||||||
|
.andExpect(jsonPath("$.alreadyAssigned", equalTo(1)))
|
||||||
|
.andExpect(jsonPath("$.total", equalTo(knownTargetIds.length)));
|
||||||
|
|
||||||
|
assertThat(targetManagement.findByAssignedDistributionSet(PAGE, createdDs.getId()).getContent())
|
||||||
|
.as("Five targets in repository have DS assigned").hasSize(5);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
|||||||
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
||||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||||
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||||
@@ -130,7 +131,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
|||||||
testdataFactory.createTargets(20, "target", "rollout");
|
testdataFactory.createTargets(20, "target", "rollout");
|
||||||
|
|
||||||
final DistributionSet dsA = testdataFactory.createDistributionSet("");
|
final DistributionSet dsA = testdataFactory.createDistributionSet("");
|
||||||
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20);
|
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20, Action.ActionType.FORCED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -367,8 +368,8 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
|||||||
testdataFactory.createTargets(20, "target", "rollout");
|
testdataFactory.createTargets(20, "target", "rollout");
|
||||||
|
|
||||||
// setup - create 2 rollouts
|
// setup - create 2 rollouts
|
||||||
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20);
|
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20, Action.ActionType.FORCED);
|
||||||
postRollout("rollout2", 5, dsA.getId(), "id==target-0001*", 10);
|
postRollout("rollout2", 5, dsA.getId(), "id==target-0001*", 10, Action.ActionType.FORCED);
|
||||||
|
|
||||||
// Run here, because Scheduler is disabled during tests
|
// Run here, because Scheduler is disabled during tests
|
||||||
rolloutManagement.handleRollouts();
|
rolloutManagement.handleRollouts();
|
||||||
@@ -408,8 +409,8 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
|||||||
testdataFactory.createTargets(20, "target", "rollout");
|
testdataFactory.createTargets(20, "target", "rollout");
|
||||||
|
|
||||||
// setup - create 2 rollouts
|
// setup - create 2 rollouts
|
||||||
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20);
|
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20, Action.ActionType.FORCED);
|
||||||
postRollout("rollout2", 5, dsA.getId(), "id==target*", 20);
|
postRollout("rollout2", 5, dsA.getId(), "id==target*", 20, Action.ActionType.FORCED);
|
||||||
|
|
||||||
// Run here, because Scheduler is disabled during tests
|
// Run here, because Scheduler is disabled during tests
|
||||||
rolloutManagement.handleRollouts();
|
rolloutManagement.handleRollouts();
|
||||||
@@ -906,6 +907,15 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that a DOWNLOAD_ONLY rollout is possible")
|
||||||
|
public void createDownloadOnlyRollout() throws Exception {
|
||||||
|
testdataFactory.createTargets(20, "target", "rollout");
|
||||||
|
|
||||||
|
final DistributionSet dsA = testdataFactory.createDistributionSet("");
|
||||||
|
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20, Action.ActionType.DOWNLOAD_ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
protected <T> T doWithTimeout(final Callable<T> callable, final SuccessCondition<T> successCondition,
|
protected <T> T doWithTimeout(final Callable<T> callable, final SuccessCondition<T> successCondition,
|
||||||
final long timeout, final long pollInterval) throws Exception // NOPMD
|
final long timeout, final long pollInterval) throws Exception // NOPMD
|
||||||
{
|
{
|
||||||
@@ -944,13 +954,17 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void postRollout(final String name, final int groupSize, final Long distributionSetId,
|
private void postRollout(final String name, final int groupSize, final Long distributionSetId,
|
||||||
final String targetFilterQuery, final int targets) throws Exception {
|
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);
|
||||||
|
|
||||||
mvc.perform(post("/rest/v1/rollouts")
|
mvc.perform(post("/rest/v1/rollouts")
|
||||||
.content(JsonBuilder.rollout(name, "desc", groupSize, distributionSetId, targetFilterQuery,
|
.content(rollout)
|
||||||
new RolloutGroupConditionBuilder().withDefaults().build()))
|
|
||||||
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
|
||||||
.andExpect(jsonPath("$.name", equalTo(name))).andExpect(jsonPath("$.status", equalTo("creating")))
|
.andExpect(jsonPath("$.name", equalTo(name))).andExpect(jsonPath("$.status", equalTo("creating")))
|
||||||
|
.andExpect(jsonPath("$.type", equalTo(actionType)))
|
||||||
.andExpect(jsonPath("$.targetFilterQuery", equalTo(targetFilterQuery)))
|
.andExpect(jsonPath("$.targetFilterQuery", equalTo(targetFilterQuery)))
|
||||||
.andExpect(jsonPath("$.description", equalTo("desc")))
|
.andExpect(jsonPath("$.description", equalTo("desc")))
|
||||||
.andExpect(jsonPath("$.distributionSetId", equalTo(distributionSetId.intValue())))
|
.andExpect(jsonPath("$.distributionSetId", equalTo(distributionSetId.intValue())))
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
package org.eclipse.hawkbit.mgmt.rest.resource;
|
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.eclipse.hawkbit.rest.util.MockMvcResultPrinter.print;
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
@@ -104,7 +105,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
public void updateTargetWhichDoesNotExistsLeadsToEntityNotFound() throws Exception {
|
public void updateTargetWhichDoesNotExistsLeadsToEntityNotFound() throws Exception {
|
||||||
final String notExistingId = "4395";
|
final String notExistingId = "4395";
|
||||||
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + notExistingId).content("{}")
|
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + notExistingId).content("{}")
|
||||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
.contentType(MediaType.APPLICATION_JSON)).andDo(print())
|
||||||
.andExpect(status().isNotFound());
|
.andExpect(status().isNotFound());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +121,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
final TargetFilterQuery tfq = createSingleTargetFilterQuery(filterName, filterQuery);
|
final TargetFilterQuery tfq = createSingleTargetFilterQuery(filterName, filterQuery);
|
||||||
|
|
||||||
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body)
|
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body)
|
||||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
.contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk())
|
||||||
.andExpect(jsonPath(JSON_PATH_ID, equalTo(tfq.getId().intValue())))
|
.andExpect(jsonPath(JSON_PATH_ID, equalTo(tfq.getId().intValue())))
|
||||||
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(filterQuery2)))
|
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(filterQuery2)))
|
||||||
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(filterName)));
|
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(filterName)));
|
||||||
@@ -143,7 +144,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
.create(entityFactory.targetFilterQuery().create().name(filterName).query(filterQuery));
|
.create(entityFactory.targetFilterQuery().create().name(filterName).query(filterQuery));
|
||||||
|
|
||||||
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body)
|
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body)
|
||||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
.contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk())
|
||||||
.andExpect(jsonPath(JSON_PATH_ID, equalTo(tfq.getId().intValue())))
|
.andExpect(jsonPath(JSON_PATH_ID, equalTo(tfq.getId().intValue())))
|
||||||
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(filterQuery)))
|
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(filterQuery)))
|
||||||
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(filterName2)));
|
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(filterName2)));
|
||||||
@@ -167,7 +168,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
createSingleTargetFilterQuery(idC, testQuery);
|
createSingleTargetFilterQuery(idC, testQuery);
|
||||||
|
|
||||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)).andExpect(status().isOk())
|
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)).andExpect(status().isOk())
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(print())
|
||||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount)))
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount)))
|
||||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(knownTargetAmount)))
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(knownTargetAmount)))
|
||||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(knownTargetAmount)))
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(knownTargetAmount)))
|
||||||
@@ -198,7 +199,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
|
|
||||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)
|
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)
|
||||||
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(limitSize)))
|
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(limitSize)))
|
||||||
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
|
.andExpect(status().isOk()).andDo(print())
|
||||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount)))
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount)))
|
||||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(limitSize)))
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(limitSize)))
|
||||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(limitSize)))
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(limitSize)))
|
||||||
@@ -227,7 +228,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)
|
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING)
|
||||||
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(offsetParam))
|
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(offsetParam))
|
||||||
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(knownTargetAmount)))
|
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, String.valueOf(knownTargetAmount)))
|
||||||
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
|
.andExpect(status().isOk()).andDo(print())
|
||||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount)))
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(knownTargetAmount)))
|
||||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(expectedSize)))
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(expectedSize)))
|
||||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(expectedSize)))
|
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(expectedSize)))
|
||||||
@@ -253,7 +254,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
+ tfq.getId();
|
+ tfq.getId();
|
||||||
// test
|
// test
|
||||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()))
|
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
.andDo(print()).andExpect(status().isOk())
|
||||||
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(knownName)))
|
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(knownName)))
|
||||||
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(knownQuery)))
|
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(knownQuery)))
|
||||||
.andExpect(jsonPath("$._links.self.href", equalTo(hrefPrefix)))
|
.andExpect(jsonPath("$._links.self.href", equalTo(hrefPrefix)))
|
||||||
@@ -284,7 +285,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
final MvcResult mvcResult = mvc
|
final MvcResult mvcResult = mvc
|
||||||
.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING).content(notJson)
|
.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING).content(notJson)
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest()).andReturn();
|
.andDo(print()).andExpect(status().isBadRequest()).andReturn();
|
||||||
|
|
||||||
assertThat(targetFilterQueryManagement.count()).isEqualTo(0);
|
assertThat(targetFilterQueryManagement.count()).isEqualTo(0);
|
||||||
|
|
||||||
@@ -311,7 +312,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
mvc.perform(
|
mvc.perform(
|
||||||
post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId() + "/autoAssignDS")
|
post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId() + "/autoAssignDS")
|
||||||
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden())
|
.andDo(print()).andExpect(status().isForbidden())
|
||||||
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(QuotaExceededException.class.getName())))
|
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(QuotaExceededException.class.getName())))
|
||||||
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||||
}
|
}
|
||||||
@@ -333,7 +334,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
mvc.perform(
|
mvc.perform(
|
||||||
post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId() + "/autoAssignDS")
|
post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId() + "/autoAssignDS")
|
||||||
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
.andDo(print()).andExpect(status().isOk());
|
||||||
|
|
||||||
final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(filterQuery.getId()).get();
|
final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(filterQuery.getId()).get();
|
||||||
|
|
||||||
@@ -343,7 +344,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
// update the query of the filter query to trigger a quota hit
|
// update the query of the filter query to trigger a quota hit
|
||||||
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId())
|
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId())
|
||||||
.content("{\"query\":\"controllerId==target*\"}").contentType(MediaType.APPLICATION_JSON))
|
.content("{\"query\":\"controllerId==target*\"}").contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden())
|
.andDo(print()).andExpect(status().isForbidden())
|
||||||
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(QuotaExceededException.class.getName())))
|
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(QuotaExceededException.class.getName())))
|
||||||
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||||
|
|
||||||
@@ -366,6 +367,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
|
|
||||||
verifyAutoAssignmentWithTimeForcedActionType(tfq, set);
|
verifyAutoAssignmentWithTimeForcedActionType(tfq, set);
|
||||||
|
|
||||||
|
verifyAutoAssignmentWithDownloadOnlyActionType(tfq, set);
|
||||||
|
|
||||||
verifyAutoAssignmentWithUnknownActionType(tfq, set);
|
verifyAutoAssignmentWithUnknownActionType(tfq, set);
|
||||||
|
|
||||||
verifyAutoAssignmentWithIncompleteDs(tfq);
|
verifyAutoAssignmentWithIncompleteDs(tfq);
|
||||||
@@ -400,7 +403,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
? "{\"id\":" + set.getId() + ", \"type\":\"" + actionType.getName() + "\"}"
|
? "{\"id\":" + set.getId() + ", \"type\":\"" + actionType.getName() + "\"}"
|
||||||
: "{\"id\":" + set.getId() + "}";
|
: "{\"id\":" + set.getId() + "}";
|
||||||
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
||||||
.content(payload).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
.content(payload).contentType(MediaType.APPLICATION_JSON)).andDo(print())
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(tfq.getId()).get();
|
final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(tfq.getId()).get();
|
||||||
@@ -411,7 +414,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
.isEqualTo(MgmtRestModelMapper.convertActionType(expectedActionType));
|
.isEqualTo(MgmtRestModelMapper.convertActionType(expectedActionType));
|
||||||
|
|
||||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()))
|
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
.andDo(print()).andExpect(status().isOk())
|
||||||
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(tfq.getName())))
|
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(tfq.getName())))
|
||||||
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(tfq.getQuery())))
|
.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_DS, equalTo(set.getId().intValue())))
|
||||||
@@ -425,7 +428,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
||||||
.content("{\"id\":" + set.getId() + ", \"type\":\"" + MgmtActionType.TIMEFORCED.getName() + "\"}")
|
.content("{\"id\":" + set.getId() + ", \"type\":\"" + MgmtActionType.TIMEFORCED.getName() + "\"}")
|
||||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
.contentType(MediaType.APPLICATION_JSON)).andDo(print())
|
||||||
.andExpect(status().isBadRequest())
|
.andExpect(status().isBadRequest())
|
||||||
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
|
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
|
||||||
equalTo(InvalidAutoAssignActionTypeException.class.getName())))
|
equalTo(InvalidAutoAssignActionTypeException.class.getName())))
|
||||||
@@ -433,12 +436,18 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
equalTo(SpServerError.SP_AUTO_ASSIGN_ACTION_TYPE_INVALID.getKey())));
|
equalTo(SpServerError.SP_AUTO_ASSIGN_ACTION_TYPE_INVALID.getKey())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Step
|
||||||
|
private void verifyAutoAssignmentWithDownloadOnlyActionType(final TargetFilterQuery tfq, final DistributionSet set)
|
||||||
|
throws Exception {
|
||||||
|
verifyAutoAssignmentByActionType(tfq, set, MgmtActionType.DOWNLOAD_ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
@Step
|
@Step
|
||||||
private void verifyAutoAssignmentWithUnknownActionType(final TargetFilterQuery tfq, final DistributionSet set)
|
private void verifyAutoAssignmentWithUnknownActionType(final TargetFilterQuery tfq, final DistributionSet set)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
||||||
.content("{\"id\":" + set.getId() + ", \"type\":\"unknown\"}").contentType(MediaType.APPLICATION_JSON))
|
.content("{\"id\":" + set.getId() + ", \"type\":\"unknown\"}").contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest())
|
.andDo(print()).andExpect(status().isBadRequest())
|
||||||
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(MessageNotReadableException.class.getName())))
|
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(MessageNotReadableException.class.getName())))
|
||||||
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_REST_BODY_NOT_READABLE.getKey())));
|
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_REST_BODY_NOT_READABLE.getKey())));
|
||||||
}
|
}
|
||||||
@@ -451,7 +460,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
|
|
||||||
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
||||||
.content("{\"id\":" + incompleteDistributionSet.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
.content("{\"id\":" + incompleteDistributionSet.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest())
|
.andDo(print()).andExpect(status().isBadRequest())
|
||||||
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
|
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
|
||||||
equalTo(InvalidAutoAssignDistributionSetException.class.getName())))
|
equalTo(InvalidAutoAssignDistributionSetException.class.getName())))
|
||||||
.andExpect(jsonPath(JSON_PATH_ERROR_CODE,
|
.andExpect(jsonPath(JSON_PATH_ERROR_CODE,
|
||||||
@@ -466,7 +475,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
|
|
||||||
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
||||||
.content("{\"id\":" + softDeletedDs.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
.content("{\"id\":" + softDeletedDs.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest())
|
.andDo(print()).andExpect(status().isBadRequest())
|
||||||
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
|
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
|
||||||
equalTo(InvalidAutoAssignDistributionSetException.class.getName())))
|
equalTo(InvalidAutoAssignDistributionSetException.class.getName())))
|
||||||
.andExpect(jsonPath(JSON_PATH_ERROR_CODE,
|
.andExpect(jsonPath(JSON_PATH_ERROR_CODE,
|
||||||
|
|||||||
@@ -1287,6 +1287,26 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
|
|||||||
assertThat(targetManagement.getByControllerID(target.getControllerId()).get()).isEqualTo(target);
|
assertThat(targetManagement.getByControllerID(target.getControllerId()).get()).isEqualTo(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verfies that a DOWNLOAD_ONLY DS to target assignment is properly handled")
|
||||||
|
public void assignDownloadOnlyDistributionSetToTarget() throws Exception {
|
||||||
|
|
||||||
|
Target target = testdataFactory.createTarget();
|
||||||
|
final DistributionSet set = testdataFactory.createDistributionSet("one");
|
||||||
|
|
||||||
|
mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + target.getControllerId() + "/assignedDS")
|
||||||
|
.content("{\"id\":" + set.getId() + ",\"type\": \"downloadonly\"}")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("assigned", equalTo(1))).andExpect(jsonPath("alreadyAssigned", equalTo(0)))
|
||||||
|
.andExpect(jsonPath("total", equalTo(1)));
|
||||||
|
|
||||||
|
assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get()).isEqualTo(set);
|
||||||
|
Slice<Action> actions = deploymentManagement.findActionsByTarget("targetExist", PageRequest.of(0, 100));
|
||||||
|
assertThat(actions.getSize()).isGreaterThan(0);
|
||||||
|
actions.stream().filter(a -> a.getDistributionSet().equals(set))
|
||||||
|
.forEach(a -> ActionType.DOWNLOAD_ONLY.equals(a.getActionType()));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verfies that an offline DS to target assignment is reflected by the repository and that repeating "
|
@Description("Verfies that an offline DS to target assignment is reflected by the repository and that repeating "
|
||||||
+ "the assignment does not change the target.")
|
+ "the assignment does not change the target.")
|
||||||
|
|||||||
@@ -449,12 +449,18 @@ public abstract class JsonBuilder {
|
|||||||
|
|
||||||
public static String rollout(final String name, final String description, final int groupSize,
|
public static String rollout(final String name, final String description, final int groupSize,
|
||||||
final long distributionSetId, final String targetFilterQuery, final RolloutGroupConditions conditions) {
|
final long distributionSetId, final String targetFilterQuery, final RolloutGroupConditions conditions) {
|
||||||
return rollout(name, description, groupSize, distributionSetId, targetFilterQuery, conditions, null);
|
return rollout(name, description, groupSize, distributionSetId, targetFilterQuery, conditions, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String rollout(final String name, final String description, final Integer groupSize,
|
public static String rollout(final String name, final String description, final Integer groupSize,
|
||||||
final long distributionSetId, final String targetFilterQuery, final RolloutGroupConditions conditions,
|
final long distributionSetId, final String targetFilterQuery, final RolloutGroupConditions conditions,
|
||||||
final List<RolloutGroup> groups) {
|
final List<RolloutGroup> groups) {
|
||||||
|
return rollout(name, description, groupSize, distributionSetId, targetFilterQuery, conditions, groups, 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 JSONObject json = new JSONObject();
|
final JSONObject json = new JSONObject();
|
||||||
try {
|
try {
|
||||||
json.put("name", name);
|
json.put("name", name);
|
||||||
@@ -463,6 +469,10 @@ public abstract class JsonBuilder {
|
|||||||
json.put("distributionSetId", distributionSetId);
|
json.put("distributionSetId", distributionSetId);
|
||||||
json.put("targetFilterQuery", targetFilterQuery);
|
json.put("targetFilterQuery", targetFilterQuery);
|
||||||
|
|
||||||
|
if(type != null){
|
||||||
|
json.put("type", type);
|
||||||
|
}
|
||||||
|
|
||||||
if (conditions != null) {
|
if (conditions != null) {
|
||||||
final JSONObject successCondition = new JSONObject();
|
final JSONObject successCondition = new JSONObject();
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ public final class MgmtApiModelProperties {
|
|||||||
public static final String ROLLOUT_TOTAL_TARGETS = "the total targets of a rollout";
|
public static final String ROLLOUT_TOTAL_TARGETS = "the total targets of a rollout";
|
||||||
public static final String ROLLOUT_TOTAL_TARGETS_PER_STATUS = "the total targets per status";
|
public static final String ROLLOUT_TOTAL_TARGETS_PER_STATUS = "the total targets per status";
|
||||||
public static final String ROLLOUT_STATUS = "the status of this rollout";
|
public static final String ROLLOUT_STATUS = "the status of this rollout";
|
||||||
|
public static final String ROLLOUT_TYPE = "the type of this rollout";
|
||||||
public static final String ROLLOUT_GROUP_STATUS = "the status of this rollout group";
|
public static final String ROLLOUT_GROUP_STATUS = "the status of this rollout group";
|
||||||
public static final String ROLLOUT_AMOUNT_GROUPS = "the amount of groups the rollout should split targets into";
|
public static final String ROLLOUT_AMOUNT_GROUPS = "the amount of groups the rollout should split targets into";
|
||||||
public static final String ROLLOUT_GROUPS = "the list of group definitions";
|
public static final String ROLLOUT_GROUPS = "the list of group definitions";
|
||||||
|
|||||||
@@ -391,7 +391,7 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
|
|||||||
requestFieldWithPath("[]maintenanceWindow.timezone")
|
requestFieldWithPath("[]maintenanceWindow.timezone")
|
||||||
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_TIMEZONE).optional(),
|
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_TIMEZONE).optional(),
|
||||||
requestFieldWithPath("[]type").description(MgmtApiModelProperties.FORCETIME_TYPE)
|
requestFieldWithPath("[]type").description(MgmtApiModelProperties.FORCETIME_TYPE)
|
||||||
.attributes(key("value").value("['soft', 'forced','timeforced']"))),
|
.attributes(key("value").value("['soft', 'forced','timeforced', 'downloadonly']"))),
|
||||||
responseFields(
|
responseFields(
|
||||||
fieldWithPath("assigned").description(MgmtApiModelProperties.DS_NEW_ASSIGNED_TARGETS),
|
fieldWithPath("assigned").description(MgmtApiModelProperties.DS_NEW_ASSIGNED_TARGETS),
|
||||||
fieldWithPath("alreadyAssigned").type(JsonFieldType.NUMBER)
|
fieldWithPath("alreadyAssigned").type(JsonFieldType.NUMBER)
|
||||||
|
|||||||
@@ -130,6 +130,8 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
|
|||||||
fieldWithPath(arrayPrefix + "distributionSetId").description(MgmtApiModelProperties.ROLLOUT_DS_ID));
|
fieldWithPath(arrayPrefix + "distributionSetId").description(MgmtApiModelProperties.ROLLOUT_DS_ID));
|
||||||
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "status").description(MgmtApiModelProperties.ROLLOUT_STATUS)
|
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "status").description(MgmtApiModelProperties.ROLLOUT_STATUS)
|
||||||
.attributes(key("value").value("['creating','ready','paused','running','finished']")));
|
.attributes(key("value").value("['creating','ready','paused','running','finished']")));
|
||||||
|
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "type").description(MgmtApiModelProperties.ROLLOUT_TYPE)
|
||||||
|
.attributes(key("value").value("['forced','soft','timeforced','downloadonly']")));
|
||||||
allFieldDescriptor.add(
|
allFieldDescriptor.add(
|
||||||
fieldWithPath(arrayPrefix + "totalTargets").description(MgmtApiModelProperties.ROLLOUT_TOTAL_TARGETS));
|
fieldWithPath(arrayPrefix + "totalTargets").description(MgmtApiModelProperties.ROLLOUT_TOTAL_TARGETS));
|
||||||
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.self").ignored());
|
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.self").ignored());
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
|
|||||||
fieldWithPath("content[].autoAssignActionType")
|
fieldWithPath("content[].autoAssignActionType")
|
||||||
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE)
|
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE)
|
||||||
.type(JsonFieldType.STRING.toString())
|
.type(JsonFieldType.STRING.toString())
|
||||||
.attributes(key("value").value("['forced', 'soft']")),
|
.attributes(key("value").value("['forced', 'soft', 'downloadonly']")),
|
||||||
fieldWithPath("content[].createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
fieldWithPath("content[].createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
||||||
fieldWithPath("content[].createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
fieldWithPath("content[].createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||||
fieldWithPath("content[].lastModifiedAt")
|
fieldWithPath("content[].lastModifiedAt")
|
||||||
@@ -199,7 +199,7 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
|
|||||||
requestFields(requestFieldWithPath("id").description(MgmtApiModelProperties.DS_ID),
|
requestFields(requestFieldWithPath("id").description(MgmtApiModelProperties.DS_ID),
|
||||||
optionalRequestFieldWithPath("type")
|
optionalRequestFieldWithPath("type")
|
||||||
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE)
|
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE)
|
||||||
.attributes(key("value").value("['forced', 'soft']"))),
|
.attributes(key("value").value("['forced', 'soft', 'downloadonly']"))),
|
||||||
getResponseFieldTargetFilterQuery(false)));
|
getResponseFieldTargetFilterQuery(false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +226,7 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
|
|||||||
.type(JsonFieldType.NUMBER.toString()),
|
.type(JsonFieldType.NUMBER.toString()),
|
||||||
fieldWithPath(arrayPrefix + "autoAssignActionType")
|
fieldWithPath(arrayPrefix + "autoAssignActionType")
|
||||||
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE).type(JsonFieldType.STRING.toString())
|
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE).type(JsonFieldType.STRING.toString())
|
||||||
.attributes(key("value").value("['forced', 'soft']")),
|
.attributes(key("value").value("['forced', 'soft', 'downloadonly']")),
|
||||||
fieldWithPath(arrayPrefix + "createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
fieldWithPath(arrayPrefix + "createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
|
||||||
fieldWithPath(arrayPrefix + "createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
fieldWithPath(arrayPrefix + "createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||||
fieldWithPath(arrayPrefix + "lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT),
|
fieldWithPath(arrayPrefix + "lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT),
|
||||||
|
|||||||
@@ -519,7 +519,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
|
|||||||
requestFieldWithPath("maintenanceWindow.timezone")
|
requestFieldWithPath("maintenanceWindow.timezone")
|
||||||
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_TIMEZONE).optional(),
|
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_TIMEZONE).optional(),
|
||||||
requestFieldWithPath("type").description(MgmtApiModelProperties.FORCETIME_TYPE)
|
requestFieldWithPath("type").description(MgmtApiModelProperties.FORCETIME_TYPE)
|
||||||
.attributes(key("value").value("['soft', 'forced','timeforced']"))),
|
.attributes(key("value").value("['soft', 'forced','timeforced', 'downloadonly']"))),
|
||||||
responseFields(
|
responseFields(
|
||||||
fieldWithPath("assigned").description(MgmtApiModelProperties.DS_NEW_ASSIGNED_TARGETS),
|
fieldWithPath("assigned").description(MgmtApiModelProperties.DS_NEW_ASSIGNED_TARGETS),
|
||||||
fieldWithPath("alreadyAssigned").type(JsonFieldType.NUMBER)
|
fieldWithPath("alreadyAssigned").type(JsonFieldType.NUMBER)
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ public abstract class AbstractGrid<T extends Indexed> extends Grid implements Re
|
|||||||
setColumnProperties();
|
setColumnProperties();
|
||||||
setColumnHeaderNames();
|
setColumnHeaderNames();
|
||||||
setColumnsHidable();
|
setColumnsHidable();
|
||||||
addColumnRenderes();
|
addColumnRenderers();
|
||||||
setColumnExpandRatio();
|
setColumnExpandRatio();
|
||||||
|
|
||||||
setHiddenColumns();
|
setHiddenColumns();
|
||||||
@@ -327,7 +327,7 @@ public abstract class AbstractGrid<T extends Indexed> extends Grid implements Re
|
|||||||
* Template method invoked by {@link #addNewContainerDS()} for adding
|
* Template method invoked by {@link #addNewContainerDS()} for adding
|
||||||
* special column renderers if needed.
|
* special column renderers if needed.
|
||||||
*/
|
*/
|
||||||
protected abstract void addColumnRenderes();
|
protected abstract void addColumnRenderers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Template method invoked by {@link #addNewContainerDS()} that hides
|
* Template method invoked by {@link #addNewContainerDS()} that hides
|
||||||
|
|||||||
@@ -0,0 +1,286 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.ui.management;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||||
|
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||||
|
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
|
||||||
|
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||||
|
import org.eclipse.hawkbit.ui.UiProperties;
|
||||||
|
import org.eclipse.hawkbit.ui.common.confirmwindow.layout.ConfirmationTab;
|
||||||
|
import org.eclipse.hawkbit.ui.common.entity.DistributionSetIdName;
|
||||||
|
import org.eclipse.hawkbit.ui.common.entity.TargetIdName;
|
||||||
|
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
|
||||||
|
import org.eclipse.hawkbit.ui.management.event.PinUnpinEvent;
|
||||||
|
import org.eclipse.hawkbit.ui.management.event.SaveActionWindowEvent;
|
||||||
|
import org.eclipse.hawkbit.ui.management.miscs.AbstractActionTypeOptionGroupLayout;
|
||||||
|
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupAssignmentLayout;
|
||||||
|
import org.eclipse.hawkbit.ui.management.miscs.MaintenanceWindowLayout;
|
||||||
|
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
|
||||||
|
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
|
||||||
|
import org.eclipse.hawkbit.ui.utils.UINotification;
|
||||||
|
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.vaadin.spring.events.EventBus.UIEventBus;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.vaadin.data.Property;
|
||||||
|
import com.vaadin.ui.CheckBox;
|
||||||
|
import com.vaadin.ui.HorizontalLayout;
|
||||||
|
import com.vaadin.ui.Link;
|
||||||
|
import com.vaadin.ui.themes.ValoTheme;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper Class for Target Assignment Operations from the Deployment View
|
||||||
|
*/
|
||||||
|
public final class TargetAssignmentOperations {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(TargetAssignmentOperations.class);
|
||||||
|
|
||||||
|
private TargetAssignmentOperations() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save all target(s)-distributionSet assignments
|
||||||
|
*
|
||||||
|
* @param managementUIState
|
||||||
|
* the management UI state
|
||||||
|
* @param actionTypeOptionGroupLayout
|
||||||
|
* the action Type Option Group Layout
|
||||||
|
* @param maintenanceWindowLayout
|
||||||
|
* the Maintenance Window Layout
|
||||||
|
* @param deploymentManagement
|
||||||
|
* the Deployment Management
|
||||||
|
* @param notification
|
||||||
|
* the UI Notification
|
||||||
|
* @param eventBus
|
||||||
|
* the UI Event Bus
|
||||||
|
* @param i18n
|
||||||
|
* the Vaadin Message Source for multi language
|
||||||
|
* @param eventSource
|
||||||
|
* the source object for sending potential events
|
||||||
|
*/
|
||||||
|
public static void saveAllAssignments(final ManagementUIState managementUIState,
|
||||||
|
final ActionTypeOptionGroupAssignmentLayout actionTypeOptionGroupLayout,
|
||||||
|
final MaintenanceWindowLayout maintenanceWindowLayout, final DeploymentManagement deploymentManagement,
|
||||||
|
final UINotification notification, final UIEventBus eventBus, final VaadinMessageSource i18n,
|
||||||
|
final Object eventSource) {
|
||||||
|
final Set<TargetIdName> itemIds = managementUIState.getAssignedList().keySet();
|
||||||
|
Long distId;
|
||||||
|
List<TargetIdName> targetIdSetList;
|
||||||
|
List<TargetIdName> tempIdList;
|
||||||
|
final ActionType actionType = ((AbstractActionTypeOptionGroupLayout.ActionTypeOption) actionTypeOptionGroupLayout
|
||||||
|
.getActionTypeOptionGroup().getValue()).getActionType();
|
||||||
|
final long forcedTimeStamp = actionTypeOptionGroupLayout.getActionTypeOptionGroup()
|
||||||
|
.getValue() == AbstractActionTypeOptionGroupLayout.ActionTypeOption.AUTO_FORCED
|
||||||
|
? actionTypeOptionGroupLayout.getForcedTimeDateField().getValue().getTime()
|
||||||
|
: RepositoryModelConstants.NO_FORCE_TIME;
|
||||||
|
|
||||||
|
final Map<Long, List<TargetIdName>> saveAssignedList = Maps.newHashMapWithExpectedSize(itemIds.size());
|
||||||
|
|
||||||
|
for (final TargetIdName itemId : itemIds) {
|
||||||
|
final DistributionSetIdName distitem = managementUIState.getAssignedList().get(itemId);
|
||||||
|
distId = distitem.getId();
|
||||||
|
|
||||||
|
if (saveAssignedList.containsKey(distId)) {
|
||||||
|
targetIdSetList = saveAssignedList.get(distId);
|
||||||
|
} else {
|
||||||
|
targetIdSetList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
targetIdSetList.add(itemId);
|
||||||
|
saveAssignedList.put(distId, targetIdSetList);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String maintenanceSchedule = maintenanceWindowLayout.getMaintenanceSchedule();
|
||||||
|
final String maintenanceDuration = maintenanceWindowLayout.getMaintenanceDuration();
|
||||||
|
final String maintenanceTimeZone = maintenanceWindowLayout.getMaintenanceTimeZone();
|
||||||
|
|
||||||
|
for (final Map.Entry<Long, List<TargetIdName>> mapEntry : saveAssignedList.entrySet()) {
|
||||||
|
tempIdList = saveAssignedList.get(mapEntry.getKey());
|
||||||
|
final DistributionSetAssignmentResult distributionSetAssignmentResult = deploymentManagement
|
||||||
|
.assignDistributionSet(mapEntry.getKey(),
|
||||||
|
tempIdList.stream().map(t -> maintenanceWindowLayout.isEnabled()
|
||||||
|
? new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp,
|
||||||
|
maintenanceSchedule, maintenanceDuration, maintenanceTimeZone)
|
||||||
|
: new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
if (distributionSetAssignmentResult.getAssigned() > 0) {
|
||||||
|
notification.displaySuccess(
|
||||||
|
i18n.getMessage("message.target.assignment", distributionSetAssignmentResult.getAssigned()));
|
||||||
|
}
|
||||||
|
if (distributionSetAssignmentResult.getAlreadyAssigned() > 0) {
|
||||||
|
notification.displaySuccess(i18n.getMessage("message.target.alreadyAssigned",
|
||||||
|
distributionSetAssignmentResult.getAlreadyAssigned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
refreshPinnedDetails(saveAssignedList, managementUIState, eventBus, eventSource);
|
||||||
|
managementUIState.getAssignedList().clear();
|
||||||
|
notification.displaySuccess(i18n.getMessage("message.target.ds.assign.success"));
|
||||||
|
eventBus.publish(eventSource, SaveActionWindowEvent.SAVED_ASSIGNMENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void refreshPinnedDetails(final Map<Long, List<TargetIdName>> saveAssignedList,
|
||||||
|
final ManagementUIState managementUIState, final UIEventBus eventBus, final Object eventSource) {
|
||||||
|
final Optional<Long> pinnedDist = managementUIState.getTargetTableFilters().getPinnedDistId();
|
||||||
|
final Optional<TargetIdName> pinnedTarget = managementUIState.getDistributionTableFilters().getPinnedTarget();
|
||||||
|
|
||||||
|
if (pinnedDist.isPresent()) {
|
||||||
|
if (saveAssignedList.keySet().contains(pinnedDist.get())) {
|
||||||
|
eventBus.publish(eventSource, PinUnpinEvent.PIN_DISTRIBUTION);
|
||||||
|
}
|
||||||
|
} else if (pinnedTarget.isPresent()) {
|
||||||
|
final Set<TargetIdName> assignedTargetIds = managementUIState.getAssignedList().keySet();
|
||||||
|
if (assignedTargetIds.contains(pinnedTarget.get())) {
|
||||||
|
eventBus.publish(eventSource, PinUnpinEvent.PIN_TARGET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check wether the maintenance window is valid or not
|
||||||
|
*
|
||||||
|
* @param maintenanceWindowLayout
|
||||||
|
* the maintenance window layout
|
||||||
|
* @param notification
|
||||||
|
* the UI Notification
|
||||||
|
* @return boolean if maintenance window is valid or not
|
||||||
|
*/
|
||||||
|
public static boolean isMaintenanceWindowValid(final MaintenanceWindowLayout maintenanceWindowLayout,
|
||||||
|
final UINotification notification) {
|
||||||
|
if (maintenanceWindowLayout.isEnabled()) {
|
||||||
|
try {
|
||||||
|
MaintenanceScheduleHelper.validateMaintenanceSchedule(maintenanceWindowLayout.getMaintenanceSchedule(),
|
||||||
|
maintenanceWindowLayout.getMaintenanceDuration(),
|
||||||
|
maintenanceWindowLayout.getMaintenanceTimeZone());
|
||||||
|
} catch (final InvalidMaintenanceScheduleException e) {
|
||||||
|
LOG.error("Maintenance window is not valid", e);
|
||||||
|
notification.displayValidationError(e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the Assignment Confirmation Tab
|
||||||
|
*
|
||||||
|
* @param actionTypeOptionGroupLayout
|
||||||
|
* the action Type Option Group Layout
|
||||||
|
* @param maintenanceWindowLayout
|
||||||
|
* the Maintenance Window Layout
|
||||||
|
* @param saveButtonToggle
|
||||||
|
* The event listener to derimne if save button should be enabled or not
|
||||||
|
* @param i18n
|
||||||
|
* the Vaadin Message Source for multi language
|
||||||
|
* @param uiProperties
|
||||||
|
* the UI Properties
|
||||||
|
* @return the Assignment Confirmation tab
|
||||||
|
*/
|
||||||
|
public static ConfirmationTab createAssignmentTab(
|
||||||
|
final ActionTypeOptionGroupAssignmentLayout actionTypeOptionGroupLayout,
|
||||||
|
final MaintenanceWindowLayout maintenanceWindowLayout, final Consumer<Boolean> saveButtonToggle,
|
||||||
|
final VaadinMessageSource i18n, final UiProperties uiProperties) {
|
||||||
|
|
||||||
|
final CheckBox maintenanceWindowControl = maintenanceWindowControl(i18n, maintenanceWindowLayout,
|
||||||
|
saveButtonToggle);
|
||||||
|
final Link maintenanceWindowHelpLink = maintenanceWindowHelpLinkControl(uiProperties, i18n);
|
||||||
|
final HorizontalLayout layout = createHorizontalLayout(maintenanceWindowControl, maintenanceWindowHelpLink);
|
||||||
|
actionTypeOptionGroupLayout.selectDefaultOption();
|
||||||
|
|
||||||
|
initMaintenanceWindow(maintenanceWindowLayout, saveButtonToggle);
|
||||||
|
addValueChangeListener(actionTypeOptionGroupLayout, maintenanceWindowControl, maintenanceWindowHelpLink);
|
||||||
|
return createAssignmentTab(actionTypeOptionGroupLayout, layout, maintenanceWindowLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HorizontalLayout createHorizontalLayout(final CheckBox maintenanceWindowControl,
|
||||||
|
final Link maintenanceWindowHelpLink) {
|
||||||
|
final HorizontalLayout layout = new HorizontalLayout();
|
||||||
|
layout.addComponent(maintenanceWindowControl);
|
||||||
|
layout.addComponent(maintenanceWindowHelpLink);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConfirmationTab createAssignmentTab(
|
||||||
|
final ActionTypeOptionGroupAssignmentLayout actionTypeOptionGroupLayout, final HorizontalLayout layout,
|
||||||
|
final MaintenanceWindowLayout maintenanceWindowLayout) {
|
||||||
|
final ConfirmationTab assignmentTab = new ConfirmationTab();
|
||||||
|
assignmentTab.addComponent(actionTypeOptionGroupLayout);
|
||||||
|
assignmentTab.addComponent(layout);
|
||||||
|
assignmentTab.addComponent(maintenanceWindowLayout);
|
||||||
|
return assignmentTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initMaintenanceWindow(final MaintenanceWindowLayout maintenanceWindowLayout,
|
||||||
|
final Consumer<Boolean> saveButtonToggle) {
|
||||||
|
maintenanceWindowLayout.setVisible(false);
|
||||||
|
maintenanceWindowLayout.setEnabled(false);
|
||||||
|
maintenanceWindowLayout.getScheduleControl().addTextChangeListener(
|
||||||
|
event -> saveButtonToggle.accept(maintenanceWindowLayout.onScheduleChange(event)));
|
||||||
|
maintenanceWindowLayout.getDurationControl().addTextChangeListener(
|
||||||
|
event -> saveButtonToggle.accept(maintenanceWindowLayout.onDurationChange(event)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CheckBox maintenanceWindowControl(final VaadinMessageSource i18n,
|
||||||
|
final MaintenanceWindowLayout maintenanceWindowLayout, final Consumer<Boolean> saveButtonToggle) {
|
||||||
|
final CheckBox enableMaintenanceWindow = new CheckBox(i18n.getMessage("caption.maintenancewindow.enabled"));
|
||||||
|
enableMaintenanceWindow.setId(UIComponentIdProvider.MAINTENANCE_WINDOW_ENABLED_ID);
|
||||||
|
enableMaintenanceWindow.addStyleName(ValoTheme.CHECKBOX_SMALL);
|
||||||
|
enableMaintenanceWindow.addStyleName("dist-window-maintenance-window-enable");
|
||||||
|
enableMaintenanceWindow.addValueChangeListener(event -> {
|
||||||
|
final Boolean isMaintenanceWindowEnabled = enableMaintenanceWindow.getValue();
|
||||||
|
maintenanceWindowLayout.setVisible(isMaintenanceWindowEnabled);
|
||||||
|
maintenanceWindowLayout.setEnabled(isMaintenanceWindowEnabled);
|
||||||
|
saveButtonToggle.accept(!isMaintenanceWindowEnabled);
|
||||||
|
maintenanceWindowLayout.clearAllControls();
|
||||||
|
});
|
||||||
|
return enableMaintenanceWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addValueChangeListener(final ActionTypeOptionGroupAssignmentLayout actionTypeOptionGroupLayout,
|
||||||
|
final CheckBox enableMaintenanceWindowControl, final Link maintenanceWindowHelpLinkControl) {
|
||||||
|
actionTypeOptionGroupLayout.getActionTypeOptionGroup()
|
||||||
|
.addValueChangeListener(new Property.ValueChangeListener() {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void valueChange(final Property.ValueChangeEvent event) {
|
||||||
|
if (event.getProperty().getValue()
|
||||||
|
.equals(AbstractActionTypeOptionGroupLayout.ActionTypeOption.DOWNLOAD_ONLY)) {
|
||||||
|
enableMaintenanceWindowControl.setValue(false);
|
||||||
|
enableMaintenanceWindowControl.setEnabled(false);
|
||||||
|
maintenanceWindowHelpLinkControl.setEnabled(false);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
enableMaintenanceWindowControl.setEnabled(true);
|
||||||
|
maintenanceWindowHelpLinkControl.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Link maintenanceWindowHelpLinkControl(final UiProperties uiProperties,
|
||||||
|
final VaadinMessageSource i18n) {
|
||||||
|
final String maintenanceWindowHelpUrl = uiProperties.getLinks().getDocumentation().getMaintenanceWindowView();
|
||||||
|
return SPUIComponentProvider.getHelpLink(i18n, maintenanceWindowHelpUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -69,8 +69,10 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
private static final String STATUS_ICON_NEUTRAL = "statusIconNeutral";
|
private static final String STATUS_ICON_NEUTRAL = "statusIconNeutral";
|
||||||
private static final String STATUS_ICON_ACTIVE = "statusIconActive";
|
private static final String STATUS_ICON_ACTIVE = "statusIconActive";
|
||||||
private static final String STATUS_ICON_FORCED = "statusIconForced";
|
private static final String STATUS_ICON_FORCED = "statusIconForced";
|
||||||
|
private static final String STATUS_ICON_DOWNLOAD_ONLY = "statusIconDownloadOnly";
|
||||||
|
private static final String STATUS_ICON_SOFT = "statusIconSoft";
|
||||||
|
|
||||||
private static final String VIRT_PROP_FORCED = "forced";
|
private static final String VIRT_PROP_TYPE = "type";
|
||||||
private static final String VIRT_PROP_TIMEFORCED = "timeForced";
|
private static final String VIRT_PROP_TIMEFORCED = "timeForced";
|
||||||
private static final String VIRT_PROP_ACTION_CANCEL = "cancel-action";
|
private static final String VIRT_PROP_ACTION_CANCEL = "cancel-action";
|
||||||
private static final String VIRT_PROP_ACTION_FORCE = "force-action";
|
private static final String VIRT_PROP_ACTION_FORCE = "force-action";
|
||||||
@@ -79,20 +81,20 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
private static final Object[] maxColumnOrder = new Object[] { ProxyAction.PXY_ACTION_IS_ACTIVE_DECO,
|
private static final Object[] maxColumnOrder = new Object[] { ProxyAction.PXY_ACTION_IS_ACTIVE_DECO,
|
||||||
ProxyAction.PXY_ACTION_ID, ProxyAction.PXY_ACTION_DS_NAME_VERSION, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT,
|
ProxyAction.PXY_ACTION_ID, ProxyAction.PXY_ACTION_DS_NAME_VERSION, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT,
|
||||||
ProxyAction.PXY_ACTION_STATUS, ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW,
|
ProxyAction.PXY_ACTION_STATUS, ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW,
|
||||||
ProxyAction.PXY_ACTION_ROLLOUT_NAME, VIRT_PROP_FORCED, VIRT_PROP_TIMEFORCED, VIRT_PROP_ACTION_CANCEL,
|
ProxyAction.PXY_ACTION_ROLLOUT_NAME, VIRT_PROP_TYPE, VIRT_PROP_TIMEFORCED, VIRT_PROP_ACTION_CANCEL,
|
||||||
VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT };
|
VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT };
|
||||||
|
|
||||||
private static final Object[] minColumnOrder = new Object[] { ProxyAction.PXY_ACTION_IS_ACTIVE_DECO,
|
private static final Object[] minColumnOrder = new Object[] { ProxyAction.PXY_ACTION_IS_ACTIVE_DECO,
|
||||||
ProxyAction.PXY_ACTION_DS_NAME_VERSION, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT,
|
ProxyAction.PXY_ACTION_DS_NAME_VERSION, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT,
|
||||||
ProxyAction.PXY_ACTION_STATUS, ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW, VIRT_PROP_FORCED,
|
ProxyAction.PXY_ACTION_STATUS, ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW, VIRT_PROP_TYPE,
|
||||||
VIRT_PROP_TIMEFORCED, VIRT_PROP_ACTION_CANCEL, VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT };
|
VIRT_PROP_TIMEFORCED, VIRT_PROP_ACTION_CANCEL, VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT };
|
||||||
|
|
||||||
private static final String[] leftAlignedColumns = new String[] { VIRT_PROP_TIMEFORCED };
|
private static final String[] leftAlignedColumns = new String[] {};
|
||||||
|
|
||||||
private static final String[] centerAlignedColumns = new String[] { ProxyAction.PXY_ACTION_IS_ACTIVE_DECO,
|
private static final String[] centerAlignedColumns = new String[] { ProxyAction.PXY_ACTION_IS_ACTIVE_DECO,
|
||||||
ProxyAction.PXY_ACTION_STATUS };
|
ProxyAction.PXY_ACTION_STATUS, VIRT_PROP_TYPE, ProxyAction.PXY_ACTION_ID, VIRT_PROP_TIMEFORCED };
|
||||||
|
|
||||||
private static final String[] rightAlignedColumns = new String[] { VIRT_PROP_FORCED, ProxyAction.PXY_ACTION_ID };
|
private static final String[] rightAlignedColumns = new String[] {};
|
||||||
|
|
||||||
private final transient DeploymentManagement deploymentManagement;
|
private final transient DeploymentManagement deploymentManagement;
|
||||||
private final UINotification notification;
|
private final UINotification notification;
|
||||||
@@ -107,19 +109,7 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
|
|
||||||
private final BeanQueryFactory<ActionBeanQuery> targetQF = new BeanQueryFactory<>(ActionBeanQuery.class);
|
private final BeanQueryFactory<ActionBeanQuery> targetQF = new BeanQueryFactory<>(ActionBeanQuery.class);
|
||||||
|
|
||||||
boolean forceClientRefreshToggle = true;
|
ActionHistoryGrid(final VaadinMessageSource i18n, final DeploymentManagement deploymentManagement,
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param i18n
|
|
||||||
* @param deploymentManagement
|
|
||||||
* @param eventBus
|
|
||||||
* @param notification
|
|
||||||
* @param managementUIState
|
|
||||||
* @param permissionChecker
|
|
||||||
*/
|
|
||||||
protected ActionHistoryGrid(final VaadinMessageSource i18n, final DeploymentManagement deploymentManagement,
|
|
||||||
final UIEventBus eventBus, final UINotification notification, final ManagementUIState managementUIState,
|
final UIEventBus eventBus, final UINotification notification, final ManagementUIState managementUIState,
|
||||||
final SpPermissionChecker permissionChecker) {
|
final SpPermissionChecker permissionChecker) {
|
||||||
super(i18n, eventBus, permissionChecker);
|
super(i18n, eventBus, permissionChecker);
|
||||||
@@ -220,14 +210,14 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addColumnRenderes() {
|
protected void addColumnRenderers() {
|
||||||
getColumn(ProxyAction.PXY_ACTION_LAST_MODIFIED_AT).setConverter(new LongToFormattedDateStringConverter());
|
getColumn(ProxyAction.PXY_ACTION_LAST_MODIFIED_AT).setConverter(new LongToFormattedDateStringConverter());
|
||||||
getColumn(ProxyAction.PXY_ACTION_STATUS).setRenderer(new HtmlLabelRenderer(),
|
getColumn(ProxyAction.PXY_ACTION_STATUS).setRenderer(new HtmlLabelRenderer(),
|
||||||
new HtmlStatusLabelConverter(this::createStatusLabelMetadata));
|
new HtmlStatusLabelConverter(this::createStatusLabelMetadata));
|
||||||
getColumn(ProxyAction.PXY_ACTION_IS_ACTIVE_DECO).setRenderer(new HtmlLabelRenderer(),
|
getColumn(ProxyAction.PXY_ACTION_IS_ACTIVE_DECO).setRenderer(new HtmlLabelRenderer(),
|
||||||
new HtmlIsActiveLabelConverter(this::createIsActiveLabelMetadata));
|
new HtmlIsActiveLabelConverter(this::createIsActiveLabelMetadata));
|
||||||
getColumn(VIRT_PROP_FORCED).setRenderer(new HtmlLabelRenderer(),
|
getColumn(VIRT_PROP_TYPE).setRenderer(new HtmlLabelRenderer(),
|
||||||
new HtmlVirtPropLabelConverter(this::createForcedLabelMetadata));
|
new HtmlVirtPropLabelConverter(this::createTypeLabelMetadata));
|
||||||
getColumn(VIRT_PROP_TIMEFORCED).setRenderer(new HtmlLabelRenderer(),
|
getColumn(VIRT_PROP_TIMEFORCED).setRenderer(new HtmlLabelRenderer(),
|
||||||
new HtmlVirtPropLabelConverter(this::createTimeForcedLabelMetadata));
|
new HtmlVirtPropLabelConverter(this::createTimeForcedLabelMetadata));
|
||||||
getColumn(VIRT_PROP_ACTION_CANCEL).setRenderer(
|
getColumn(VIRT_PROP_ACTION_CANCEL).setRenderer(
|
||||||
@@ -270,14 +260,23 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
return activeStates.get(isActiveDeco);
|
return activeStates.get(isActiveDeco);
|
||||||
}
|
}
|
||||||
|
|
||||||
private StatusFontIcon createForcedLabelMetadata(final Action action) {
|
private StatusFontIcon createTypeLabelMetadata(final Action action) {
|
||||||
StatusFontIcon result = null;
|
|
||||||
if (ActionType.FORCED.equals(action.getActionType()) || ActionType.TIMEFORCED.equals(action.getActionType())) {
|
if (ActionType.FORCED.equals(action.getActionType()) || ActionType.TIMEFORCED.equals(action.getActionType())) {
|
||||||
result = new StatusFontIcon(FontAwesome.BOLT, STATUS_ICON_FORCED,
|
return new StatusFontIcon(FontAwesome.BOLT, STATUS_ICON_FORCED,
|
||||||
i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_FORCED),
|
i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_FORCED),
|
||||||
UIComponentIdProvider.ACTION_HISTORY_TABLE_FORCED_LABEL_ID);
|
UIComponentIdProvider.ACTION_HISTORY_TABLE_TYPE_LABEL_ID);
|
||||||
}
|
}
|
||||||
return result;
|
if (ActionType.SOFT.equals(action.getActionType())) {
|
||||||
|
return new StatusFontIcon(FontAwesome.STEP_FORWARD, STATUS_ICON_SOFT,
|
||||||
|
i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_SOFT),
|
||||||
|
UIComponentIdProvider.ACTION_HISTORY_TABLE_TYPE_LABEL_ID);
|
||||||
|
}
|
||||||
|
if (ActionType.DOWNLOAD_ONLY.equals(action.getActionType())) {
|
||||||
|
return new StatusFontIcon(FontAwesome.DOWNLOAD, STATUS_ICON_DOWNLOAD_ONLY,
|
||||||
|
i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_DOWNLOAD_ONLY),
|
||||||
|
UIComponentIdProvider.ACTION_HISTORY_TABLE_TYPE_LABEL_ID);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private StatusFontIcon createTimeForcedLabelMetadata(final Action action) {
|
private StatusFontIcon createTimeForcedLabelMetadata(final Action action) {
|
||||||
@@ -408,9 +407,6 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
eventBus.publish(this, PinUnpinEvent.PIN_TARGET);
|
eventBus.publish(this, PinUnpinEvent.PIN_TARGET);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!managementUIState.getDistributionTableFilters().getPinnedTarget().isPresent()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// service call to cancel the active action
|
// service call to cancel the active action
|
||||||
@@ -443,7 +439,7 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setHiddenColumns() {
|
protected void setHiddenColumns() {
|
||||||
getColumn(VIRT_PROP_FORCED).setHidable(false);
|
getColumn(VIRT_PROP_TYPE).setHidable(false);
|
||||||
getColumn(VIRT_PROP_TIMEFORCED).setHidable(false);
|
getColumn(VIRT_PROP_TIMEFORCED).setHidable(false);
|
||||||
getColumn(VIRT_PROP_ACTION_CANCEL).setHidable(false);
|
getColumn(VIRT_PROP_ACTION_CANCEL).setHidable(false);
|
||||||
getColumn(VIRT_PROP_ACTION_FORCE).setHidable(false);
|
getColumn(VIRT_PROP_ACTION_FORCE).setHidable(false);
|
||||||
@@ -470,10 +466,8 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
getColumn(ProxyAction.PXY_ACTION_STATUS).setHeaderCaption(i18n.getMessage("header.status"));
|
getColumn(ProxyAction.PXY_ACTION_STATUS).setHeaderCaption(i18n.getMessage("header.status"));
|
||||||
getColumn(ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW)
|
getColumn(ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW)
|
||||||
.setHeaderCaption(i18n.getMessage("header.maintenancewindow"));
|
.setHeaderCaption(i18n.getMessage("header.maintenancewindow"));
|
||||||
getColumn(VIRT_PROP_FORCED).setHeaderCaption(String.valueOf(forceClientRefreshToggle));
|
|
||||||
forceClientRefreshToggle = !forceClientRefreshToggle;
|
|
||||||
|
|
||||||
newHeaderRow.join(VIRT_PROP_FORCED, VIRT_PROP_TIMEFORCED).setText(i18n.getMessage("label.action.forced"));
|
newHeaderRow.join(VIRT_PROP_TYPE, VIRT_PROP_TIMEFORCED).setText(i18n.getMessage("label.action.type"));
|
||||||
newHeaderRow.join(VIRT_PROP_ACTION_CANCEL, VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT)
|
newHeaderRow.join(VIRT_PROP_ACTION_CANCEL, VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT)
|
||||||
.setText(i18n.getMessage("header.action"));
|
.setText(i18n.getMessage("header.action"));
|
||||||
}
|
}
|
||||||
@@ -485,7 +479,7 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
setColumnsSize(100.0, 130.0, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT);
|
setColumnsSize(100.0, 130.0, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT);
|
||||||
setColumnsSize(53.0, 55.0, ProxyAction.PXY_ACTION_STATUS);
|
setColumnsSize(53.0, 55.0, ProxyAction.PXY_ACTION_STATUS);
|
||||||
setColumnsSize(150.0, 200.0, ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW);
|
setColumnsSize(150.0, 200.0, ProxyAction.PXY_ACTION_MAINTENANCE_WINDOW);
|
||||||
setColumnsSize(FIXED_PIX_MIN, FIXED_PIX_MIN, VIRT_PROP_FORCED, VIRT_PROP_TIMEFORCED, VIRT_PROP_ACTION_CANCEL,
|
setColumnsSize(FIXED_PIX_MIN, FIXED_PIX_MIN, VIRT_PROP_TYPE, VIRT_PROP_TIMEFORCED, VIRT_PROP_ACTION_CANCEL,
|
||||||
VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT);
|
VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,7 +563,7 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
protected GeneratedPropertyContainer addGeneratedContainerProperties() {
|
protected GeneratedPropertyContainer addGeneratedContainerProperties() {
|
||||||
final GeneratedPropertyContainer decoratedContainer = getDecoratedContainer();
|
final GeneratedPropertyContainer decoratedContainer = getDecoratedContainer();
|
||||||
|
|
||||||
decoratedContainer.addGeneratedProperty(VIRT_PROP_FORCED, new GenericPropertyValueGenerator());
|
decoratedContainer.addGeneratedProperty(VIRT_PROP_TYPE, new GenericPropertyValueGenerator());
|
||||||
decoratedContainer.addGeneratedProperty(VIRT_PROP_TIMEFORCED, new GenericPropertyValueGenerator());
|
decoratedContainer.addGeneratedProperty(VIRT_PROP_TIMEFORCED, new GenericPropertyValueGenerator());
|
||||||
decoratedContainer.addGeneratedProperty(VIRT_PROP_ACTION_CANCEL, new GenericPropertyValueGenerator());
|
decoratedContainer.addGeneratedProperty(VIRT_PROP_ACTION_CANCEL, new GenericPropertyValueGenerator());
|
||||||
decoratedContainer.addGeneratedProperty(VIRT_PROP_ACTION_FORCE, new GenericPropertyValueGenerator());
|
decoratedContainer.addGeneratedProperty(VIRT_PROP_ACTION_FORCE, new GenericPropertyValueGenerator());
|
||||||
@@ -622,7 +616,7 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
setColumnsSize(107.0, 500.0, ProxyAction.PXY_ACTION_DS_NAME_VERSION);
|
setColumnsSize(107.0, 500.0, ProxyAction.PXY_ACTION_DS_NAME_VERSION);
|
||||||
setColumnsSize(100.0, 150.0, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT);
|
setColumnsSize(100.0, 150.0, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT);
|
||||||
setColumnsSize(53.0, 55.0, ProxyAction.PXY_ACTION_STATUS);
|
setColumnsSize(53.0, 55.0, ProxyAction.PXY_ACTION_STATUS);
|
||||||
setColumnsSize(FIXED_PIX_MIN, FIXED_PIX_MAX, VIRT_PROP_FORCED, VIRT_PROP_TIMEFORCED,
|
setColumnsSize(FIXED_PIX_MIN, FIXED_PIX_MAX, VIRT_PROP_TYPE, VIRT_PROP_TIMEFORCED,
|
||||||
VIRT_PROP_ACTION_CANCEL, VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT);
|
VIRT_PROP_ACTION_CANCEL, VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT);
|
||||||
setColumnsSize(FIXED_PIX_MIN, 500.0, ProxyAction.PXY_ACTION_ROLLOUT_NAME);
|
setColumnsSize(FIXED_PIX_MIN, 500.0, ProxyAction.PXY_ACTION_ROLLOUT_NAME);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ public class ActionStatusGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addColumnRenderes() {
|
protected void addColumnRenderers() {
|
||||||
getColumn(ProxyActionStatus.PXY_AS_STATUS).setRenderer(new HtmlLabelRenderer(),
|
getColumn(ProxyActionStatus.PXY_AS_STATUS).setRenderer(new HtmlLabelRenderer(),
|
||||||
new HtmlStatusLabelConverter(this::createStatusLabelMetadata));
|
new HtmlStatusLabelConverter(this::createStatusLabelMetadata));
|
||||||
getColumn(ProxyActionStatus.PXY_AS_CREATED_AT).setConverter(new LongToFormattedDateStringConverter());
|
getColumn(ProxyActionStatus.PXY_AS_CREATED_AT).setConverter(new LongToFormattedDateStringConverter());
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ public class ActionStatusMsgGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addColumnRenderes() {
|
protected void addColumnRenderers() {
|
||||||
// no specific column renderers
|
// no specific column renderers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.ui.management.dstable;
|
package org.eclipse.hawkbit.ui.management.dstable;
|
||||||
|
|
||||||
|
import static org.eclipse.hawkbit.ui.management.TargetAssignmentOperations.createAssignmentTab;
|
||||||
|
import static org.eclipse.hawkbit.ui.management.TargetAssignmentOperations.isMaintenanceWindowValid;
|
||||||
|
import static org.eclipse.hawkbit.ui.management.TargetAssignmentOperations.saveAllAssignments;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -18,27 +22,21 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||||
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
|
|
||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetTagManagement;
|
import org.eclipse.hawkbit.repository.TargetTagManagement;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
|
|
||||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult;
|
import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult;
|
||||||
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
|
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
|
||||||
import org.eclipse.hawkbit.ui.SpPermissionChecker;
|
import org.eclipse.hawkbit.ui.SpPermissionChecker;
|
||||||
import org.eclipse.hawkbit.ui.UiProperties;
|
import org.eclipse.hawkbit.ui.UiProperties;
|
||||||
import org.eclipse.hawkbit.ui.common.ConfirmationDialog;
|
import org.eclipse.hawkbit.ui.common.ConfirmationDialog;
|
||||||
import org.eclipse.hawkbit.ui.common.confirmwindow.layout.ConfirmationTab;
|
|
||||||
import org.eclipse.hawkbit.ui.common.entity.DistributionSetIdName;
|
import org.eclipse.hawkbit.ui.common.entity.DistributionSetIdName;
|
||||||
import org.eclipse.hawkbit.ui.common.entity.TargetIdName;
|
import org.eclipse.hawkbit.ui.common.entity.TargetIdName;
|
||||||
import org.eclipse.hawkbit.ui.common.table.AbstractNamedVersionTable;
|
import org.eclipse.hawkbit.ui.common.table.AbstractNamedVersionTable;
|
||||||
@@ -49,8 +47,6 @@ import org.eclipse.hawkbit.ui.management.event.DistributionTableEvent;
|
|||||||
import org.eclipse.hawkbit.ui.management.event.ManagementUIEvent;
|
import org.eclipse.hawkbit.ui.management.event.ManagementUIEvent;
|
||||||
import org.eclipse.hawkbit.ui.management.event.PinUnpinEvent;
|
import org.eclipse.hawkbit.ui.management.event.PinUnpinEvent;
|
||||||
import org.eclipse.hawkbit.ui.management.event.RefreshDistributionTableByFilterEvent;
|
import org.eclipse.hawkbit.ui.management.event.RefreshDistributionTableByFilterEvent;
|
||||||
import org.eclipse.hawkbit.ui.management.event.SaveActionWindowEvent;
|
|
||||||
import org.eclipse.hawkbit.ui.management.miscs.AbstractActionTypeOptionGroupLayout.ActionTypeOption;
|
|
||||||
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupAssignmentLayout;
|
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupAssignmentLayout;
|
||||||
import org.eclipse.hawkbit.ui.management.miscs.MaintenanceWindowLayout;
|
import org.eclipse.hawkbit.ui.management.miscs.MaintenanceWindowLayout;
|
||||||
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
|
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
|
||||||
@@ -66,8 +62,6 @@ import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
|
|||||||
import org.eclipse.hawkbit.ui.utils.UINotification;
|
import org.eclipse.hawkbit.ui.utils.UINotification;
|
||||||
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
||||||
import org.eclipse.hawkbit.ui.view.filter.OnlyEventsFromDeploymentViewFilter;
|
import org.eclipse.hawkbit.ui.view.filter.OnlyEventsFromDeploymentViewFilter;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
@@ -86,13 +80,9 @@ import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
|
|||||||
import com.vaadin.server.FontAwesome;
|
import com.vaadin.server.FontAwesome;
|
||||||
import com.vaadin.ui.Button;
|
import com.vaadin.ui.Button;
|
||||||
import com.vaadin.ui.Button.ClickEvent;
|
import com.vaadin.ui.Button.ClickEvent;
|
||||||
import com.vaadin.ui.CheckBox;
|
|
||||||
import com.vaadin.ui.DragAndDropWrapper;
|
import com.vaadin.ui.DragAndDropWrapper;
|
||||||
import com.vaadin.ui.HorizontalLayout;
|
|
||||||
import com.vaadin.ui.Link;
|
|
||||||
import com.vaadin.ui.Table;
|
import com.vaadin.ui.Table;
|
||||||
import com.vaadin.ui.UI;
|
import com.vaadin.ui.UI;
|
||||||
import com.vaadin.ui.themes.ValoTheme;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Distribution set table which is shown on the Deployment View.
|
* Distribution set table which is shown on the Deployment View.
|
||||||
@@ -101,8 +91,6 @@ public class DistributionTable extends AbstractNamedVersionTable<DistributionSet
|
|||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(DistributionTable.class);
|
|
||||||
|
|
||||||
private final SpPermissionChecker permissionChecker;
|
private final SpPermissionChecker permissionChecker;
|
||||||
|
|
||||||
private final ManagementUIState managementUIState;
|
private final ManagementUIState managementUIState;
|
||||||
@@ -470,12 +458,19 @@ public class DistributionTable extends AbstractNamedVersionTable<DistributionSet
|
|||||||
confirmDialog = new ConfirmationDialog(getI18n().getMessage(CAPTION_ENTITY_ASSIGN_ACTION_CONFIRMBOX),
|
confirmDialog = new ConfirmationDialog(getI18n().getMessage(CAPTION_ENTITY_ASSIGN_ACTION_CONFIRMBOX),
|
||||||
confirmQuestion, getI18n().getMessage(UIMessageIdProvider.BUTTON_OK),
|
confirmQuestion, getI18n().getMessage(UIMessageIdProvider.BUTTON_OK),
|
||||||
getI18n().getMessage(UIMessageIdProvider.BUTTON_CANCEL), ok -> {
|
getI18n().getMessage(UIMessageIdProvider.BUTTON_CANCEL), ok -> {
|
||||||
if (ok && isMaintenanceWindowValid()) {
|
if (ok && isMaintenanceWindowValid(maintenanceWindowLayout, getNotification())) {
|
||||||
saveAllAssignments();
|
saveAllAssignments(managementUIState, actionTypeOptionGroupLayout, maintenanceWindowLayout,
|
||||||
|
deploymentManagement, getNotification(), getEventBus(), getI18n(), this);
|
||||||
} else {
|
} else {
|
||||||
managementUIState.getAssignedList().clear();
|
managementUIState.getAssignedList().clear();
|
||||||
}
|
}
|
||||||
}, createAssignmentTab(), UIComponentIdProvider.DIST_SET_TO_TARGET_ASSIGNMENT_CONFIRM_ID);
|
}, createAssignmentTab(actionTypeOptionGroupLayout, maintenanceWindowLayout, saveButtonToggle(),
|
||||||
|
getI18n(), uiProperties),
|
||||||
|
UIComponentIdProvider.DIST_SET_TO_TARGET_ASSIGNMENT_CONFIRM_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Consumer<Boolean> saveButtonToggle() {
|
||||||
|
return isEnabled -> confirmDialog.getOkButton().setEnabled(isEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createConfirmationQuestionForAssignment(final String distributionNameToAssign,
|
private String createConfirmationQuestionForAssignment(final String distributionNameToAssign,
|
||||||
@@ -489,145 +484,6 @@ public class DistributionTable extends AbstractNamedVersionTable<DistributionSet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMaintenanceWindowValid() {
|
|
||||||
if (maintenanceWindowLayout.isEnabled()) {
|
|
||||||
try {
|
|
||||||
MaintenanceScheduleHelper.validateMaintenanceSchedule(maintenanceWindowLayout.getMaintenanceSchedule(),
|
|
||||||
maintenanceWindowLayout.getMaintenanceDuration(),
|
|
||||||
maintenanceWindowLayout.getMaintenanceTimeZone());
|
|
||||||
} catch (final InvalidMaintenanceScheduleException e) {
|
|
||||||
LOG.error("Maintenance window is not valid", e);
|
|
||||||
getNotification().displayValidationError(e.getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveAllAssignments() {
|
|
||||||
final Set<TargetIdName> itemIds = managementUIState.getAssignedList().keySet();
|
|
||||||
Long distId;
|
|
||||||
List<TargetIdName> targetIdSetList;
|
|
||||||
List<TargetIdName> tempIdList;
|
|
||||||
final ActionType actionType = ((ActionTypeOption) actionTypeOptionGroupLayout.getActionTypeOptionGroup()
|
|
||||||
.getValue()).getActionType();
|
|
||||||
final long forcedTimeStamp = (((ActionTypeOption) actionTypeOptionGroupLayout.getActionTypeOptionGroup()
|
|
||||||
.getValue()) == ActionTypeOption.AUTO_FORCED)
|
|
||||||
? actionTypeOptionGroupLayout.getForcedTimeDateField().getValue().getTime()
|
|
||||||
: RepositoryModelConstants.NO_FORCE_TIME;
|
|
||||||
|
|
||||||
final Map<Long, List<TargetIdName>> saveAssignedList = Maps.newHashMapWithExpectedSize(itemIds.size());
|
|
||||||
|
|
||||||
for (final TargetIdName itemId : itemIds) {
|
|
||||||
final DistributionSetIdName distitem = managementUIState.getAssignedList().get(itemId);
|
|
||||||
distId = distitem.getId();
|
|
||||||
|
|
||||||
if (saveAssignedList.containsKey(distId)) {
|
|
||||||
targetIdSetList = saveAssignedList.get(distId);
|
|
||||||
} else {
|
|
||||||
targetIdSetList = new ArrayList<>();
|
|
||||||
}
|
|
||||||
targetIdSetList.add(itemId);
|
|
||||||
saveAssignedList.put(distId, targetIdSetList);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String maintenanceSchedule = maintenanceWindowLayout.getMaintenanceSchedule();
|
|
||||||
final String maintenanceDuration = maintenanceWindowLayout.getMaintenanceDuration();
|
|
||||||
final String maintenanceTimeZone = maintenanceWindowLayout.getMaintenanceTimeZone();
|
|
||||||
|
|
||||||
for (final Map.Entry<Long, List<TargetIdName>> mapEntry : saveAssignedList.entrySet()) {
|
|
||||||
tempIdList = saveAssignedList.get(mapEntry.getKey());
|
|
||||||
final DistributionSetAssignmentResult distributionSetAssignmentResult = deploymentManagement
|
|
||||||
.assignDistributionSet(mapEntry.getKey(),
|
|
||||||
tempIdList.stream().map(t -> maintenanceWindowLayout.isEnabled()
|
|
||||||
? new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp,
|
|
||||||
maintenanceSchedule, maintenanceDuration, maintenanceTimeZone)
|
|
||||||
: new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp))
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
|
|
||||||
if (distributionSetAssignmentResult.getAssigned() > 0) {
|
|
||||||
getNotification().displaySuccess(getI18n().getMessage("message.target.assignment",
|
|
||||||
distributionSetAssignmentResult.getAssigned()));
|
|
||||||
}
|
|
||||||
if (distributionSetAssignmentResult.getAlreadyAssigned() > 0) {
|
|
||||||
getNotification().displaySuccess(getI18n().getMessage("message.target.alreadyAssigned",
|
|
||||||
distributionSetAssignmentResult.getAlreadyAssigned()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resfreshPinnedDetails(saveAssignedList);
|
|
||||||
|
|
||||||
managementUIState.getAssignedList().clear();
|
|
||||||
getNotification().displaySuccess(getI18n().getMessage("message.target.ds.assign.success"));
|
|
||||||
getEventBus().publish(this, SaveActionWindowEvent.SAVED_ASSIGNMENTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resfreshPinnedDetails(final Map<Long, List<TargetIdName>> saveAssignedList) {
|
|
||||||
final Optional<Long> pinnedDist = managementUIState.getTargetTableFilters().getPinnedDistId();
|
|
||||||
final Optional<TargetIdName> pinnedTarget = managementUIState.getDistributionTableFilters().getPinnedTarget();
|
|
||||||
|
|
||||||
if (pinnedDist.isPresent()) {
|
|
||||||
if (saveAssignedList.keySet().contains(pinnedDist.get())) {
|
|
||||||
getEventBus().publish(this, PinUnpinEvent.PIN_DISTRIBUTION);
|
|
||||||
}
|
|
||||||
} else if (pinnedTarget.isPresent()) {
|
|
||||||
final Set<TargetIdName> assignedTargetIds = managementUIState.getAssignedList().keySet();
|
|
||||||
if (assignedTargetIds.contains(pinnedTarget.get())) {
|
|
||||||
getEventBus().publish(this, PinUnpinEvent.PIN_TARGET);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConfirmationTab createAssignmentTab() {
|
|
||||||
final ConfirmationTab assignmentTab = new ConfirmationTab();
|
|
||||||
actionTypeOptionGroupLayout.selectDefaultOption();
|
|
||||||
assignmentTab.addComponent(actionTypeOptionGroupLayout);
|
|
||||||
assignmentTab.addComponent(enableMaintenanceWindowLayout());
|
|
||||||
initMaintenanceWindow();
|
|
||||||
assignmentTab.addComponent(maintenanceWindowLayout);
|
|
||||||
return assignmentTab;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HorizontalLayout enableMaintenanceWindowLayout() {
|
|
||||||
final HorizontalLayout layout = new HorizontalLayout();
|
|
||||||
layout.addComponent(enableMaintenanceWindowControl());
|
|
||||||
layout.addComponent(maintenanceWindowHelpLinkControl());
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CheckBox enableMaintenanceWindowControl() {
|
|
||||||
final CheckBox enableMaintenanceWindow = new CheckBox(
|
|
||||||
getI18n().getMessage("caption.maintenancewindow.enabled"));
|
|
||||||
enableMaintenanceWindow.setId(UIComponentIdProvider.MAINTENANCE_WINDOW_ENABLED_ID);
|
|
||||||
enableMaintenanceWindow.addStyleName(ValoTheme.CHECKBOX_SMALL);
|
|
||||||
enableMaintenanceWindow.addStyleName("dist-window-maintenance-window-enable");
|
|
||||||
enableMaintenanceWindow.addValueChangeListener(event -> {
|
|
||||||
final Boolean isMaintenanceWindowEnabled = enableMaintenanceWindow.getValue();
|
|
||||||
maintenanceWindowLayout.setVisible(isMaintenanceWindowEnabled);
|
|
||||||
maintenanceWindowLayout.setEnabled(isMaintenanceWindowEnabled);
|
|
||||||
enableSaveButton(!isMaintenanceWindowEnabled);
|
|
||||||
maintenanceWindowLayout.clearAllControls();
|
|
||||||
});
|
|
||||||
return enableMaintenanceWindow;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Link maintenanceWindowHelpLinkControl() {
|
|
||||||
final String maintenanceWindowHelpUrl = uiProperties.getLinks().getDocumentation().getMaintenanceWindowView();
|
|
||||||
return SPUIComponentProvider.getHelpLink(getI18n(), maintenanceWindowHelpUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initMaintenanceWindow() {
|
|
||||||
maintenanceWindowLayout.setVisible(false);
|
|
||||||
maintenanceWindowLayout.setEnabled(false);
|
|
||||||
maintenanceWindowLayout.getScheduleControl()
|
|
||||||
.addTextChangeListener(event -> enableSaveButton(maintenanceWindowLayout.onScheduleChange(event)));
|
|
||||||
maintenanceWindowLayout.getDurationControl()
|
|
||||||
.addTextChangeListener(event -> enableSaveButton(maintenanceWindowLayout.onDurationChange(event)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enableSaveButton(final boolean enabled) {
|
|
||||||
confirmDialog.getOkButton().setEnabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<String> hasMissingPermissionsForDrop() {
|
protected List<String> hasMissingPermissionsForDrop() {
|
||||||
return permissionChecker.hasUpdateTargetPermission() ? Collections.emptyList()
|
return permissionChecker.hasUpdateTargetPermission() ? Collections.emptyList()
|
||||||
|
|||||||
@@ -78,9 +78,25 @@ public abstract class AbstractActionTypeOptionGroupLayout extends HorizontalLayo
|
|||||||
softLabel.setCaption(i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_SOFT));
|
softLabel.setCaption(i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_SOFT));
|
||||||
softLabel.setDescription(i18n.getMessage(UIMessageIdProvider.TOOLTIP_SOFT_ITEM));
|
softLabel.setDescription(i18n.getMessage(UIMessageIdProvider.TOOLTIP_SOFT_ITEM));
|
||||||
softLabel.setStyleName("padding-right-style");
|
softLabel.setStyleName("padding-right-style");
|
||||||
|
softLabel.setIcon(FontAwesome.STEP_FORWARD);
|
||||||
addComponent(softLabel);
|
addComponent(softLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void addDownloadOnlyItemWithLabel() {
|
||||||
|
final FlexibleOptionGroupItemComponent downloadOnlyItem = actionTypeOptionGroup
|
||||||
|
.getItemComponent(ActionTypeOption.DOWNLOAD_ONLY);
|
||||||
|
downloadOnlyItem.setId(UIComponentIdProvider.ACTION_DETAILS_DOWNLOAD_ONLY_ID);
|
||||||
|
downloadOnlyItem.setStyleName(STYLE_DIST_WINDOW_ACTIONTYPE);
|
||||||
|
addComponent(downloadOnlyItem);
|
||||||
|
final Label downloadOnlyLabel = new Label();
|
||||||
|
downloadOnlyLabel.setSizeFull();
|
||||||
|
downloadOnlyLabel.setCaption(i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_DOWNLOAD_ONLY));
|
||||||
|
downloadOnlyLabel.setDescription(i18n.getMessage(UIMessageIdProvider.TOOLTIP_DOWNLOAD_ONLY_ITEM));
|
||||||
|
downloadOnlyLabel.setStyleName("padding-right-style");
|
||||||
|
downloadOnlyLabel.setIcon(FontAwesome.DOWNLOAD);
|
||||||
|
addComponent(downloadOnlyLabel);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To Set Default option for save.
|
* To Set Default option for save.
|
||||||
*/
|
*/
|
||||||
@@ -93,7 +109,8 @@ public abstract class AbstractActionTypeOptionGroupLayout extends HorizontalLayo
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public enum ActionTypeOption {
|
public enum ActionTypeOption {
|
||||||
FORCED(ActionType.FORCED), SOFT(ActionType.SOFT), AUTO_FORCED(ActionType.TIMEFORCED);
|
FORCED(ActionType.FORCED), SOFT(ActionType.SOFT), AUTO_FORCED(ActionType.TIMEFORCED),
|
||||||
|
DOWNLOAD_ONLY(ActionType.DOWNLOAD_ONLY);
|
||||||
|
|
||||||
private final ActionType actionType;
|
private final ActionType actionType;
|
||||||
|
|
||||||
|
|||||||
@@ -70,13 +70,17 @@ public class ActionTypeOptionGroupAssignmentLayout extends AbstractActionTypeOpt
|
|||||||
actionTypeOptionGroup.addItem(ActionTypeOption.SOFT);
|
actionTypeOptionGroup.addItem(ActionTypeOption.SOFT);
|
||||||
actionTypeOptionGroup.addItem(ActionTypeOption.FORCED);
|
actionTypeOptionGroup.addItem(ActionTypeOption.FORCED);
|
||||||
actionTypeOptionGroup.addItem(ActionTypeOption.AUTO_FORCED);
|
actionTypeOptionGroup.addItem(ActionTypeOption.AUTO_FORCED);
|
||||||
|
actionTypeOptionGroup.addItem(ActionTypeOption.DOWNLOAD_ONLY);
|
||||||
selectDefaultOption();
|
selectDefaultOption();
|
||||||
|
|
||||||
addForcedItemWithLabel();
|
addForcedItemWithLabel();
|
||||||
addSoftItemWithLabel();
|
addSoftItemWithLabel();
|
||||||
addAutoForceItemWithLabelAndDateField();
|
addAutoForceItemWithLabelAndDateField();
|
||||||
|
addDownloadOnlyItemWithLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void addAutoForceItemWithLabelAndDateField() {
|
private void addAutoForceItemWithLabelAndDateField() {
|
||||||
final FlexibleOptionGroupItemComponent autoForceItem = actionTypeOptionGroup
|
final FlexibleOptionGroupItemComponent autoForceItem = actionTypeOptionGroup
|
||||||
.getItemComponent(ActionTypeOption.AUTO_FORCED);
|
.getItemComponent(ActionTypeOption.AUTO_FORCED);
|
||||||
|
|||||||
@@ -32,9 +32,11 @@ public class ActionTypeOptionGroupAutoAssignmentLayout extends AbstractActionTyp
|
|||||||
actionTypeOptionGroup = new FlexibleOptionGroup();
|
actionTypeOptionGroup = new FlexibleOptionGroup();
|
||||||
actionTypeOptionGroup.addItem(ActionTypeOption.SOFT);
|
actionTypeOptionGroup.addItem(ActionTypeOption.SOFT);
|
||||||
actionTypeOptionGroup.addItem(ActionTypeOption.FORCED);
|
actionTypeOptionGroup.addItem(ActionTypeOption.FORCED);
|
||||||
|
actionTypeOptionGroup.addItem(ActionTypeOption.DOWNLOAD_ONLY);
|
||||||
selectDefaultOption();
|
selectDefaultOption();
|
||||||
|
|
||||||
addForcedItemWithLabel();
|
addForcedItemWithLabel();
|
||||||
addSoftItemWithLabel();
|
addSoftItemWithLabel();
|
||||||
|
addDownloadOnlyItemWithLabel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.ui.management.targettable;
|
package org.eclipse.hawkbit.ui.management.targettable;
|
||||||
|
|
||||||
|
import static org.eclipse.hawkbit.ui.management.TargetAssignmentOperations.createAssignmentTab;
|
||||||
|
import static org.eclipse.hawkbit.ui.management.TargetAssignmentOperations.isMaintenanceWindowValid;
|
||||||
|
import static org.eclipse.hawkbit.ui.management.TargetAssignmentOperations.saveAllAssignments;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -19,6 +23,7 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -26,32 +31,24 @@ import org.eclipse.hawkbit.im.authentication.SpPermission;
|
|||||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||||
import org.eclipse.hawkbit.repository.FilterParams;
|
import org.eclipse.hawkbit.repository.FilterParams;
|
||||||
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
|
|
||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetTagManagement;
|
import org.eclipse.hawkbit.repository.TargetTagManagement;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.RemoteEntityEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.RemoteEntityEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
|
|
||||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
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.Tag;
|
import org.eclipse.hawkbit.repository.model.Tag;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
|
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
|
||||||
import org.eclipse.hawkbit.ui.SpPermissionChecker;
|
import org.eclipse.hawkbit.ui.SpPermissionChecker;
|
||||||
import org.eclipse.hawkbit.ui.UiProperties;
|
import org.eclipse.hawkbit.ui.UiProperties;
|
||||||
import org.eclipse.hawkbit.ui.common.ConfirmationDialog;
|
import org.eclipse.hawkbit.ui.common.ConfirmationDialog;
|
||||||
import org.eclipse.hawkbit.ui.common.ManagementEntityState;
|
import org.eclipse.hawkbit.ui.common.ManagementEntityState;
|
||||||
import org.eclipse.hawkbit.ui.common.UserDetailsFormatter;
|
import org.eclipse.hawkbit.ui.common.UserDetailsFormatter;
|
||||||
import org.eclipse.hawkbit.ui.common.confirmwindow.layout.ConfirmationTab;
|
|
||||||
import org.eclipse.hawkbit.ui.common.entity.DistributionSetIdName;
|
import org.eclipse.hawkbit.ui.common.entity.DistributionSetIdName;
|
||||||
import org.eclipse.hawkbit.ui.common.entity.TargetIdName;
|
import org.eclipse.hawkbit.ui.common.entity.TargetIdName;
|
||||||
import org.eclipse.hawkbit.ui.common.table.AbstractTable;
|
import org.eclipse.hawkbit.ui.common.table.AbstractTable;
|
||||||
import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType;
|
import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType;
|
||||||
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
|
|
||||||
import org.eclipse.hawkbit.ui.dd.criteria.ManagementViewClientCriterion;
|
import org.eclipse.hawkbit.ui.dd.criteria.ManagementViewClientCriterion;
|
||||||
import org.eclipse.hawkbit.ui.management.event.ManagementUIEvent;
|
import org.eclipse.hawkbit.ui.management.event.ManagementUIEvent;
|
||||||
import org.eclipse.hawkbit.ui.management.event.PinUnpinEvent;
|
import org.eclipse.hawkbit.ui.management.event.PinUnpinEvent;
|
||||||
@@ -60,7 +57,6 @@ import org.eclipse.hawkbit.ui.management.event.TargetAddUpdateWindowEvent;
|
|||||||
import org.eclipse.hawkbit.ui.management.event.TargetFilterEvent;
|
import org.eclipse.hawkbit.ui.management.event.TargetFilterEvent;
|
||||||
import org.eclipse.hawkbit.ui.management.event.TargetTableEvent;
|
import org.eclipse.hawkbit.ui.management.event.TargetTableEvent;
|
||||||
import org.eclipse.hawkbit.ui.management.event.TargetTableEvent.TargetComponentEvent;
|
import org.eclipse.hawkbit.ui.management.event.TargetTableEvent.TargetComponentEvent;
|
||||||
import org.eclipse.hawkbit.ui.management.miscs.AbstractActionTypeOptionGroupLayout.ActionTypeOption;
|
|
||||||
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupAssignmentLayout;
|
import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupAssignmentLayout;
|
||||||
import org.eclipse.hawkbit.ui.management.miscs.MaintenanceWindowLayout;
|
import org.eclipse.hawkbit.ui.management.miscs.MaintenanceWindowLayout;
|
||||||
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
|
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
|
||||||
@@ -99,11 +95,8 @@ import com.vaadin.server.FontAwesome;
|
|||||||
import com.vaadin.shared.ui.label.ContentMode;
|
import com.vaadin.shared.ui.label.ContentMode;
|
||||||
import com.vaadin.ui.Button;
|
import com.vaadin.ui.Button;
|
||||||
import com.vaadin.ui.Button.ClickEvent;
|
import com.vaadin.ui.Button.ClickEvent;
|
||||||
import com.vaadin.ui.CheckBox;
|
|
||||||
import com.vaadin.ui.DragAndDropWrapper;
|
import com.vaadin.ui.DragAndDropWrapper;
|
||||||
import com.vaadin.ui.HorizontalLayout;
|
|
||||||
import com.vaadin.ui.Label;
|
import com.vaadin.ui.Label;
|
||||||
import com.vaadin.ui.Link;
|
|
||||||
import com.vaadin.ui.Table;
|
import com.vaadin.ui.Table;
|
||||||
import com.vaadin.ui.UI;
|
import com.vaadin.ui.UI;
|
||||||
import com.vaadin.ui.themes.ValoTheme;
|
import com.vaadin.ui.themes.ValoTheme;
|
||||||
@@ -379,8 +372,7 @@ public class TargetTable extends AbstractTable<Target> {
|
|||||||
if (
|
if (
|
||||||
|
|
||||||
isFilteredByTags()) {
|
isFilteredByTags()) {
|
||||||
final List<String> list = new ArrayList<>();
|
final List<String> list = new ArrayList<>(managementUIState.getTargetTableFilters().getClickedTargetTags());
|
||||||
list.addAll(managementUIState.getTargetTableFilters().getClickedTargetTags());
|
|
||||||
queryConfig.put(SPUIDefinitions.FILTER_BY_TAG, list.toArray(new String[list.size()]));
|
queryConfig.put(SPUIDefinitions.FILTER_BY_TAG, list.toArray(new String[list.size()]));
|
||||||
}
|
}
|
||||||
if (isFilteredByStatus()) {
|
if (isFilteredByStatus()) {
|
||||||
@@ -524,7 +516,7 @@ public class TargetTable extends AbstractTable<Target> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void tagAssignment(final DragAndDropEvent event) {
|
private void tagAssignment(final DragAndDropEvent event) {
|
||||||
final List<Long> targetList = getDraggedTargetList(event).stream().collect(Collectors.toList());
|
final List<Long> targetList = new ArrayList<>(getDraggedTargetList(event));
|
||||||
|
|
||||||
final String targTagName = HawkbitCommonUtil.removePrefix(event.getTransferable().getSourceComponent().getId(),
|
final String targTagName = HawkbitCommonUtil.removePrefix(event.getTransferable().getSourceComponent().getId(),
|
||||||
SPUIDefinitions.TARGET_TAG_ID_PREFIXS);
|
SPUIDefinitions.TARGET_TAG_ID_PREFIXS);
|
||||||
@@ -858,95 +850,6 @@ public class TargetTable extends AbstractTable<Target> {
|
|||||||
return !managementUIState.getTargetTableFilters().getClickedTargetTags().isEmpty();
|
return !managementUIState.getTargetTableFilters().getClickedTargetTags().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code for assignment start
|
|
||||||
private void saveAllAssignments() {
|
|
||||||
final Set<TargetIdName> itemIds = managementUIState.getAssignedList().keySet();
|
|
||||||
Long distId;
|
|
||||||
List<TargetIdName> targetIdSetList;
|
|
||||||
List<TargetIdName> tempIdList;
|
|
||||||
final ActionType actionType = ((ActionTypeOption) actionTypeOptionGroupLayout.getActionTypeOptionGroup()
|
|
||||||
.getValue()).getActionType();
|
|
||||||
final long forcedTimeStamp = (((ActionTypeOption) actionTypeOptionGroupLayout.getActionTypeOptionGroup()
|
|
||||||
.getValue()) == ActionTypeOption.AUTO_FORCED)
|
|
||||||
? actionTypeOptionGroupLayout.getForcedTimeDateField().getValue().getTime()
|
|
||||||
: RepositoryModelConstants.NO_FORCE_TIME;
|
|
||||||
|
|
||||||
final Map<Long, List<TargetIdName>> saveAssignedList = Maps.newHashMapWithExpectedSize(itemIds.size());
|
|
||||||
|
|
||||||
for (final TargetIdName itemId : itemIds) {
|
|
||||||
final DistributionSetIdName distitem = managementUIState.getAssignedList().get(itemId);
|
|
||||||
distId = distitem.getId();
|
|
||||||
|
|
||||||
if (saveAssignedList.containsKey(distId)) {
|
|
||||||
targetIdSetList = saveAssignedList.get(distId);
|
|
||||||
} else {
|
|
||||||
targetIdSetList = new ArrayList<>();
|
|
||||||
}
|
|
||||||
targetIdSetList.add(itemId);
|
|
||||||
saveAssignedList.put(distId, targetIdSetList);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String maintenanceSchedule = maintenanceWindowLayout.getMaintenanceSchedule();
|
|
||||||
final String maintenanceDuration = maintenanceWindowLayout.getMaintenanceDuration();
|
|
||||||
final String maintenanceTimeZone = maintenanceWindowLayout.getMaintenanceTimeZone();
|
|
||||||
|
|
||||||
for (final Map.Entry<Long, List<TargetIdName>> mapEntry : saveAssignedList.entrySet()) {
|
|
||||||
tempIdList = saveAssignedList.get(mapEntry.getKey());
|
|
||||||
final DistributionSetAssignmentResult distributionSetAssignmentResult = deploymentManagement
|
|
||||||
.assignDistributionSet(mapEntry.getKey(),
|
|
||||||
tempIdList.stream().map(t -> maintenanceWindowLayout.isEnabled()
|
|
||||||
? new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp,
|
|
||||||
maintenanceSchedule, maintenanceDuration, maintenanceTimeZone)
|
|
||||||
: new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp))
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
|
|
||||||
if (distributionSetAssignmentResult.getAssigned() > 0) {
|
|
||||||
getNotification().displaySuccess(getI18n().getMessage("message.target.assignment",
|
|
||||||
distributionSetAssignmentResult.getAssigned()));
|
|
||||||
}
|
|
||||||
if (distributionSetAssignmentResult.getAlreadyAssigned() > 0) {
|
|
||||||
getNotification().displaySuccess(getI18n().getMessage("message.target.alreadyAssigned",
|
|
||||||
distributionSetAssignmentResult.getAlreadyAssigned()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resfreshPinnedDetails(saveAssignedList);
|
|
||||||
|
|
||||||
managementUIState.getAssignedList().clear();
|
|
||||||
getNotification().displaySuccess(getI18n().getMessage("message.target.ds.assign.success"));
|
|
||||||
getEventBus().publish(this, SaveActionWindowEvent.SAVED_ASSIGNMENTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resfreshPinnedDetails(final Map<Long, List<TargetIdName>> saveAssignedList) {
|
|
||||||
final Optional<Long> pinnedDist = managementUIState.getTargetTableFilters().getPinnedDistId();
|
|
||||||
final Optional<TargetIdName> pinnedTarget = managementUIState.getDistributionTableFilters().getPinnedTarget();
|
|
||||||
|
|
||||||
if (pinnedDist.isPresent()) {
|
|
||||||
if (saveAssignedList.keySet().contains(pinnedDist.get())) {
|
|
||||||
getEventBus().publish(this, PinUnpinEvent.PIN_DISTRIBUTION);
|
|
||||||
}
|
|
||||||
} else if (pinnedTarget.isPresent()) {
|
|
||||||
final Set<TargetIdName> assignedTargetIds = managementUIState.getAssignedList().keySet();
|
|
||||||
if (assignedTargetIds.contains(pinnedTarget.get())) {
|
|
||||||
getEventBus().publish(this, PinUnpinEvent.PIN_TARGET);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isMaintenanceWindowValid() {
|
|
||||||
if (maintenanceWindowLayout.isEnabled()) {
|
|
||||||
try {
|
|
||||||
MaintenanceScheduleHelper.validateMaintenanceSchedule(maintenanceWindowLayout.getMaintenanceSchedule(),
|
|
||||||
maintenanceWindowLayout.getMaintenanceDuration(),
|
|
||||||
maintenanceWindowLayout.getMaintenanceTimeZone());
|
|
||||||
} catch (final InvalidMaintenanceScheduleException e) {
|
|
||||||
LOG.error("Maintenance window is not valid", e);
|
|
||||||
getNotification().displayValidationError(e.getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assignDsToTarget(final DragAndDropEvent event) {
|
private void assignDsToTarget(final DragAndDropEvent event) {
|
||||||
final TableTransferable transferable = (TableTransferable) event.getTransferable();
|
final TableTransferable transferable = (TableTransferable) event.getTransferable();
|
||||||
final AbstractTable<?> source = (AbstractTable<?>) transferable.getSourceComponent();
|
final AbstractTable<?> source = (AbstractTable<?>) transferable.getSourceComponent();
|
||||||
@@ -988,65 +891,21 @@ public class TargetTable extends AbstractTable<Target> {
|
|||||||
getI18n().getMessage(MESSAGE_CONFIRM_ASSIGN_ENTITY, distributionNameToAssign, "target", targetName),
|
getI18n().getMessage(MESSAGE_CONFIRM_ASSIGN_ENTITY, distributionNameToAssign, "target", targetName),
|
||||||
getI18n().getMessage(UIMessageIdProvider.BUTTON_OK),
|
getI18n().getMessage(UIMessageIdProvider.BUTTON_OK),
|
||||||
getI18n().getMessage(UIMessageIdProvider.BUTTON_CANCEL), ok -> {
|
getI18n().getMessage(UIMessageIdProvider.BUTTON_CANCEL), ok -> {
|
||||||
if (ok && isMaintenanceWindowValid()) {
|
if (ok && isMaintenanceWindowValid(maintenanceWindowLayout, getNotification())) {
|
||||||
saveAllAssignments();
|
saveAllAssignments(managementUIState, actionTypeOptionGroupLayout, maintenanceWindowLayout,
|
||||||
|
deploymentManagement, getNotification(), getEventBus(), getI18n(), this);
|
||||||
} else {
|
} else {
|
||||||
managementUIState.getAssignedList().clear();
|
managementUIState.getAssignedList().clear();
|
||||||
}
|
}
|
||||||
}, createAssignmentTab(), UIComponentIdProvider.DIST_SET_TO_TARGET_ASSIGNMENT_CONFIRM_ID);
|
}, createAssignmentTab(actionTypeOptionGroupLayout, maintenanceWindowLayout, saveButtonToggle(),
|
||||||
|
getI18n(), uiProperties),
|
||||||
|
UIComponentIdProvider.DIST_SET_TO_TARGET_ASSIGNMENT_CONFIRM_ID);
|
||||||
UI.getCurrent().addWindow(confirmDialog.getWindow());
|
UI.getCurrent().addWindow(confirmDialog.getWindow());
|
||||||
confirmDialog.getWindow().bringToFront();
|
confirmDialog.getWindow().bringToFront();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfirmationTab createAssignmentTab() {
|
private Consumer<Boolean> saveButtonToggle() {
|
||||||
final ConfirmationTab assignmentTab = new ConfirmationTab();
|
return isEnabled -> confirmDialog.getOkButton().setEnabled(isEnabled);
|
||||||
actionTypeOptionGroupLayout.selectDefaultOption();
|
|
||||||
assignmentTab.addComponent(actionTypeOptionGroupLayout);
|
|
||||||
assignmentTab.addComponent(enableMaintenanceWindowLayout());
|
|
||||||
initMaintenanceWindow();
|
|
||||||
assignmentTab.addComponent(maintenanceWindowLayout);
|
|
||||||
return assignmentTab;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HorizontalLayout enableMaintenanceWindowLayout() {
|
|
||||||
final HorizontalLayout layout = new HorizontalLayout();
|
|
||||||
layout.addComponent(enableMaintenanceWindowControl());
|
|
||||||
layout.addComponent(maintenanceWindowHelpLinkControl());
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CheckBox enableMaintenanceWindowControl() {
|
|
||||||
final CheckBox enableMaintenanceWindow = new CheckBox(
|
|
||||||
getI18n().getMessage("caption.maintenancewindow.enabled"));
|
|
||||||
enableMaintenanceWindow.setId(UIComponentIdProvider.MAINTENANCE_WINDOW_ENABLED_ID);
|
|
||||||
enableMaintenanceWindow.addStyleName(ValoTheme.CHECKBOX_SMALL);
|
|
||||||
enableMaintenanceWindow.addStyleName("dist-window-maintenance-window-enable");
|
|
||||||
enableMaintenanceWindow.addValueChangeListener(event -> {
|
|
||||||
final Boolean isMaintenanceWindowEnabled = enableMaintenanceWindow.getValue();
|
|
||||||
maintenanceWindowLayout.setVisible(isMaintenanceWindowEnabled);
|
|
||||||
maintenanceWindowLayout.setEnabled(isMaintenanceWindowEnabled);
|
|
||||||
enableSaveButton(!isMaintenanceWindowEnabled);
|
|
||||||
maintenanceWindowLayout.clearAllControls();
|
|
||||||
});
|
|
||||||
return enableMaintenanceWindow;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Link maintenanceWindowHelpLinkControl() {
|
|
||||||
final String maintenanceWindowHelpUrl = uiProperties.getLinks().getDocumentation().getMaintenanceWindowView();
|
|
||||||
return SPUIComponentProvider.getHelpLink(getI18n(), maintenanceWindowHelpUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initMaintenanceWindow() {
|
|
||||||
maintenanceWindowLayout.setVisible(false);
|
|
||||||
maintenanceWindowLayout.setEnabled(false);
|
|
||||||
maintenanceWindowLayout.getScheduleControl()
|
|
||||||
.addTextChangeListener(event -> enableSaveButton(maintenanceWindowLayout.onScheduleChange(event)));
|
|
||||||
maintenanceWindowLayout.getDurationControl()
|
|
||||||
.addTextChangeListener(event -> enableSaveButton(maintenanceWindowLayout.onDurationChange(event)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enableSaveButton(final boolean enabled) {
|
|
||||||
confirmDialog.getOkButton().setEnabled(enabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addNewTargetToAssignmentList(final TargetIdName createTargetIdName,
|
private void addNewTargetToAssignmentList(final TargetIdName createTargetIdName,
|
||||||
|
|||||||
@@ -36,10 +36,6 @@ public class TargetTableLayout extends AbstractTableLayout<TargetTable> {
|
|||||||
|
|
||||||
private final transient EventBus.UIEventBus eventBus;
|
private final transient EventBus.UIEventBus eventBus;
|
||||||
|
|
||||||
private final TargetDetails targetDetails;
|
|
||||||
|
|
||||||
private final TargetTableHeader targetTableHeader;
|
|
||||||
|
|
||||||
public TargetTableLayout(final UIEventBus eventBus, final TargetTable targetTable,
|
public TargetTableLayout(final UIEventBus eventBus, final TargetTable targetTable,
|
||||||
final TargetManagement targetManagement, final EntityFactory entityFactory, final VaadinMessageSource i18n,
|
final TargetManagement targetManagement, final EntityFactory entityFactory, final VaadinMessageSource i18n,
|
||||||
final UINotification uiNotification, final ManagementUIState managementUIState,
|
final UINotification uiNotification, final ManagementUIState managementUIState,
|
||||||
@@ -50,10 +46,10 @@ public class TargetTableLayout extends AbstractTableLayout<TargetTable> {
|
|||||||
final TargetMetadataPopupLayout targetMetadataPopupLayout = new TargetMetadataPopupLayout(i18n, uiNotification,
|
final TargetMetadataPopupLayout targetMetadataPopupLayout = new TargetMetadataPopupLayout(i18n, uiNotification,
|
||||||
eventBus, targetManagement, entityFactory, permissionChecker);
|
eventBus, targetManagement, entityFactory, permissionChecker);
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.targetDetails = new TargetDetails(i18n, eventBus, permissionChecker, managementUIState, uiNotification,
|
TargetDetails targetDetails = new TargetDetails(i18n, eventBus, permissionChecker, managementUIState, uiNotification,
|
||||||
tagManagement, targetManagement, targetMetadataPopupLayout, deploymentManagement, entityFactory,
|
tagManagement, targetManagement, targetMetadataPopupLayout, deploymentManagement, entityFactory,
|
||||||
targetTable);
|
targetTable);
|
||||||
this.targetTableHeader = new TargetTableHeader(i18n, permissionChecker, eventBus, uiNotification,
|
TargetTableHeader targetTableHeader = new TargetTableHeader(i18n, permissionChecker, eventBus, uiNotification,
|
||||||
managementUIState, managementViewClientCriterion, targetManagement, deploymentManagement, uiProperties,
|
managementUIState, managementViewClientCriterion, targetManagement, deploymentManagement, uiProperties,
|
||||||
entityFactory, uiNotification, tagManagement, distributionSetManagement, uiExecutor, targetTable);
|
entityFactory, uiNotification, tagManagement, distributionSetManagement, uiExecutor, targetTable);
|
||||||
|
|
||||||
|
|||||||
@@ -392,6 +392,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
private CommonDialogWindow createWindow() {
|
private CommonDialogWindow createWindow() {
|
||||||
return new WindowBuilder(SPUIDefinitions.CREATE_UPDATE_WINDOW)
|
return new WindowBuilder(SPUIDefinitions.CREATE_UPDATE_WINDOW)
|
||||||
.caption(i18n.getMessage("caption.create.new", i18n.getMessage("caption.rollout"))).content(this)
|
.caption(i18n.getMessage("caption.create.new", i18n.getMessage("caption.rollout"))).content(this)
|
||||||
|
.id(UIComponentIdProvider.ROLLOUT_POPUP_ID)
|
||||||
.layout(this).i18n(i18n).helpLink(uiProperties.getLinks().getDocumentation().getRolloutView())
|
.layout(this).i18n(i18n).helpLink(uiProperties.getLinks().getDocumentation().getRolloutView())
|
||||||
.saveDialogCloseListener(new SaveOnDialogCloseListener()).buildCommonDialogWindow();
|
.saveDialogCloseListener(new SaveOnDialogCloseListener()).buildCommonDialogWindow();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.ui.rollout.rollout;
|
package org.eclipse.hawkbit.ui.rollout.rollout;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
||||||
import org.eclipse.hawkbit.ui.customrenderers.client.renderers.RolloutRendererData;
|
import org.eclipse.hawkbit.ui.customrenderers.client.renderers.RolloutRendererData;
|
||||||
@@ -45,6 +46,15 @@ public class ProxyRollout {
|
|||||||
private TotalTargetCountStatus totalTargetCountStatus;
|
private TotalTargetCountStatus totalTargetCountStatus;
|
||||||
private String approvalDecidedBy;
|
private String approvalDecidedBy;
|
||||||
private String approvalRemark;
|
private String approvalRemark;
|
||||||
|
private ActionType actionType;
|
||||||
|
|
||||||
|
public ActionType getActionType() {
|
||||||
|
return actionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActionType(final ActionType actionType) {
|
||||||
|
this.actionType = actionType;
|
||||||
|
}
|
||||||
|
|
||||||
public RolloutRendererData getRolloutRendererData() {
|
public RolloutRendererData getRolloutRendererData() {
|
||||||
return rolloutRendererData;
|
return rolloutRendererData;
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ public class RolloutBeanQuery extends AbstractBeanQuery<ProxyRollout> {
|
|||||||
proxyRollout.setForcedTime(rollout.getForcedTime());
|
proxyRollout.setForcedTime(rollout.getForcedTime());
|
||||||
proxyRollout.setId(rollout.getId());
|
proxyRollout.setId(rollout.getId());
|
||||||
proxyRollout.setStatus(rollout.getStatus());
|
proxyRollout.setStatus(rollout.getStatus());
|
||||||
|
proxyRollout.setActionType(rollout.getActionType());
|
||||||
proxyRollout.setRolloutRendererData(new RolloutRendererData(rollout.getName(), rollout.getStatus().toString()));
|
proxyRollout.setRolloutRendererData(new RolloutRendererData(rollout.getName(), rollout.getStatus().toString()));
|
||||||
|
|
||||||
final TotalTargetCountStatus totalTargetCountActionStatus = rollout.getTotalTargetCountStatus();
|
final TotalTargetCountStatus totalTargetCountActionStatus = rollout.getTotalTargetCountStatus();
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.eclipse.hawkbit.repository.RolloutManagement;
|
|||||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
||||||
@@ -38,6 +39,7 @@ import org.eclipse.hawkbit.ui.common.ConfirmationDialog;
|
|||||||
import org.eclipse.hawkbit.ui.common.grid.AbstractGrid;
|
import org.eclipse.hawkbit.ui.common.grid.AbstractGrid;
|
||||||
import org.eclipse.hawkbit.ui.customrenderers.client.renderers.RolloutRendererData;
|
import org.eclipse.hawkbit.ui.customrenderers.client.renderers.RolloutRendererData;
|
||||||
import org.eclipse.hawkbit.ui.customrenderers.renderers.AbstractGridButtonConverter;
|
import org.eclipse.hawkbit.ui.customrenderers.renderers.AbstractGridButtonConverter;
|
||||||
|
import org.eclipse.hawkbit.ui.customrenderers.renderers.AbstractHtmlLabelConverter;
|
||||||
import org.eclipse.hawkbit.ui.customrenderers.renderers.GridButtonRenderer;
|
import org.eclipse.hawkbit.ui.customrenderers.renderers.GridButtonRenderer;
|
||||||
import org.eclipse.hawkbit.ui.customrenderers.renderers.HtmlLabelRenderer;
|
import org.eclipse.hawkbit.ui.customrenderers.renderers.HtmlLabelRenderer;
|
||||||
import org.eclipse.hawkbit.ui.customrenderers.renderers.RolloutRenderer;
|
import org.eclipse.hawkbit.ui.customrenderers.renderers.RolloutRenderer;
|
||||||
@@ -87,6 +89,10 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
private static final String VIRT_PROP_UPDATE = "update";
|
private static final String VIRT_PROP_UPDATE = "update";
|
||||||
private static final String VIRT_PROP_COPY = "copy";
|
private static final String VIRT_PROP_COPY = "copy";
|
||||||
private static final String VIRT_PROP_DELETE = "delete";
|
private static final String VIRT_PROP_DELETE = "delete";
|
||||||
|
private static final String STATUS_ICON_DOWNLOAD_ONLY = "statusIconDownloadOnly";
|
||||||
|
private static final String STATUS_ICON_SOFT = "statusIconSoft";
|
||||||
|
private static final String STATUS_ICON_FORCED = "statusIconForced";
|
||||||
|
private static final String PROP_TYPE = "actionType";
|
||||||
|
|
||||||
private final transient RolloutManagement rolloutManagement;
|
private final transient RolloutManagement rolloutManagement;
|
||||||
|
|
||||||
@@ -100,6 +106,8 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
|
|
||||||
private final RolloutUIState rolloutUIState;
|
private final RolloutUIState rolloutUIState;
|
||||||
|
|
||||||
|
private final AlignCellStyleGenerator alignGenerator;
|
||||||
|
|
||||||
private static final List<RolloutStatus> DELETE_COPY_BUTTON_ENABLED = Arrays.asList(RolloutStatus.CREATING,
|
private static final List<RolloutStatus> DELETE_COPY_BUTTON_ENABLED = Arrays.asList(RolloutStatus.CREATING,
|
||||||
RolloutStatus.ERROR_CREATING, RolloutStatus.ERROR_STARTING, RolloutStatus.PAUSED, RolloutStatus.READY,
|
RolloutStatus.ERROR_CREATING, RolloutStatus.ERROR_STARTING, RolloutStatus.PAUSED, RolloutStatus.READY,
|
||||||
RolloutStatus.RUNNING, RolloutStatus.STARTING, RolloutStatus.STOPPED, RolloutStatus.FINISHED,
|
RolloutStatus.RUNNING, RolloutStatus.STARTING, RolloutStatus.STOPPED, RolloutStatus.FINISHED,
|
||||||
@@ -109,7 +117,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
RolloutStatus.ERROR_CREATING, RolloutStatus.ERROR_STARTING, RolloutStatus.PAUSED, RolloutStatus.READY,
|
RolloutStatus.ERROR_CREATING, RolloutStatus.ERROR_STARTING, RolloutStatus.PAUSED, RolloutStatus.READY,
|
||||||
RolloutStatus.RUNNING, RolloutStatus.STARTING, RolloutStatus.STOPPED);
|
RolloutStatus.RUNNING, RolloutStatus.STARTING, RolloutStatus.STOPPED);
|
||||||
|
|
||||||
private static final List<RolloutStatus> PAUSE_BUTTON_ENABLED = Arrays.asList(RolloutStatus.RUNNING);
|
private static final List<RolloutStatus> PAUSE_BUTTON_ENABLED = Collections.singletonList(RolloutStatus.RUNNING);
|
||||||
|
|
||||||
private static final List<RolloutStatus> RUN_BUTTON_ENABLED = Arrays.asList(RolloutStatus.READY,
|
private static final List<RolloutStatus> RUN_BUTTON_ENABLED = Arrays.asList(RolloutStatus.READY,
|
||||||
RolloutStatus.PAUSED);
|
RolloutStatus.PAUSED);
|
||||||
@@ -119,11 +127,13 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
|
|
||||||
private static final Map<RolloutStatus, StatusFontIcon> statusIconMap = new EnumMap<>(RolloutStatus.class);
|
private static final Map<RolloutStatus, StatusFontIcon> statusIconMap = new EnumMap<>(RolloutStatus.class);
|
||||||
|
|
||||||
private static final List<Object> HIDDEN_COLUMNS = Arrays.asList(SPUILabelDefinitions.VAR_CREATED_DATE,
|
private static final List<Object> HIDDEN_COLUMNS = Arrays.asList(PROP_TYPE, SPUILabelDefinitions.VAR_CREATED_DATE,
|
||||||
SPUILabelDefinitions.VAR_CREATED_USER, SPUILabelDefinitions.VAR_MODIFIED_DATE,
|
SPUILabelDefinitions.VAR_CREATED_USER, SPUILabelDefinitions.VAR_MODIFIED_DATE,
|
||||||
SPUILabelDefinitions.VAR_MODIFIED_BY, SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY,
|
SPUILabelDefinitions.VAR_MODIFIED_BY, SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY,
|
||||||
SPUILabelDefinitions.VAR_APPROVAL_REMARK, SPUILabelDefinitions.VAR_DESC);
|
SPUILabelDefinitions.VAR_APPROVAL_REMARK, SPUILabelDefinitions.VAR_DESC);
|
||||||
|
|
||||||
|
private static final String[] centerAlignedColumns = new String[] { PROP_TYPE };
|
||||||
|
|
||||||
static {
|
static {
|
||||||
statusIconMap.put(RolloutStatus.FINISHED,
|
statusIconMap.put(RolloutStatus.FINISHED,
|
||||||
new StatusFontIcon(FontAwesome.CHECK_CIRCLE, SPUIStyleDefinitions.STATUS_ICON_GREEN));
|
new StatusFontIcon(FontAwesome.CHECK_CIRCLE, SPUIStyleDefinitions.STATUS_ICON_GREEN));
|
||||||
@@ -163,6 +173,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
rolloutGroupManagement, quotaManagement);
|
rolloutGroupManagement, quotaManagement);
|
||||||
this.uiNotification = uiNotification;
|
this.uiNotification = uiNotification;
|
||||||
this.rolloutUIState = rolloutUIState;
|
this.rolloutUIState = rolloutUIState;
|
||||||
|
alignGenerator = new AlignCellStyleGenerator(null, centerAlignedColumns, null);
|
||||||
|
|
||||||
setGeneratedPropertySupport(new RolloutGeneratedPropertySupport());
|
setGeneratedPropertySupport(new RolloutGeneratedPropertySupport());
|
||||||
init();
|
init();
|
||||||
@@ -183,7 +194,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
refreshContainer();
|
refreshContainer();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,6 +242,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
private void updateItem(final Rollout rollout, final Item item) {
|
private void updateItem(final Rollout rollout, final Item item) {
|
||||||
final TotalTargetCountStatus totalTargetCountStatus = rollout.getTotalTargetCountStatus();
|
final TotalTargetCountStatus totalTargetCountStatus = rollout.getTotalTargetCountStatus();
|
||||||
item.getItemProperty(SPUILabelDefinitions.VAR_STATUS).setValue(rollout.getStatus());
|
item.getItemProperty(SPUILabelDefinitions.VAR_STATUS).setValue(rollout.getStatus());
|
||||||
|
item.getItemProperty(PROP_TYPE).setValue(rollout.getActionType());
|
||||||
item.getItemProperty(SPUILabelDefinitions.VAR_TOTAL_TARGETS_COUNT_STATUS).setValue(totalTargetCountStatus);
|
item.getItemProperty(SPUILabelDefinitions.VAR_TOTAL_TARGETS_COUNT_STATUS).setValue(totalTargetCountStatus);
|
||||||
final Long groupCount = Long
|
final Long groupCount = Long
|
||||||
.valueOf((Integer) item.getItemProperty(SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS).getValue());
|
.valueOf((Integer) item.getItemProperty(SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS).getValue());
|
||||||
@@ -270,6 +282,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
false);
|
false);
|
||||||
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY, String.class, null,
|
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY, String.class, null,
|
||||||
false, false);
|
false, false);
|
||||||
|
rolloutGridContainer.addContainerProperty(PROP_TYPE, ActionType.class, null, false, false);
|
||||||
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_APPROVAL_REMARK, String.class, null, false,
|
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_APPROVAL_REMARK, String.class, null, false,
|
||||||
false);
|
false);
|
||||||
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_MODIFIED_DATE, String.class, null, false,
|
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_MODIFIED_DATE, String.class, null, false,
|
||||||
@@ -307,6 +320,9 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
getColumn(SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS).setMinimumWidth(40);
|
getColumn(SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS).setMinimumWidth(40);
|
||||||
getColumn(SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS).setMaximumWidth(60);
|
getColumn(SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS).setMaximumWidth(60);
|
||||||
|
|
||||||
|
getColumn(PROP_TYPE).setMinimumWidth(45);
|
||||||
|
getColumn(PROP_TYPE).setMaximumWidth(45);
|
||||||
|
|
||||||
getColumn(VIRT_PROP_RUN).setMinimumWidth(25);
|
getColumn(VIRT_PROP_RUN).setMinimumWidth(25);
|
||||||
getColumn(VIRT_PROP_RUN).setMaximumWidth(25);
|
getColumn(VIRT_PROP_RUN).setMaximumWidth(25);
|
||||||
|
|
||||||
@@ -335,6 +351,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
.setHeaderCaption(i18n.getMessage("header.distributionset"));
|
.setHeaderCaption(i18n.getMessage("header.distributionset"));
|
||||||
getColumn(SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS).setHeaderCaption(i18n.getMessage("header.numberofgroups"));
|
getColumn(SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS).setHeaderCaption(i18n.getMessage("header.numberofgroups"));
|
||||||
getColumn(SPUILabelDefinitions.VAR_TOTAL_TARGETS).setHeaderCaption(i18n.getMessage("header.total.targets"));
|
getColumn(SPUILabelDefinitions.VAR_TOTAL_TARGETS).setHeaderCaption(i18n.getMessage("header.total.targets"));
|
||||||
|
getColumn(PROP_TYPE).setHeaderCaption(i18n.getMessage("header.type"));
|
||||||
getColumn(SPUILabelDefinitions.VAR_CREATED_DATE).setHeaderCaption(i18n.getMessage("header.createdDate"));
|
getColumn(SPUILabelDefinitions.VAR_CREATED_DATE).setHeaderCaption(i18n.getMessage("header.createdDate"));
|
||||||
getColumn(SPUILabelDefinitions.VAR_CREATED_USER).setHeaderCaption(i18n.getMessage("header.createdBy"));
|
getColumn(SPUILabelDefinitions.VAR_CREATED_USER).setHeaderCaption(i18n.getMessage("header.createdBy"));
|
||||||
getColumn(SPUILabelDefinitions.VAR_MODIFIED_DATE).setHeaderCaption(i18n.getMessage("header.modifiedDate"));
|
getColumn(SPUILabelDefinitions.VAR_MODIFIED_DATE).setHeaderCaption(i18n.getMessage("header.modifiedDate"));
|
||||||
@@ -374,13 +391,14 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
final List<String> columnsToShowInOrder = Arrays.asList(ROLLOUT_RENDERER_DATA,
|
final List<String> columnsToShowInOrder = Arrays.asList(ROLLOUT_RENDERER_DATA,
|
||||||
SPUILabelDefinitions.VAR_DIST_NAME_VERSION, SPUILabelDefinitions.VAR_STATUS,
|
SPUILabelDefinitions.VAR_DIST_NAME_VERSION, SPUILabelDefinitions.VAR_STATUS,
|
||||||
SPUILabelDefinitions.VAR_TOTAL_TARGETS_COUNT_STATUS, SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS,
|
SPUILabelDefinitions.VAR_TOTAL_TARGETS_COUNT_STATUS, SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS,
|
||||||
SPUILabelDefinitions.VAR_TOTAL_TARGETS, VIRT_PROP_APPROVE, VIRT_PROP_RUN, VIRT_PROP_PAUSE,
|
SPUILabelDefinitions.VAR_TOTAL_TARGETS, PROP_TYPE, VIRT_PROP_APPROVE, VIRT_PROP_RUN, VIRT_PROP_PAUSE,
|
||||||
VIRT_PROP_UPDATE, VIRT_PROP_COPY, VIRT_PROP_DELETE, SPUILabelDefinitions.VAR_CREATED_DATE,
|
VIRT_PROP_UPDATE, VIRT_PROP_COPY, VIRT_PROP_DELETE, SPUILabelDefinitions.VAR_CREATED_DATE,
|
||||||
SPUILabelDefinitions.VAR_CREATED_USER, SPUILabelDefinitions.VAR_MODIFIED_DATE,
|
SPUILabelDefinitions.VAR_CREATED_USER, SPUILabelDefinitions.VAR_MODIFIED_DATE,
|
||||||
SPUILabelDefinitions.VAR_MODIFIED_BY, SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY,
|
SPUILabelDefinitions.VAR_MODIFIED_BY, SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY,
|
||||||
SPUILabelDefinitions.VAR_APPROVAL_REMARK, SPUILabelDefinitions.VAR_DESC);
|
SPUILabelDefinitions.VAR_APPROVAL_REMARK, SPUILabelDefinitions.VAR_DESC);
|
||||||
|
|
||||||
setColumns(columnsToShowInOrder.toArray());
|
setColumns(columnsToShowInOrder.toArray());
|
||||||
|
setCellStyleGenerator(alignGenerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -403,7 +421,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addColumnRenderes() {
|
protected void addColumnRenderers() {
|
||||||
getColumn(SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS).setRenderer(new HtmlRenderer(),
|
getColumn(SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS).setRenderer(new HtmlRenderer(),
|
||||||
new TotalTargetGroupsConverter());
|
new TotalTargetGroupsConverter());
|
||||||
getColumn(SPUILabelDefinitions.VAR_TOTAL_TARGETS_COUNT_STATUS).setRenderer(new HtmlRenderer(),
|
getColumn(SPUILabelDefinitions.VAR_TOTAL_TARGETS_COUNT_STATUS).setRenderer(new HtmlRenderer(),
|
||||||
@@ -411,6 +429,9 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
|
|
||||||
getColumn(SPUILabelDefinitions.VAR_STATUS).setRenderer(new HtmlLabelRenderer(), new RolloutStatusConverter());
|
getColumn(SPUILabelDefinitions.VAR_STATUS).setRenderer(new HtmlLabelRenderer(), new RolloutStatusConverter());
|
||||||
|
|
||||||
|
getColumn(PROP_TYPE).setRenderer(new HtmlLabelRenderer(),
|
||||||
|
new RolloutTypeConverter(this::createTypeLabelAdapter));
|
||||||
|
|
||||||
final RolloutRenderer customObjectRenderer = new RolloutRenderer(RolloutRendererData.class);
|
final RolloutRenderer customObjectRenderer = new RolloutRenderer(RolloutRendererData.class);
|
||||||
customObjectRenderer.addClickListener(this::onClickOfRolloutName);
|
customObjectRenderer.addClickListener(this::onClickOfRolloutName);
|
||||||
getColumn(ROLLOUT_RENDERER_DATA).setRenderer(customObjectRenderer);
|
getColumn(ROLLOUT_RENDERER_DATA).setRenderer(customObjectRenderer);
|
||||||
@@ -465,7 +486,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LazyQueryContainer getRawContainer() {
|
public LazyQueryContainer getRawContainer() {
|
||||||
return (LazyQueryContainer) (getDecoratedContainer()).getWrappedContainer();
|
return (LazyQueryContainer) getDecoratedContainer().getWrappedContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -550,7 +571,6 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
if (RolloutStatus.PAUSED.equals(rolloutStatus)) {
|
if (RolloutStatus.PAUSED.equals(rolloutStatus)) {
|
||||||
rolloutManagement.resumeRollout(rolloutId);
|
rolloutManagement.resumeRollout(rolloutId);
|
||||||
uiNotification.displaySuccess(i18n.getMessage("message.rollout.resumed", rolloutName));
|
uiNotification.displaySuccess(i18n.getMessage("message.rollout.resumed", rolloutName));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,7 +628,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
}
|
}
|
||||||
final Long runningActions = statusTotalCount.get(Status.RUNNING);
|
final Long runningActions = statusTotalCount.get(Status.RUNNING);
|
||||||
String rolloutDetailsMessage = "";
|
String rolloutDetailsMessage = "";
|
||||||
if ((scheduledActions > 0) || (runningActions > 0)) {
|
if (scheduledActions > 0 || runningActions > 0) {
|
||||||
rolloutDetailsMessage = i18n.getMessage("message.delete.rollout.details", runningActions, scheduledActions);
|
rolloutDetailsMessage = i18n.getMessage("message.delete.rollout.details", runningActions, scheduledActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -716,6 +736,51 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Converter to convert {@link RolloutStatus} to string.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
class RolloutTypeConverter extends AbstractHtmlLabelConverter<ActionType> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
RolloutTypeConverter(final LabelAdapter<ActionType> adapter) {
|
||||||
|
addAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<ActionType> getModelType() {
|
||||||
|
return ActionType.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private StatusFontIcon createTypeLabelAdapter(final ActionType actionType) {
|
||||||
|
if (ActionType.FORCED.equals(actionType)) {
|
||||||
|
return new StatusFontIcon(FontAwesome.BOLT, STATUS_ICON_FORCED,
|
||||||
|
i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_FORCED),
|
||||||
|
UIComponentIdProvider.ACTION_HISTORY_TABLE_TYPE_LABEL_ID);
|
||||||
|
}
|
||||||
|
if (ActionType.TIMEFORCED.equals(actionType)) {
|
||||||
|
return new StatusFontIcon(FontAwesome.HISTORY, STATUS_ICON_FORCED,
|
||||||
|
i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_TIME_FORCED),
|
||||||
|
UIComponentIdProvider.ACTION_HISTORY_TABLE_TYPE_LABEL_ID);
|
||||||
|
}
|
||||||
|
if (ActionType.SOFT.equals(actionType)) {
|
||||||
|
return new StatusFontIcon(FontAwesome.STEP_FORWARD, STATUS_ICON_SOFT,
|
||||||
|
i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_SOFT),
|
||||||
|
UIComponentIdProvider.ACTION_HISTORY_TABLE_TYPE_LABEL_ID);
|
||||||
|
}
|
||||||
|
if (ActionType.DOWNLOAD_ONLY.equals(actionType)) {
|
||||||
|
return new StatusFontIcon(FontAwesome.DOWNLOAD, STATUS_ICON_DOWNLOAD_ONLY,
|
||||||
|
i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_DOWNLOAD_ONLY),
|
||||||
|
UIComponentIdProvider.ACTION_HISTORY_TABLE_TYPE_LABEL_ID);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converter to convert {@link TotalTargetCountStatus} to formatted string
|
* Converter to convert {@link TotalTargetCountStatus} to formatted string
|
||||||
* with status and count details.
|
* with status and count details.
|
||||||
@@ -782,7 +847,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final void hideColumnsDueToInsufficientPermissions() {
|
private void hideColumnsDueToInsufficientPermissions() {
|
||||||
|
|
||||||
final List<Object> modifiableColumnsList = getColumns().stream().map(Column::getPropertyId)
|
final List<Object> modifiableColumnsList = getColumns().stream().map(Column::getPropertyId)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ public class RolloutGroupListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addColumnRenderes() {
|
protected void addColumnRenderers() {
|
||||||
getColumn(SPUILabelDefinitions.VAR_STATUS).setRenderer(new HtmlLabelRenderer(),
|
getColumn(SPUILabelDefinitions.VAR_STATUS).setRenderer(new HtmlLabelRenderer(),
|
||||||
new RolloutGroupStatusConverter());
|
new RolloutGroupStatusConverter());
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import java.util.EnumMap;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||||
@@ -173,7 +174,7 @@ public class RolloutGroupTargetsListGrid extends AbstractGrid<LazyQueryContainer
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addColumnRenderes() {
|
protected void addColumnRenderers() {
|
||||||
getColumn(SPUILabelDefinitions.VAR_STATUS).setRenderer(new HtmlLabelRenderer(), new StatusConverter());
|
getColumn(SPUILabelDefinitions.VAR_STATUS).setRenderer(new HtmlLabelRenderer(), new StatusConverter());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,11 +239,20 @@ public class RolloutGroupTargetsListGrid extends AbstractGrid<LazyQueryContainer
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String processActionStatus(final Status status) {
|
private String processActionStatus(final Status status) {
|
||||||
final StatusFontIcon statusFontIcon = statusIconMap.get(status);
|
final StatusFontIcon statusFontIcon = mapActionStatusToPresentation(status);
|
||||||
final String codePoint = HawkbitCommonUtil.getCodePoint(statusFontIcon);
|
final String codePoint = HawkbitCommonUtil.getCodePoint(statusFontIcon);
|
||||||
return HawkbitCommonUtil.getStatusLabelDetailsInString(codePoint, statusFontIcon.getStyle(), null);
|
return HawkbitCommonUtil.getStatusLabelDetailsInString(codePoint, statusFontIcon.getStyle(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private StatusFontIcon mapActionStatusToPresentation(final Status status) {
|
||||||
|
final boolean isFinishedDownloadOnlyAssignment = Status.DOWNLOADED.equals(status)
|
||||||
|
&& rolloutUIState.getRolloutGroup()
|
||||||
|
.map(group -> Action.ActionType.DOWNLOAD_ONLY.equals(group.getRollout().getActionType()))
|
||||||
|
.orElse(false);
|
||||||
|
|
||||||
|
return isFinishedDownloadOnlyAssignment ? statusIconMap.get(Status.FINISHED) : statusIconMap.get(status);
|
||||||
|
}
|
||||||
|
|
||||||
private String getStatus() {
|
private String getStatus() {
|
||||||
final RolloutGroup rolloutGroup = rolloutUIState.getRolloutGroup().orElse(null);
|
final RolloutGroup rolloutGroup = rolloutUIState.getRolloutGroup().orElse(null);
|
||||||
if (rolloutGroup != null && rolloutGroup.getStatus() == RolloutGroupStatus.READY) {
|
if (rolloutGroup != null && rolloutGroup.getStatus() == RolloutGroupStatus.READY) {
|
||||||
|
|||||||
@@ -177,6 +177,10 @@ public final class UIComponentIdProvider {
|
|||||||
* Action history table cancel Id.
|
* Action history table cancel Id.
|
||||||
*/
|
*/
|
||||||
public static final String ACTION_DETAILS_SOFT_ID = "action.details.soft.group";
|
public static final String ACTION_DETAILS_SOFT_ID = "action.details.soft.group";
|
||||||
|
/**
|
||||||
|
* Download Only Action Id.
|
||||||
|
*/
|
||||||
|
public static final String ACTION_DETAILS_DOWNLOAD_ONLY_ID = "action.details.downloadonly.group";
|
||||||
/**
|
/**
|
||||||
* Start type of rollout manual radio button
|
* Start type of rollout manual radio button
|
||||||
*/
|
*/
|
||||||
@@ -256,7 +260,7 @@ public final class UIComponentIdProvider {
|
|||||||
/**
|
/**
|
||||||
* Action history table forced label Id.
|
* Action history table forced label Id.
|
||||||
*/
|
*/
|
||||||
public static final String ACTION_HISTORY_TABLE_FORCED_LABEL_ID = "action.history.table.forcedId";
|
public static final String ACTION_HISTORY_TABLE_TYPE_LABEL_ID = "action.history.table.typeId";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action history table time-forced label Id.
|
* Action history table time-forced label Id.
|
||||||
@@ -1145,6 +1149,11 @@ public final class UIComponentIdProvider {
|
|||||||
*/
|
*/
|
||||||
public static final String METADATA_POPUP_ID = "metadata.popup.id";
|
public static final String METADATA_POPUP_ID = "metadata.popup.id";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollout popup id.
|
||||||
|
*/
|
||||||
|
public static final String ROLLOUT_POPUP_ID = "add.update.rollout.popup";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DistributionSet table details tab id in Distributions .
|
* DistributionSet table details tab id in Distributions .
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ public final class UIMessageIdProvider {
|
|||||||
|
|
||||||
public static final String CAPTION_ACTION_SOFT = "label.action.soft";
|
public static final String CAPTION_ACTION_SOFT = "label.action.soft";
|
||||||
|
|
||||||
|
public static final String CAPTION_ACTION_DOWNLOAD_ONLY = "label.action.downloadonly";
|
||||||
|
|
||||||
public static final String CAPTION_ACTION_TIME_FORCED = "label.action.time.forced";
|
public static final String CAPTION_ACTION_TIME_FORCED = "label.action.time.forced";
|
||||||
|
|
||||||
public static final String CAPTION_ACTION_MESSAGES = "caption.action.messages";
|
public static final String CAPTION_ACTION_MESSAGES = "caption.action.messages";
|
||||||
@@ -129,6 +131,8 @@ public final class UIMessageIdProvider {
|
|||||||
|
|
||||||
public static final String TOOLTIP_SOFT_ITEM = "tooltip.soft.item";
|
public static final String TOOLTIP_SOFT_ITEM = "tooltip.soft.item";
|
||||||
|
|
||||||
|
public static final String TOOLTIP_DOWNLOAD_ONLY_ITEM = "tooltip.downloadonly.item";
|
||||||
|
|
||||||
public static final String TOOLTIP_FORCED_ITEM = "tooltip.forced.item";
|
public static final String TOOLTIP_FORCED_ITEM = "tooltip.forced.item";
|
||||||
|
|
||||||
public static final String TOOLTIP_TARGET_PIN = "tooltip.target.pin";
|
public static final String TOOLTIP_TARGET_PIN = "tooltip.target.pin";
|
||||||
|
|||||||
@@ -165,7 +165,9 @@ label.active = Active
|
|||||||
label.action.id = Action Id
|
label.action.id = Action Id
|
||||||
label.no.tag = NO TAG
|
label.no.tag = NO TAG
|
||||||
label.action.forced = Forced
|
label.action.forced = Forced
|
||||||
|
label.action.type = Type
|
||||||
label.action.soft = Soft
|
label.action.soft = Soft
|
||||||
|
label.action.downloadonly = Download Only
|
||||||
label.action.time.forced = Time Forced
|
label.action.time.forced = Time Forced
|
||||||
label.dist.details.type = Type :
|
label.dist.details.type = Type :
|
||||||
label.dist.details.name = Name :
|
label.dist.details.name = Name :
|
||||||
@@ -218,6 +220,7 @@ label.cancelled = Cancelled
|
|||||||
label.cancelling = Canceling
|
label.cancelling = Canceling
|
||||||
label.retrieved = Retrieved
|
label.retrieved = Retrieved
|
||||||
label.download = Downloading
|
label.download = Downloading
|
||||||
|
label.downloaded = Downloaded
|
||||||
label.unknown = Unknown
|
label.unknown = Unknown
|
||||||
label.target.id = Controller Id :
|
label.target.id = Controller Id :
|
||||||
label.target.ip = Controller IP :
|
label.target.ip = Controller IP :
|
||||||
@@ -283,6 +286,7 @@ tooltip.status.overdue = Overdue
|
|||||||
tooltip.delete.module = Select and delete Software Module
|
tooltip.delete.module = Select and delete Software Module
|
||||||
tooltip.forced.item=Device is supposed to install the update immediately
|
tooltip.forced.item=Device is supposed to install the update immediately
|
||||||
tooltip.soft.item=Device can execute the update at any time, e.g. with user approval or according to its regular update time plan
|
tooltip.soft.item=Device can execute the update at any time, e.g. with user approval or according to its regular update time plan
|
||||||
|
tooltip.downloadonly.item=Device is supposed to only download the update and not install it
|
||||||
tooltip.timeforced.item=Soft update which turns into a forced update after a specific time
|
tooltip.timeforced.item=Soft update which turns into a forced update after a specific time
|
||||||
tooltip.timeforced.forced.in=Auto forcing in {0}
|
tooltip.timeforced.forced.in=Auto forcing in {0}
|
||||||
tooltip.timeforced.forced.since=Auto forced since {0}
|
tooltip.timeforced.forced.since=Auto forced since {0}
|
||||||
@@ -409,6 +413,7 @@ message.forcequit.action = Force Quit..
|
|||||||
message.forcequit.action.success = Action has been force quit successfully !
|
message.forcequit.action.success = Action has been force quit successfully !
|
||||||
message.forcequit.action.failed = Force Quitting the action is not possible !
|
message.forcequit.action.failed = Force Quitting the action is not possible !
|
||||||
message.forcequit.action.confirm = Attention!\nForce quit should only be used when the assignment action is not working properly.\nForce quitting an action has no effect on the connected target. It is just resetting \nthe data stored on the SP update server. \nAre you absolutely sure you want to force quit this action?
|
message.forcequit.action.confirm = Attention!\nForce quit should only be used when the assignment action is not working properly.\nForce quitting an action has no effect on the connected target. It is just resetting \nthe data stored on the SP update server. \nAre you absolutely sure you want to force quit this action?
|
||||||
|
message.downloadonly.action = DownloadOnly
|
||||||
message.distribution.no.update = distribution {0} set is already assigned to targets and cannot be changed
|
message.distribution.no.update = distribution {0} set is already assigned to targets and cannot be changed
|
||||||
message.action.not.allowed = Action not allowed
|
message.action.not.allowed = Action not allowed
|
||||||
message.action.did.not.work = Action did not work. Please try again.
|
message.action.did.not.work = Action did not work. Please try again.
|
||||||
@@ -570,6 +575,7 @@ header.name = Name
|
|||||||
header.vendor = Vendor
|
header.vendor = Vendor
|
||||||
header.version = Version
|
header.version = Version
|
||||||
header.description = Description
|
header.description = Description
|
||||||
|
header.type = Type
|
||||||
header.createdBy = Created By
|
header.createdBy = Created By
|
||||||
header.createdDate = Created Date
|
header.createdDate = Created Date
|
||||||
header.modifiedBy = Modified By
|
header.modifiedBy = Modified By
|
||||||
|
|||||||
Reference in New Issue
Block a user