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
|
||||
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.
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 94 KiB |
@@ -225,7 +225,7 @@ public enum SpServerError {
|
||||
* invalid.
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -133,7 +133,6 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
|
||||
checkByTargetId(sha1Hash, secruityToken.getTargetId());
|
||||
} else {
|
||||
LOG.info("anonymous download no authentication check for artifact {}", sha1Hash);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -270,6 +270,12 @@ public class AmqpConfiguration {
|
||||
return new DefaultAmqpMessageSenderService(rabbitTemplate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create RabbitListenerContainerFactory bean if no listenerContainerFactory
|
||||
* bean found
|
||||
*
|
||||
* @return RabbitListenerContainerFactory bean
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "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.TargetDeletedEvent;
|
||||
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.SoftwareModule;
|
||||
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())
|
||||
.getContent()));
|
||||
|
||||
targetManagement.getByControllerID(assignedEvent.getActions().keySet())
|
||||
.forEach(target -> sendUpdateMessageToTarget(assignedEvent.getTenant(), target,
|
||||
assignedEvent.getActions().get(target.getControllerId()), modules,
|
||||
assignedEvent.isMaintenanceWindowAvailable()));
|
||||
targetManagement.getByControllerID(assignedEvent.getActions().keySet()).forEach(
|
||||
target -> sendUpdateMessageToTarget(assignedEvent.getActions().get(target.getControllerId()),
|
||||
target, modules));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get the type of event depending on whether the action has a
|
||||
* valid maintenance window available or not based on defined maintenance
|
||||
* schedule. In case of no maintenance schedule or if there is a valid
|
||||
* window available, the topic {@link EventTopic#DOWNLOAD_AND_INSTALL} is
|
||||
* Method to get the type of event depending on whether the action is a
|
||||
* DOWNLOAD_ONLY action or if it has a valid maintenance window available
|
||||
* or not based on defined maintenance schedule. In case of no maintenance
|
||||
* schedule or if there is a valid window available, the topic {@link EventTopic#DOWNLOAD_AND_INSTALL} is
|
||||
* returned else {@link EventTopic#DOWNLOAD} is returned.
|
||||
*
|
||||
* @param maintenanceWindowAvailable
|
||||
* valid maintenance window or not.
|
||||
* @param action
|
||||
* current action properties.
|
||||
*
|
||||
* @return {@link EventTopic} to use for message.
|
||||
*/
|
||||
private static EventTopic getEventTypeForTarget(final boolean maintenanceWindowAvailable) {
|
||||
return maintenanceWindowAvailable ? EventTopic.DOWNLOAD_AND_INSTALL : EventTopic.DOWNLOAD;
|
||||
private static EventTopic getEventTypeForTarget(final ActionProperties action) {
|
||||
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());
|
||||
}
|
||||
|
||||
protected void sendUpdateMessageToTarget(final String tenant, final Target target, final Long actionId,
|
||||
final Map<SoftwareModule, List<SoftwareModuleMetadata>> modules, final boolean maintenanceWindowAvailable) {
|
||||
protected void sendUpdateMessageToTarget(final ActionProperties action, final Target target,
|
||||
final Map<SoftwareModule, List<SoftwareModuleMetadata>> modules) {
|
||||
|
||||
String tenant = action.getTenant();
|
||||
|
||||
final URI targetAdress = target.getAddress();
|
||||
if (!IpUtil.isAmqpUri(targetAdress)) {
|
||||
@@ -215,7 +219,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
}
|
||||
|
||||
final DmfDownloadAndUpdateRequest downloadAndUpdateRequest = new DmfDownloadAndUpdateRequest();
|
||||
downloadAndUpdateRequest.setActionId(actionId);
|
||||
downloadAndUpdateRequest.setActionId(action.getId());
|
||||
|
||||
final String targetSecurityToken = systemSecurityContext.runAsSystem(target::getSecurityToken);
|
||||
downloadAndUpdateRequest.setTargetSecurityToken(targetSecurityToken);
|
||||
@@ -227,8 +231,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
});
|
||||
|
||||
final Message message = getMessageConverter().toMessage(downloadAndUpdateRequest,
|
||||
createConnectorMessagePropertiesEvent(tenant, target.getControllerId(),
|
||||
getEventTypeForTarget(maintenanceWindowAvailable)));
|
||||
createConnectorMessagePropertiesEvent(tenant, target.getControllerId(), getEventTypeForTarget(action)));
|
||||
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.model.Action;
|
||||
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.SoftwareModuleMetadata;
|
||||
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())));
|
||||
|
||||
amqpMessageDispatcherService.sendUpdateMessageToTarget(action.getTenant(), action.getTarget(), action.getId(),
|
||||
modules, action.isMaintenanceWindowAvailable());
|
||||
amqpMessageDispatcherService.sendUpdateMessageToTarget(new ActionProperties(action), action.getTarget(),
|
||||
modules);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.amqp;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
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.model.Action;
|
||||
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.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -301,7 +301,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
|
||||
final Message message = new Message(new byte[0], messageProperties);
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||
amqpMessageHandlerService.onMessage(message, "unknownMessageType", TENANT, "vHost");
|
||||
fail("AmqpRejectAndDontRequeueException was excepeted due to unknown message type");
|
||||
} catch (final AmqpRejectAndDontRequeueException e) {
|
||||
}
|
||||
@@ -462,19 +462,16 @@ public class AmqpMessageHandlerServiceTest {
|
||||
// test
|
||||
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<Long> actionIdCaptor = ArgumentCaptor.forClass(Long.class);
|
||||
|
||||
verify(amqpMessageDispatcherServiceMock, times(1)).sendUpdateMessageToTarget(tenantCaptor.capture(),
|
||||
targetCaptor.capture(), actionIdCaptor.capture(), any(Map.class), anyBoolean());
|
||||
final String tenant = tenantCaptor.getValue();
|
||||
final String controllerId = targetCaptor.getValue().getControllerId();
|
||||
final Long actionId = actionIdCaptor.getValue();
|
||||
|
||||
assertThat(tenant).as("event has tenant").isEqualTo("DEFAULT");
|
||||
assertThat(controllerId).as("event has wrong controller id").isEqualTo("target1");
|
||||
assertThat(actionId).as("event has wrong action id").isEqualTo(22L);
|
||||
verify(amqpMessageDispatcherServiceMock, times(1))
|
||||
.sendUpdateMessageToTarget(actionPropertiesCaptor.capture(), targetCaptor.capture(), any(Map.class));
|
||||
final ActionProperties actionProperties = actionPropertiesCaptor.getValue();
|
||||
assertThat(actionProperties).isNotNull();
|
||||
assertThat(actionProperties.getTenant()).as("event has tenant").isEqualTo("DEFAULT");
|
||||
assertThat(targetCaptor.getValue().getControllerId()).as("event has wrong controller id").isEqualTo("target1");
|
||||
assertThat(actionProperties.getId()).as("event has wrong action id").isEqualTo(22L);
|
||||
|
||||
}
|
||||
|
||||
@@ -516,6 +513,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
when(actionMock.getId()).thenReturn(targetId);
|
||||
when(actionMock.getTenant()).thenReturn("DEFAULT");
|
||||
when(actionMock.getTarget()).thenReturn(targetMock);
|
||||
when(actionMock.getActionType()).thenReturn(Action.ActionType.SOFT);
|
||||
when(targetMock.getControllerId()).thenReturn("target1");
|
||||
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.AmqpTestConfiguration;
|
||||
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.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
@@ -235,7 +236,7 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
||||
assertThat(targetManagement.count()).isEqualTo(expectedTargetsCount);
|
||||
}
|
||||
|
||||
private Message assertReplyMessageHeader(final EventTopic eventTopic, final String controllerId) {
|
||||
protected Message assertReplyMessageHeader(final EventTopic eventTopic, final String controllerId) {
|
||||
verifyReplyToListener();
|
||||
final Message replyMessage = replyToListener.getEventTopicMessages().get(eventTopic);
|
||||
assertAllTargetsCount(1);
|
||||
@@ -379,4 +380,16 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
||||
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;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
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.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
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.TargetCreatedEvent;
|
||||
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.DistributionSetAssignmentResult;
|
||||
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.ExpectEvents;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.amqp.core.Message;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
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")
|
||||
@Story("Amqp Message Dispatcher Service")
|
||||
public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpServiceIntegrationTest {
|
||||
@@ -198,6 +207,37 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
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) {
|
||||
waitUntil(() -> {
|
||||
final Optional<Target> findTargetByControllerID = targetManagement.getByControllerID(controllerId);
|
||||
|
||||
@@ -9,14 +9,20 @@
|
||||
package org.eclipse.hawkbit.integration;
|
||||
|
||||
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.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
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.dmf.amqp.api.EventTopic;
|
||||
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.TargetCreatedEvent;
|
||||
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.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
@@ -797,6 +805,99 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
||||
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,
|
||||
final String value) {
|
||||
final DmfAttributeUpdate controllerAttribute = new DmfAttributeUpdate();
|
||||
@@ -887,4 +988,11 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
|
||||
createConditionFactory().untilAsserted(() -> Mockito
|
||||
.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
|
||||
private List<String> message;
|
||||
|
||||
public DmfActionUpdateStatus(@JsonProperty(value = "actionId", required = true) Long actionId,
|
||||
@JsonProperty(value = "actionStatus", required = true) DmfActionStatus actionStatus) {
|
||||
public DmfActionUpdateStatus(@JsonProperty(value = "actionId", required = true) final Long actionId,
|
||||
@JsonProperty(value = "actionStatus", required = true) final DmfActionStatus actionStatus) {
|
||||
this.actionId = actionId;
|
||||
this.actionStatus = actionStatus;
|
||||
}
|
||||
|
||||
@@ -23,9 +23,12 @@ public class RepositoryProperties {
|
||||
|
||||
/**
|
||||
* Set to <code>true</code> if the repository has to reject
|
||||
* {@link ActionStatus} entries for actions that are closed. Note: if this
|
||||
* is enforced you have to make sure that the feedback channel from the
|
||||
* devices i in order.
|
||||
* {@link ActionStatus} entries for actions that are closed. This is
|
||||
* especially useful if the action status feedback channel order from the
|
||||
* device cannot be guaranteed.
|
||||
*
|
||||
* Note: if this is enforced you have to make sure that the feedback
|
||||
* channel from the devices is in order.
|
||||
*/
|
||||
private boolean rejectActionStatusForClosedAction;
|
||||
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.event.remote;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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
|
||||
@@ -28,7 +29,7 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent {
|
||||
|
||||
private boolean maintenanceWindowAvailable;
|
||||
|
||||
private final Map<String, Long> actions = new HashMap<>();
|
||||
private final Map<String, ActionProperties> actions = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
@@ -57,12 +58,20 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent {
|
||||
this.distributionSetId = distributionSetId;
|
||||
this.maintenanceWindowAvailable = maintenanceWindowAvailable;
|
||||
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) {
|
||||
this(action.getTenant(), action.getDistributionSet().getId(), Arrays.asList(action), applicationId,
|
||||
this(action.getTenant(), action.getDistributionSet().getId(), Collections.singletonList(action), applicationId,
|
||||
action.isMaintenanceWindowAvailable());
|
||||
}
|
||||
|
||||
@@ -74,7 +83,7 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent {
|
||||
return maintenanceWindowAvailable;
|
||||
}
|
||||
|
||||
public Map<String, Long> getActions() {
|
||||
public Map<String, ActionProperties> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
|
||||
@@ -229,7 +229,12 @@ public interface Action extends TenantAwareBaseEntity {
|
||||
* {@link Action#isHitAutoForceTime(long)} is reached, {@link #FORCED}
|
||||
* 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
|
||||
*/
|
||||
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}.
|
||||
|
||||
@@ -58,6 +58,7 @@ public class TotalTargetCountStatus {
|
||||
|
||||
private final Map<Status, Long> statusTotalCountMap = new EnumMap<>(Status.class);
|
||||
private final Long totalTargetCount;
|
||||
private final Action.ActionType rolloutType;
|
||||
|
||||
/**
|
||||
* Create a new states map with the target count for each state.
|
||||
@@ -66,11 +67,14 @@ public class TotalTargetCountStatus {
|
||||
* the action state map
|
||||
* @param totalTargetCount
|
||||
* the total target count
|
||||
* @param rolloutType
|
||||
* the type of the rollout
|
||||
*/
|
||||
public TotalTargetCountStatus(final List<TotalTargetCountActionStatus> targetCountActionStatus,
|
||||
final Long totalTargetCount) {
|
||||
final Long totalTargetCount, final Action.ActionType rolloutType) {
|
||||
this.totalTargetCount = totalTargetCount;
|
||||
mapActionStatusToTotalTargetCountStatus(targetCountActionStatus);
|
||||
this.rolloutType = rolloutType;
|
||||
addToTotalCount(targetCountActionStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,9 +82,11 @@ public class TotalTargetCountStatus {
|
||||
*
|
||||
* @param totalTargetCount
|
||||
* the total target count
|
||||
* @param rolloutType
|
||||
* the type of the rollout
|
||||
*/
|
||||
public TotalTargetCountStatus(final Long totalTargetCount) {
|
||||
this(Collections.emptyList(), totalTargetCount);
|
||||
public TotalTargetCountStatus(final Long totalTargetCount, final Action.ActionType rolloutType) {
|
||||
this(Collections.emptyList(), totalTargetCount, rolloutType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,8 +125,7 @@ public class TotalTargetCountStatus {
|
||||
* @param rolloutStatusCountItems
|
||||
* all target {@link Status} with total count
|
||||
*/
|
||||
private final void mapActionStatusToTotalTargetCountStatus(
|
||||
final List<TotalTargetCountActionStatus> targetCountActionStatus) {
|
||||
private void addToTotalCount(final List<TotalTargetCountActionStatus> targetCountActionStatus) {
|
||||
if (targetCountActionStatus == null) {
|
||||
statusTotalCountMap.put(TotalTargetCountStatus.Status.NOTSTARTED, totalTargetCount);
|
||||
return;
|
||||
@@ -128,40 +133,40 @@ public class TotalTargetCountStatus {
|
||||
statusTotalCountMap.put(Status.RUNNING, 0L);
|
||||
Long notStartedTargetCount = totalTargetCount;
|
||||
for (final TotalTargetCountActionStatus item : targetCountActionStatus) {
|
||||
convertStatus(item);
|
||||
addToTotalCount(item);
|
||||
notStartedTargetCount -= item.getCount();
|
||||
}
|
||||
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
|
||||
// really complex.
|
||||
@SuppressWarnings("squid:MethodCyclomaticComplexity")
|
||||
private void convertStatus(final TotalTargetCountActionStatus item) {
|
||||
switch (item.getStatus()) {
|
||||
private Status convertStatus(final Action.Status status){
|
||||
switch (status) {
|
||||
case SCHEDULED:
|
||||
statusTotalCountMap.put(Status.SCHEDULED, item.getCount());
|
||||
break;
|
||||
return Status.SCHEDULED;
|
||||
case ERROR:
|
||||
statusTotalCountMap.put(Status.ERROR, item.getCount());
|
||||
break;
|
||||
return Status.ERROR;
|
||||
case FINISHED:
|
||||
statusTotalCountMap.put(Status.FINISHED, item.getCount());
|
||||
break;
|
||||
return Status.FINISHED;
|
||||
case CANCELED:
|
||||
return Status.CANCELLED;
|
||||
case RETRIEVED:
|
||||
case RUNNING:
|
||||
case WARNING:
|
||||
case DOWNLOAD:
|
||||
case DOWNLOADED:
|
||||
case CANCELING:
|
||||
final Long runningItemsCount = statusTotalCountMap.get(Status.RUNNING) + item.getCount();
|
||||
statusTotalCountMap.put(Status.RUNNING, runningItemsCount);
|
||||
break;
|
||||
case CANCELED:
|
||||
statusTotalCountMap.put(Status.CANCELLED, item.getCount());
|
||||
break;
|
||||
return Status.RUNNING;
|
||||
case DOWNLOADED:
|
||||
return Action.ActionType.DOWNLOAD_ONLY.equals(rolloutType) ? Status.FINISHED : Status.RUNNING;
|
||||
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
|
||||
* @param rolloutGroup
|
||||
* the rolloutgroup the actions are belong to
|
||||
* @param notStatus1
|
||||
* the status the action should not have
|
||||
* @param notStatus2
|
||||
* the status the action should not have
|
||||
* @param notStatus3
|
||||
* the status the action should not have
|
||||
* @param statuses
|
||||
* the list of statuses the action should not have
|
||||
* @return the count of actions referring the rollout and rolloutgroup and
|
||||
* are not in given states
|
||||
*/
|
||||
Long countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(JpaRollout rollout,
|
||||
JpaRolloutGroup rolloutGroup, Status notStatus1, Status notStatus2, Status notStatus3);
|
||||
Long countByRolloutAndRolloutGroupAndStatusNotIn(JpaRollout rollout, JpaRolloutGroup rolloutGroup,
|
||||
List<Status> statuses);
|
||||
|
||||
/**
|
||||
* Counts all actions referring to a given rollout and rolloutgroup.
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
*/
|
||||
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_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 ("
|
||||
+ 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("tenant", tenant);
|
||||
|
||||
@@ -561,23 +564,37 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());
|
||||
final JpaActionStatus actionStatus = create.build();
|
||||
|
||||
// if action is already closed we accept further status updates if
|
||||
// permitted so by configuration. This is especially useful if the
|
||||
// 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;
|
||||
if (isUpdatingActionStatusAllowed(action, actionStatus)) {
|
||||
return handleAddUpdateActionStatus(actionStatus, 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) {
|
||||
return !actionActive && (repositoryProperties.isRejectActionStatusForClosedAction()
|
||||
|| Status.ERROR.equals(actionStatus.getStatus()) || Status.FINISHED.equals(actionStatus.getStatus()));
|
||||
/**
|
||||
* ActionStatus updates are allowed mainly if the action is active. If the
|
||||
* action is not active we accept further status updates if permitted so
|
||||
* by repository configuration. In this case, only the values: Status.ERROR
|
||||
* and Status.FINISHED are allowed. In the case of a DOWNLOAD_ONLY action,
|
||||
* we accept status updates only once.
|
||||
*/
|
||||
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;
|
||||
LOG.debug("handleAddUpdateActionStatus for action {}", action.getId());
|
||||
|
||||
// information status entry - check for a potential DOS attack
|
||||
assertActionStatusQuota(action);
|
||||
assertActionStatusMessageQuota(actionStatus);
|
||||
|
||||
switch (actionStatus.getStatus()) {
|
||||
case ERROR:
|
||||
final JpaTarget target = (JpaTarget) action.getTarget();
|
||||
@@ -597,10 +618,10 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
case FINISHED:
|
||||
controllerId = handleFinishedAndStoreInTargetStatus(action);
|
||||
break;
|
||||
case DOWNLOADED:
|
||||
controllerId = handleDownloadedActionStatus(action);
|
||||
break;
|
||||
default:
|
||||
// information status entry - check for a potential DOS attack
|
||||
assertActionStatusQuota(action);
|
||||
assertActionStatusMessageQuota(actionStatus);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -615,6 +636,20 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
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) {
|
||||
final JpaTarget target = (JpaTarget) getByControllerId(controllerId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
|
||||
@@ -648,6 +683,12 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
target.setInstalledDistributionSet(ds);
|
||||
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
|
||||
// necessarily the case as another update might be pending already).
|
||||
if (target.getAssignedDistributionSet() != null
|
||||
|
||||
@@ -151,7 +151,8 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
|
||||
for (final JpaRolloutGroup rolloutGroup : rolloutGroups) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -177,7 +178,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
}
|
||||
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
|
||||
Long.valueOf(jpaRolloutGroup.getTotalTargets()));
|
||||
Long.valueOf(jpaRolloutGroup.getTotalTargets()), jpaRolloutGroup.getRollout().getActionType());
|
||||
jpaRolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
|
||||
return rolloutGroup;
|
||||
|
||||
|
||||
@@ -105,6 +105,8 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate.addSuccessAndErrorConditionsAndActions;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
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
|
||||
private RolloutRepository rolloutRepository;
|
||||
|
||||
@@ -251,17 +259,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
group.setParent(lastSavedGroup);
|
||||
group.setStatus(RolloutGroupStatus.CREATING);
|
||||
|
||||
group.setSuccessCondition(conditions.getSuccessCondition());
|
||||
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());
|
||||
addSuccessAndErrorConditionsAndActions(group, conditions);
|
||||
|
||||
group.setTargetPercentage(1.0F / (amountOfGroups - i) * 100);
|
||||
|
||||
@@ -310,17 +308,10 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
group.setTargetFilterQuery("");
|
||||
}
|
||||
|
||||
group.setSuccessCondition(srcGroup.getSuccessCondition());
|
||||
group.setSuccessConditionExp(srcGroup.getSuccessConditionExp());
|
||||
|
||||
group.setSuccessAction(srcGroup.getSuccessAction());
|
||||
group.setSuccessActionExp(srcGroup.getSuccessActionExp());
|
||||
|
||||
group.setErrorCondition(srcGroup.getErrorCondition());
|
||||
group.setErrorConditionExp(srcGroup.getErrorConditionExp());
|
||||
|
||||
group.setErrorAction(srcGroup.getErrorAction());
|
||||
group.setErrorActionExp(srcGroup.getErrorActionExp());
|
||||
addSuccessAndErrorConditionsAndActions(group, srcGroup.getSuccessCondition(),
|
||||
srcGroup.getSuccessConditionExp(), srcGroup.getSuccessAction(), srcGroup.getSuccessActionExp(),
|
||||
srcGroup.getErrorCondition(), srcGroup.getErrorConditionExp(), srcGroup.getErrorAction(),
|
||||
srcGroup.getErrorActionExp());
|
||||
|
||||
lastSavedGroup = rolloutGroupRepository.save(group);
|
||||
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup, rollout);
|
||||
@@ -760,9 +751,11 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
}
|
||||
|
||||
private boolean isRolloutGroupComplete(final JpaRollout rollout, final JpaRolloutGroup rolloutGroup) {
|
||||
final Long actionsLeftForRollout = actionRepository
|
||||
.countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(rollout, rolloutGroup,
|
||||
Action.Status.ERROR, Action.Status.FINISHED, Action.Status.CANCELED);
|
||||
final Long actionsLeftForRollout = ActionType.DOWNLOAD_ONLY.equals(rollout.getActionType())
|
||||
? actionRepository.countByRolloutAndRolloutGroupAndStatusNotIn(rollout, rolloutGroup,
|
||||
DOWNLOAD_ONLY_ACTION_TERMINATION_STATUSES)
|
||||
: actionRepository.countByRolloutAndRolloutGroupAndStatusNotIn(rollout, rolloutGroup,
|
||||
DEFAULT_ACTION_TERMINATION_STATUSES);
|
||||
return actionsLeftForRollout == 0;
|
||||
}
|
||||
|
||||
@@ -1070,7 +1063,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
}
|
||||
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
|
||||
rollout.get().getTotalTargets());
|
||||
rollout.get().getTotalTargets(), rollout.get().getActionType());
|
||||
((JpaRollout) rollout.get()).setTotalTargetCountStatus(totalTargetCountStatus);
|
||||
return rollout;
|
||||
}
|
||||
@@ -1112,7 +1105,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
if (allStatesForRollout != null) {
|
||||
rollouts.forEach(rollout -> {
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
||||
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets());
|
||||
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets(), rollout.getActionType());
|
||||
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.RolloutGroupCreate;
|
||||
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>
|
||||
implements RolloutGroupCreate {
|
||||
@@ -30,20 +32,66 @@ public class JpaRolloutGroupCreate extends AbstractRolloutGroupCreate<RolloutGro
|
||||
group.setTargetPercentage(targetPercentage);
|
||||
|
||||
if (conditions != null) {
|
||||
group.setSuccessCondition(conditions.getSuccessCondition());
|
||||
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());
|
||||
addSuccessAndErrorConditionsAndActions(group, conditions);
|
||||
}
|
||||
|
||||
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 = {
|
||||
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
|
||||
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
|
||||
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2") })
|
||||
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2"),
|
||||
@ConversionValue(objectValue = "DOWNLOAD_ONLY", dataValue = "3") })
|
||||
@Convert("actionType")
|
||||
@NotNull
|
||||
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 = {
|
||||
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
|
||||
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
|
||||
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2") })
|
||||
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2"),
|
||||
@ConversionValue(objectValue = "DOWNLOAD_ONLY", dataValue = "3") })
|
||||
@Convert("actionType")
|
||||
@NotNull
|
||||
private ActionType actionType = ActionType.FORCED;
|
||||
@@ -224,7 +225,7 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
||||
@Override
|
||||
public TotalTargetCountStatus getTotalTargetCountStatus() {
|
||||
if (totalTargetCountStatus == null) {
|
||||
totalTargetCountStatus = new TotalTargetCountStatus(totalTargets);
|
||||
totalTargetCountStatus = new TotalTargetCountStatus(totalTargets, actionType);
|
||||
}
|
||||
return totalTargetCountStatus;
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
|
||||
@Override
|
||||
public TotalTargetCountStatus getTotalTargetCountStatus() {
|
||||
if (totalTargetCountStatus == null) {
|
||||
totalTargetCountStatus = new TotalTargetCountStatus(Long.valueOf(totalTargets));
|
||||
totalTargetCountStatus = new TotalTargetCountStatus(Long.valueOf(totalTargets), rollout.getActionType());
|
||||
}
|
||||
return totalTargetCountStatus;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,9 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
|
||||
@Column(name = "auto_assign_action_type", nullable = true)
|
||||
@ObjectTypeConverter(name = "autoAssignActionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
|
||||
@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")
|
||||
private ActionType autoAssignActionType;
|
||||
|
||||
|
||||
@@ -38,8 +38,10 @@ public class ThresholdRolloutGroupSuccessCondition implements RolloutGroupCondit
|
||||
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(),
|
||||
rolloutGroup.getId(), Action.Status.FINISHED);
|
||||
rolloutGroup.getId(), completeActionStatus);
|
||||
try {
|
||||
final Integer threshold = Integer.valueOf(expression);
|
||||
// calculate threshold
|
||||
|
||||
@@ -10,14 +10,13 @@ package org.eclipse.hawkbit.repository.event.remote;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.AbstractMap.SimpleImmutableEntry;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionProperties;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.junit.Test;
|
||||
@@ -69,10 +68,10 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
|
||||
private void assertTargetAssignDistributionSetEvent(final Action action,
|
||||
final TargetAssignDistributionSetEvent underTest) {
|
||||
|
||||
final Entry<String, Long> entry = new SimpleImmutableEntry(action.getTarget().getControllerId(),
|
||||
action.getId());
|
||||
|
||||
assertThat(underTest.getActions()).containsExactly(entry);
|
||||
assertThat(underTest.getActions().size()).isEqualTo(1);
|
||||
ActionProperties actionProperties = underTest.getActions().get(action.getTarget().getControllerId());
|
||||
assertThat(actionProperties).isNotNull();
|
||||
assertThat(actionProperties).isEqualToComparingFieldByField(new ActionProperties(action));
|
||||
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.assertThatExceptionOfType;
|
||||
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 java.io.ByteArrayInputStream;
|
||||
@@ -20,6 +22,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
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.InvalidTargetAttributeException;
|
||||
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.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.ArtifactUpload;
|
||||
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.SoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||
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.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -148,7 +152,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
assertThat(actionStatusRepository.count()).isEqualTo(7);
|
||||
@@ -199,7 +203,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
||||
@@ -224,7 +228,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
// expected
|
||||
}
|
||||
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||
Action.Status.RUNNING, Action.Status.RUNNING, true);
|
||||
|
||||
assertThat(actionStatusRepository.count()).isEqualTo(1);
|
||||
@@ -245,14 +249,14 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Long actionId = createTargetAndAssignDs();
|
||||
|
||||
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);
|
||||
|
||||
simulateIntermediateStatusOnCancellation(actionId);
|
||||
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
assertThat(actionStatusRepository.count()).isEqualTo(8);
|
||||
@@ -272,14 +276,14 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Long actionId = createTargetAndAssignDs();
|
||||
|
||||
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);
|
||||
|
||||
simulateIntermediateStatusOnCancellation(actionId);
|
||||
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
assertThat(actionStatusRepository.count()).isEqualTo(8);
|
||||
@@ -300,14 +304,14 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Long actionId = createTargetAndAssignDs();
|
||||
|
||||
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);
|
||||
|
||||
simulateIntermediateStatusOnCancellation(actionId);
|
||||
|
||||
controllerManagement.addCancelActionStatus(
|
||||
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);
|
||||
|
||||
assertThat(actionStatusRepository.count()).isEqualTo(8);
|
||||
@@ -328,14 +332,14 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Long actionId = createTargetAndAssignDs();
|
||||
|
||||
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);
|
||||
|
||||
simulateIntermediateStatusOnCancellation(actionId);
|
||||
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
assertThat(actionStatusRepository.count()).isEqualTo(8);
|
||||
@@ -346,39 +350,64 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
private Long createTargetAndAssignDs() {
|
||||
final Long dsId = testdataFactory.createDistributionSet().getId();
|
||||
testdataFactory.createTarget();
|
||||
assignDistributionSet(dsId, TestdataFactory.DEFAULT_CONTROLLER_ID);
|
||||
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getUpdateStatus())
|
||||
assignDistributionSet(dsId, DEFAULT_CONTROLLER_ID);
|
||||
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.PENDING);
|
||||
|
||||
return deploymentManagement.findActiveActionsByTarget(PAGE, TestdataFactory.DEFAULT_CONTROLLER_ID).getContent()
|
||||
return deploymentManagement.findActiveActionsByTarget(PAGE, DEFAULT_CONTROLLER_ID).getContent()
|
||||
.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
|
||||
private void simulateIntermediateStatusOnCancellation(final Long actionId) {
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
controllerManagement
|
||||
.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);
|
||||
}
|
||||
|
||||
@@ -386,26 +415,26 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
private void simulateIntermediateStatusOnUpdate(final Long actionId) {
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
controllerManagement
|
||||
.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);
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
controllerManagement
|
||||
.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);
|
||||
}
|
||||
|
||||
@@ -509,7 +538,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
}
|
||||
|
||||
@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),
|
||||
@Expect(type = DistributionSetCreatedEvent.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
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
controllerManagement
|
||||
.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);
|
||||
|
||||
// try with disabled late feedback
|
||||
@@ -536,7 +565,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
|
||||
// test
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
|
||||
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
|
||||
Action.Status.ERROR, Action.Status.ERROR, false);
|
||||
|
||||
// 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));
|
||||
|
||||
// test
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
|
||||
assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
|
||||
Action.Status.ERROR, Action.Status.ERROR, false);
|
||||
|
||||
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
||||
@@ -572,7 +601,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
|
||||
// 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);
|
||||
|
||||
// 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));
|
||||
|
||||
// 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);
|
||||
|
||||
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
||||
@@ -609,7 +638,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING));
|
||||
|
||||
// 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);
|
||||
|
||||
assertThat(actionStatusRepository.count()).isEqualTo(3);
|
||||
@@ -635,7 +664,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING));
|
||||
|
||||
// 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);
|
||||
|
||||
// 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.ActionType;
|
||||
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.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
@@ -1086,11 +1087,15 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(event).isNotNull();
|
||||
assertThat(event.getDistributionSetId()).isEqualTo(ds.getId());
|
||||
|
||||
assertThat(event.getActions()).isEqualTo(targets.stream()
|
||||
.map(target -> deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
|
||||
.getContent())
|
||||
List<Long> eventActionIds = event.getActions().values().stream().map(ActionProperties::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Long> targetActiveActionIds = targets.stream()
|
||||
.map(t -> deploymentManagement.findActiveActionsByTarget(PAGE, t.getControllerId()).getContent())
|
||||
.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 {
|
||||
|
||||
@@ -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
|
||||
@Description("Verify that the targets have the right status during the rollout when an error emerges.")
|
||||
public void countCorrectStatusForEachTargetDuringRolloutWithError() {
|
||||
@@ -1727,12 +1790,19 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
private Rollout createSimpleTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout,
|
||||
final int amountOtherTargets, final int groupSize, final String successCondition,
|
||||
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");
|
||||
testdataFactory.createTargets(amountTargetsForRollout, "rollout-", "rollout");
|
||||
testdataFactory.createTargets(amountOtherTargets, "others-", "rollout");
|
||||
final String filterQuery = "controllerId==rollout-*";
|
||||
return testdataFactory.createRolloutByVariables("test-rollout-name-1", "test-rollout-description-1", groupSize,
|
||||
filterQuery, rolloutDS, successCondition, errorCondition);
|
||||
filterQuery, rolloutDS, successCondition, errorCondition, actionType);
|
||||
}
|
||||
|
||||
private Rollout createTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout,
|
||||
|
||||
@@ -196,6 +196,8 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
|
||||
|
||||
verifyAutoAssignmentWithSoftActionType(filterName, targetFilterQuery, distributionSet);
|
||||
|
||||
verifyAutoAssignmentWithDownloadOnlyActionType(filterName, targetFilterQuery, distributionSet);
|
||||
|
||||
verifyAutoAssignmentWithInvalidActionType(targetFilterQuery, distributionSet);
|
||||
|
||||
verifyAutoAssignmentWithIncompleteDs(targetFilterQuery);
|
||||
@@ -218,6 +220,14 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
|
||||
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
|
||||
private void verifyAutoAssignmentWithInvalidActionType(final TargetFilterQuery targetFilterQuery,
|
||||
final DistributionSet distributionSet) {
|
||||
|
||||
@@ -211,36 +211,46 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
|
||||
}
|
||||
|
||||
@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() {
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
final String targetDsAIdPref = "targA";
|
||||
final String targetDsBIdPref = "targB";
|
||||
final String targetDsAIdPref = "A";
|
||||
final String targetDsBIdPref = "B";
|
||||
final String targetDsCIdPref = "C";
|
||||
|
||||
final List<Target> targetsA = testdataFactory.createTargets(5, targetDsAIdPref,
|
||||
targetDsAIdPref.concat(" description"));
|
||||
final List<Target> targetsB = testdataFactory.createTargets(10, targetDsBIdPref,
|
||||
targetDsBIdPref.concat(" description"));
|
||||
final int targetsCount = targetsA.size() + targetsB.size();
|
||||
List<Target> targetsA = createTargetsAndAutoAssignDistSet(targetDsAIdPref, 5, distributionSet,
|
||||
ActionType.FORCED);
|
||||
List<Target> targetsB = createTargetsAndAutoAssignDistSet(targetDsBIdPref, 10, distributionSet,
|
||||
ActionType.SOFT);
|
||||
List<Target> targetsC = createTargetsAndAutoAssignDistSet(targetDsCIdPref, 10, distributionSet,
|
||||
ActionType.DOWNLOAD_ONLY);
|
||||
|
||||
targetFilterQueryManagement
|
||||
.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);
|
||||
final int targetsCount = targetsA.size() + targetsB.size() + targetsC.size();
|
||||
|
||||
autoAssignChecker.check();
|
||||
|
||||
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsA, targetsCount);
|
||||
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsB, targetsCount);
|
||||
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsC, targetsCount);
|
||||
|
||||
verifyThatTargetsHaveAssignmentActionType(ActionType.FORCED, targetsA);
|
||||
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
|
||||
|
||||
@@ -225,8 +225,14 @@ public abstract class AbstractIntegrationTest {
|
||||
};
|
||||
|
||||
protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final String controllerId) {
|
||||
return deploymentManagement.assignDistributionSet(dsID, Arrays.asList(
|
||||
new TargetWithActionType(controllerId, ActionType.FORCED, RepositoryModelConstants.NO_FORCE_TIME)));
|
||||
return assignDistributionSet(dsID, controllerId, ActionType.FORCED);
|
||||
}
|
||||
|
||||
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 DistributionSet ds = testdataFactory.createDistributionSet(distributionSet, isRequiredMigrationStep);
|
||||
Target savedTarget = testdataFactory.createTarget(controllerId);
|
||||
savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getAssignedEntity().iterator()
|
||||
.next();
|
||||
savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId(), ActionType.FORCED)
|
||||
.getAssignedEntity().iterator().next();
|
||||
Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
|
||||
.getContent().get(0);
|
||||
|
||||
|
||||
@@ -1003,14 +1003,41 @@ public class TestdataFactory {
|
||||
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
|
||||
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
|
||||
final String successCondition, final String errorCondition) {
|
||||
return createRolloutByVariables(rolloutName, rolloutDescription, groupSize, filterQuery, distributionSet, successCondition, errorCondition, Action.ActionType.FORCED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, successCondition)
|
||||
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition)
|
||||
.errorAction(RolloutGroupErrorAction.PAUSE, null).build();
|
||||
|
||||
final Rollout rollout = rolloutManagement.create(entityFactory.rollout().create().name(rolloutName)
|
||||
.description(rolloutDescription).targetFilterQuery(filterQuery).set(distributionSet), groupSize,
|
||||
conditions);
|
||||
.description(rolloutDescription).targetFilterQuery(filterQuery).set(distributionSet)
|
||||
.actionType(actionType), groupSize, conditions);
|
||||
|
||||
// Run here, because Scheduler is disabled during tests
|
||||
rolloutManagement.handleRollouts();
|
||||
|
||||
@@ -57,8 +57,8 @@ public class MgmtAction extends MgmtBaseEntity {
|
||||
@JsonProperty
|
||||
private Long forceTime;
|
||||
|
||||
@JsonProperty
|
||||
private MgmtActionType forceType;
|
||||
@JsonProperty(value="forceType")
|
||||
private MgmtActionType actionType;
|
||||
|
||||
@JsonProperty
|
||||
private MgmtMaintenanceWindow maintenanceWindow;
|
||||
@@ -79,12 +79,12 @@ public class MgmtAction extends MgmtBaseEntity {
|
||||
this.forceTime = forceTime;
|
||||
}
|
||||
|
||||
public MgmtActionType getForceType() {
|
||||
return forceType;
|
||||
public MgmtActionType getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public void setForceType(final MgmtActionType forceType) {
|
||||
this.forceType = forceType;
|
||||
public void setActionType(final MgmtActionType actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
|
||||
@@ -18,15 +18,15 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
*/
|
||||
public class MgmtActionRequestBodyPut {
|
||||
|
||||
@JsonProperty
|
||||
private MgmtActionType forceType;
|
||||
@JsonProperty(value="forceType")
|
||||
private MgmtActionType actionType;
|
||||
|
||||
public MgmtActionType getForceType() {
|
||||
return forceType;
|
||||
public MgmtActionType getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public void setForceType(final MgmtActionType forceType) {
|
||||
this.forceType = forceType;
|
||||
public void setActionType(final MgmtActionType actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,12 @@ public enum MgmtActionType {
|
||||
/**
|
||||
* The time forced action type.
|
||||
*/
|
||||
TIMEFORCED("timeforced");
|
||||
TIMEFORCED("timeforced"),
|
||||
|
||||
/**
|
||||
* The Download-Only action type.
|
||||
*/
|
||||
DOWNLOAD_ONLY("downloadonly");
|
||||
|
||||
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.Include;
|
||||
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
|
||||
private boolean deleted;
|
||||
|
||||
@JsonProperty
|
||||
private MgmtActionType type;
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
@@ -102,4 +106,12 @@ public class MgmtRolloutResponseBody extends MgmtNamedEntity {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public MgmtAssignedDistributionSetRequestBody setDistributionSetId(final Long distributionSetId) {
|
||||
public void setDistributionSetId(final Long distributionSetId) {
|
||||
this.distributionSetId = distributionSetId;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
* back.
|
||||
*
|
||||
*/
|
||||
final class MgmtRestModelMapper {
|
||||
public final class MgmtRestModelMapper {
|
||||
|
||||
// private constructor, utility class
|
||||
private MgmtRestModelMapper() {
|
||||
@@ -66,6 +66,8 @@ final class MgmtRestModelMapper {
|
||||
return ActionType.FORCED;
|
||||
case TIMEFORCED:
|
||||
return ActionType.TIMEFORCED;
|
||||
case DOWNLOAD_ONLY:
|
||||
return ActionType.DOWNLOAD_ONLY;
|
||||
default:
|
||||
throw new IllegalStateException("Action Type is not supported");
|
||||
}
|
||||
@@ -92,6 +94,8 @@ final class MgmtRestModelMapper {
|
||||
return MgmtActionType.FORCED;
|
||||
case TIMEFORCED:
|
||||
return MgmtActionType.TIMEFORCED;
|
||||
case DOWNLOAD_ONLY:
|
||||
return MgmtActionType.DOWNLOAD_ONLY;
|
||||
default:
|
||||
throw new IllegalStateException("Action Type is not supported");
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ final class MgmtRolloutMapper {
|
||||
body.setStatus(rollout.getStatus().toString().toLowerCase());
|
||||
body.setTotalTargets(rollout.getTotalTargets());
|
||||
body.setDeleted(rollout.isDeleted());
|
||||
body.setType(MgmtRestModelMapper.convertActionType(rollout.getActionType()));
|
||||
|
||||
if (withDetails) {
|
||||
for (final TotalTargetCountStatus.Status status : TotalTargetCountStatus.Status.values()) {
|
||||
|
||||
@@ -205,7 +205,7 @@ public final class MgmtTargetMapper {
|
||||
if (ActionType.TIMEFORCED.equals(action.getActionType())) {
|
||||
result.setForceTime(action.getForcedTime());
|
||||
}
|
||||
result.setForceType(MgmtRestModelMapper.convertActionType(action.getActionType()));
|
||||
result.setActionType(MgmtRestModelMapper.convertActionType(action.getActionType()));
|
||||
|
||||
if (action.isActive()) {
|
||||
result.setStatus(MgmtAction.ACTION_PENDING);
|
||||
|
||||
@@ -346,7 +346,7 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
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.");
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
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.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||
@@ -1245,4 +1246,30 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
|
||||
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.RolloutManagement;
|
||||
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.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
@@ -130,7 +131,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
||||
testdataFactory.createTargets(20, "target", "rollout");
|
||||
|
||||
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
|
||||
@@ -367,8 +368,8 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
||||
testdataFactory.createTargets(20, "target", "rollout");
|
||||
|
||||
// setup - create 2 rollouts
|
||||
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20);
|
||||
postRollout("rollout2", 5, dsA.getId(), "id==target-0001*", 10);
|
||||
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20, Action.ActionType.FORCED);
|
||||
postRollout("rollout2", 5, dsA.getId(), "id==target-0001*", 10, Action.ActionType.FORCED);
|
||||
|
||||
// Run here, because Scheduler is disabled during tests
|
||||
rolloutManagement.handleRollouts();
|
||||
@@ -408,8 +409,8 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
||||
testdataFactory.createTargets(20, "target", "rollout");
|
||||
|
||||
// setup - create 2 rollouts
|
||||
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20);
|
||||
postRollout("rollout2", 5, dsA.getId(), "id==target*", 20);
|
||||
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20, Action.ActionType.FORCED);
|
||||
postRollout("rollout2", 5, dsA.getId(), "id==target*", 20, Action.ActionType.FORCED);
|
||||
|
||||
// Run here, because Scheduler is disabled during tests
|
||||
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,
|
||||
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,
|
||||
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")
|
||||
.content(JsonBuilder.rollout(name, "desc", groupSize, distributionSetId, targetFilterQuery,
|
||||
new RolloutGroupConditionBuilder().withDefaults().build()))
|
||||
.content(rollout)
|
||||
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.name", equalTo(name))).andExpect(jsonPath("$.status", equalTo("creating")))
|
||||
.andExpect(jsonPath("$.type", equalTo(actionType)))
|
||||
.andExpect(jsonPath("$.targetFilterQuery", equalTo(targetFilterQuery)))
|
||||
.andExpect(jsonPath("$.description", equalTo("desc")))
|
||||
.andExpect(jsonPath("$.distributionSetId", equalTo(distributionSetId.intValue())))
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
|
||||
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.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
@@ -104,7 +105,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
public void updateTargetWhichDoesNotExistsLeadsToEntityNotFound() throws Exception {
|
||||
final String notExistingId = "4395";
|
||||
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + notExistingId).content("{}")
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(print())
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@@ -120,7 +121,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
final TargetFilterQuery tfq = createSingleTargetFilterQuery(filterName, filterQuery);
|
||||
|
||||
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_QUERY, equalTo(filterQuery2)))
|
||||
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(filterName)));
|
||||
@@ -143,7 +144,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
.create(entityFactory.targetFilterQuery().create().name(filterName).query(filterQuery));
|
||||
|
||||
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_QUERY, equalTo(filterQuery)))
|
||||
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(filterName2)));
|
||||
@@ -167,7 +168,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
createSingleTargetFilterQuery(idC, testQuery);
|
||||
|
||||
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_SIZE, equalTo(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)
|
||||
.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_SIZE, equalTo(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)
|
||||
.param(MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, String.valueOf(offsetParam))
|
||||
.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_SIZE, equalTo(expectedSize)))
|
||||
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(expectedSize)))
|
||||
@@ -253,7 +254,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
+ tfq.getId();
|
||||
// test
|
||||
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_QUERY, equalTo(knownQuery)))
|
||||
.andExpect(jsonPath("$._links.self.href", equalTo(hrefPrefix)))
|
||||
@@ -284,7 +285,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
final MvcResult mvcResult = mvc
|
||||
.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING).content(notJson)
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest()).andReturn();
|
||||
.andDo(print()).andExpect(status().isBadRequest()).andReturn();
|
||||
|
||||
assertThat(targetFilterQueryManagement.count()).isEqualTo(0);
|
||||
|
||||
@@ -311,7 +312,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
mvc.perform(
|
||||
post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId() + "/autoAssignDS")
|
||||
.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_ERROR_CODE, equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||
}
|
||||
@@ -333,7 +334,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
mvc.perform(
|
||||
post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId() + "/autoAssignDS")
|
||||
.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();
|
||||
|
||||
@@ -343,7 +344,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
// update the query of the filter query to trigger a quota hit
|
||||
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId())
|
||||
.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_ERROR_CODE, equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||
|
||||
@@ -366,6 +367,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
|
||||
verifyAutoAssignmentWithTimeForcedActionType(tfq, set);
|
||||
|
||||
verifyAutoAssignmentWithDownloadOnlyActionType(tfq, set);
|
||||
|
||||
verifyAutoAssignmentWithUnknownActionType(tfq, set);
|
||||
|
||||
verifyAutoAssignmentWithIncompleteDs(tfq);
|
||||
@@ -400,7 +403,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
? "{\"id\":" + set.getId() + ", \"type\":\"" + actionType.getName() + "\"}"
|
||||
: "{\"id\":" + set.getId() + "}";
|
||||
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
||||
.content(payload).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.content(payload).contentType(MediaType.APPLICATION_JSON)).andDo(print())
|
||||
.andExpect(status().isOk());
|
||||
|
||||
final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(tfq.getId()).get();
|
||||
@@ -411,7 +414,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
.isEqualTo(MgmtRestModelMapper.convertActionType(expectedActionType));
|
||||
|
||||
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_QUERY, equalTo(tfq.getQuery())))
|
||||
.andExpect(jsonPath(JSON_PATH_AUTO_ASSIGN_DS, equalTo(set.getId().intValue())))
|
||||
@@ -425,7 +428,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
throws Exception {
|
||||
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
||||
.content("{\"id\":" + set.getId() + ", \"type\":\"" + MgmtActionType.TIMEFORCED.getName() + "\"}")
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(print())
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
|
||||
equalTo(InvalidAutoAssignActionTypeException.class.getName())))
|
||||
@@ -433,12 +436,18 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
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
|
||||
private void verifyAutoAssignmentWithUnknownActionType(final TargetFilterQuery tfq, final DistributionSet set)
|
||||
throws Exception {
|
||||
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
||||
.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_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")
|
||||
.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,
|
||||
equalTo(InvalidAutoAssignDistributionSetException.class.getName())))
|
||||
.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")
|
||||
.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,
|
||||
equalTo(InvalidAutoAssignDistributionSetException.class.getName())))
|
||||
.andExpect(jsonPath(JSON_PATH_ERROR_CODE,
|
||||
|
||||
@@ -1287,6 +1287,26 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
|
||||
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
|
||||
@Description("Verfies that an offline DS to target assignment is reflected by the repository and that repeating "
|
||||
+ "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,
|
||||
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,
|
||||
final long distributionSetId, final String targetFilterQuery, final RolloutGroupConditions conditions,
|
||||
final List<RolloutGroup> groups) {
|
||||
final long distributionSetId, final String targetFilterQuery, final RolloutGroupConditions conditions,
|
||||
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();
|
||||
try {
|
||||
json.put("name", name);
|
||||
@@ -463,6 +469,10 @@ public abstract class JsonBuilder {
|
||||
json.put("distributionSetId", distributionSetId);
|
||||
json.put("targetFilterQuery", targetFilterQuery);
|
||||
|
||||
if(type != null){
|
||||
json.put("type", type);
|
||||
}
|
||||
|
||||
if (conditions != null) {
|
||||
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_PER_STATUS = "the total targets per status";
|
||||
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_AMOUNT_GROUPS = "the amount of groups the rollout should split targets into";
|
||||
public static final String ROLLOUT_GROUPS = "the list of group definitions";
|
||||
|
||||
@@ -391,7 +391,7 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
|
||||
requestFieldWithPath("[]maintenanceWindow.timezone")
|
||||
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_TIMEZONE).optional(),
|
||||
requestFieldWithPath("[]type").description(MgmtApiModelProperties.FORCETIME_TYPE)
|
||||
.attributes(key("value").value("['soft', 'forced','timeforced']"))),
|
||||
.attributes(key("value").value("['soft', 'forced','timeforced', 'downloadonly']"))),
|
||||
responseFields(
|
||||
fieldWithPath("assigned").description(MgmtApiModelProperties.DS_NEW_ASSIGNED_TARGETS),
|
||||
fieldWithPath("alreadyAssigned").type(JsonFieldType.NUMBER)
|
||||
|
||||
@@ -130,6 +130,8 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
|
||||
fieldWithPath(arrayPrefix + "distributionSetId").description(MgmtApiModelProperties.ROLLOUT_DS_ID));
|
||||
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "status").description(MgmtApiModelProperties.ROLLOUT_STATUS)
|
||||
.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(
|
||||
fieldWithPath(arrayPrefix + "totalTargets").description(MgmtApiModelProperties.ROLLOUT_TOTAL_TARGETS));
|
||||
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.self").ignored());
|
||||
|
||||
@@ -81,7 +81,7 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
|
||||
fieldWithPath("content[].autoAssignActionType")
|
||||
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE)
|
||||
.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[].createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||
fieldWithPath("content[].lastModifiedAt")
|
||||
@@ -199,7 +199,7 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
|
||||
requestFields(requestFieldWithPath("id").description(MgmtApiModelProperties.DS_ID),
|
||||
optionalRequestFieldWithPath("type")
|
||||
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE)
|
||||
.attributes(key("value").value("['forced', 'soft']"))),
|
||||
.attributes(key("value").value("['forced', 'soft', 'downloadonly']"))),
|
||||
getResponseFieldTargetFilterQuery(false)));
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
|
||||
.type(JsonFieldType.NUMBER.toString()),
|
||||
fieldWithPath(arrayPrefix + "autoAssignActionType")
|
||||
.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 + "createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
|
||||
fieldWithPath(arrayPrefix + "lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT),
|
||||
|
||||
@@ -519,7 +519,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
|
||||
requestFieldWithPath("maintenanceWindow.timezone")
|
||||
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_TIMEZONE).optional(),
|
||||
requestFieldWithPath("type").description(MgmtApiModelProperties.FORCETIME_TYPE)
|
||||
.attributes(key("value").value("['soft', 'forced','timeforced']"))),
|
||||
.attributes(key("value").value("['soft', 'forced','timeforced', 'downloadonly']"))),
|
||||
responseFields(
|
||||
fieldWithPath("assigned").description(MgmtApiModelProperties.DS_NEW_ASSIGNED_TARGETS),
|
||||
fieldWithPath("alreadyAssigned").type(JsonFieldType.NUMBER)
|
||||
|
||||
@@ -134,7 +134,7 @@ public abstract class AbstractGrid<T extends Indexed> extends Grid implements Re
|
||||
setColumnProperties();
|
||||
setColumnHeaderNames();
|
||||
setColumnsHidable();
|
||||
addColumnRenderes();
|
||||
addColumnRenderers();
|
||||
setColumnExpandRatio();
|
||||
|
||||
setHiddenColumns();
|
||||
@@ -327,7 +327,7 @@ public abstract class AbstractGrid<T extends Indexed> extends Grid implements Re
|
||||
* Template method invoked by {@link #addNewContainerDS()} for adding
|
||||
* special column renderers if needed.
|
||||
*/
|
||||
protected abstract void addColumnRenderes();
|
||||
protected abstract void addColumnRenderers();
|
||||
|
||||
/**
|
||||
* 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_ACTIVE = "statusIconActive";
|
||||
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_ACTION_CANCEL = "cancel-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,
|
||||
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_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 };
|
||||
|
||||
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_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 };
|
||||
|
||||
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,
|
||||
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 UINotification notification;
|
||||
@@ -107,19 +109,7 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
|
||||
private final BeanQueryFactory<ActionBeanQuery> targetQF = new BeanQueryFactory<>(ActionBeanQuery.class);
|
||||
|
||||
boolean forceClientRefreshToggle = true;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param i18n
|
||||
* @param deploymentManagement
|
||||
* @param eventBus
|
||||
* @param notification
|
||||
* @param managementUIState
|
||||
* @param permissionChecker
|
||||
*/
|
||||
protected ActionHistoryGrid(final VaadinMessageSource i18n, final DeploymentManagement deploymentManagement,
|
||||
ActionHistoryGrid(final VaadinMessageSource i18n, final DeploymentManagement deploymentManagement,
|
||||
final UIEventBus eventBus, final UINotification notification, final ManagementUIState managementUIState,
|
||||
final SpPermissionChecker permissionChecker) {
|
||||
super(i18n, eventBus, permissionChecker);
|
||||
@@ -220,14 +210,14 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addColumnRenderes() {
|
||||
protected void addColumnRenderers() {
|
||||
getColumn(ProxyAction.PXY_ACTION_LAST_MODIFIED_AT).setConverter(new LongToFormattedDateStringConverter());
|
||||
getColumn(ProxyAction.PXY_ACTION_STATUS).setRenderer(new HtmlLabelRenderer(),
|
||||
new HtmlStatusLabelConverter(this::createStatusLabelMetadata));
|
||||
getColumn(ProxyAction.PXY_ACTION_IS_ACTIVE_DECO).setRenderer(new HtmlLabelRenderer(),
|
||||
new HtmlIsActiveLabelConverter(this::createIsActiveLabelMetadata));
|
||||
getColumn(VIRT_PROP_FORCED).setRenderer(new HtmlLabelRenderer(),
|
||||
new HtmlVirtPropLabelConverter(this::createForcedLabelMetadata));
|
||||
getColumn(VIRT_PROP_TYPE).setRenderer(new HtmlLabelRenderer(),
|
||||
new HtmlVirtPropLabelConverter(this::createTypeLabelMetadata));
|
||||
getColumn(VIRT_PROP_TIMEFORCED).setRenderer(new HtmlLabelRenderer(),
|
||||
new HtmlVirtPropLabelConverter(this::createTimeForcedLabelMetadata));
|
||||
getColumn(VIRT_PROP_ACTION_CANCEL).setRenderer(
|
||||
@@ -270,14 +260,23 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
return activeStates.get(isActiveDeco);
|
||||
}
|
||||
|
||||
private StatusFontIcon createForcedLabelMetadata(final Action action) {
|
||||
StatusFontIcon result = null;
|
||||
private StatusFontIcon createTypeLabelMetadata(final Action action) {
|
||||
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),
|
||||
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) {
|
||||
@@ -408,9 +407,6 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
eventBus.publish(this, PinUnpinEvent.PIN_TARGET);
|
||||
}
|
||||
});
|
||||
if (!managementUIState.getDistributionTableFilters().getPinnedTarget().isPresent()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// service call to cancel the active action
|
||||
@@ -443,7 +439,7 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
|
||||
@Override
|
||||
protected void setHiddenColumns() {
|
||||
getColumn(VIRT_PROP_FORCED).setHidable(false);
|
||||
getColumn(VIRT_PROP_TYPE).setHidable(false);
|
||||
getColumn(VIRT_PROP_TIMEFORCED).setHidable(false);
|
||||
getColumn(VIRT_PROP_ACTION_CANCEL).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_MAINTENANCE_WINDOW)
|
||||
.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)
|
||||
.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(53.0, 55.0, ProxyAction.PXY_ACTION_STATUS);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -569,7 +563,7 @@ public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
protected GeneratedPropertyContainer addGeneratedContainerProperties() {
|
||||
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_ACTION_CANCEL, 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(100.0, 150.0, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT);
|
||||
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);
|
||||
setColumnsSize(FIXED_PIX_MIN, 500.0, ProxyAction.PXY_ACTION_ROLLOUT_NAME);
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ public class ActionStatusGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addColumnRenderes() {
|
||||
protected void addColumnRenderers() {
|
||||
getColumn(ProxyActionStatus.PXY_AS_STATUS).setRenderer(new HtmlLabelRenderer(),
|
||||
new HtmlStatusLabelConverter(this::createStatusLabelMetadata));
|
||||
getColumn(ProxyActionStatus.PXY_AS_CREATED_AT).setConverter(new LongToFormattedDateStringConverter());
|
||||
|
||||
@@ -146,7 +146,7 @@ public class ActionStatusMsgGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addColumnRenderes() {
|
||||
protected void addColumnRenderers() {
|
||||
// no specific column renderers
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
*/
|
||||
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.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -18,27 +22,21 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetTagManagement;
|
||||
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.DistributionSetAssignmentResult;
|
||||
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.TargetWithActionType;
|
||||
import org.eclipse.hawkbit.ui.SpPermissionChecker;
|
||||
import org.eclipse.hawkbit.ui.UiProperties;
|
||||
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.TargetIdName;
|
||||
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.PinUnpinEvent;
|
||||
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.MaintenanceWindowLayout;
|
||||
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.VaadinMessageSource;
|
||||
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.PageRequest;
|
||||
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.ui.Button;
|
||||
import com.vaadin.ui.Button.ClickEvent;
|
||||
import com.vaadin.ui.CheckBox;
|
||||
import com.vaadin.ui.DragAndDropWrapper;
|
||||
import com.vaadin.ui.HorizontalLayout;
|
||||
import com.vaadin.ui.Link;
|
||||
import com.vaadin.ui.Table;
|
||||
import com.vaadin.ui.UI;
|
||||
import com.vaadin.ui.themes.ValoTheme;
|
||||
|
||||
/**
|
||||
* 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 Logger LOG = LoggerFactory.getLogger(DistributionTable.class);
|
||||
|
||||
private final SpPermissionChecker permissionChecker;
|
||||
|
||||
private final ManagementUIState managementUIState;
|
||||
@@ -470,12 +458,19 @@ public class DistributionTable extends AbstractNamedVersionTable<DistributionSet
|
||||
confirmDialog = new ConfirmationDialog(getI18n().getMessage(CAPTION_ENTITY_ASSIGN_ACTION_CONFIRMBOX),
|
||||
confirmQuestion, getI18n().getMessage(UIMessageIdProvider.BUTTON_OK),
|
||||
getI18n().getMessage(UIMessageIdProvider.BUTTON_CANCEL), ok -> {
|
||||
if (ok && isMaintenanceWindowValid()) {
|
||||
saveAllAssignments();
|
||||
if (ok && isMaintenanceWindowValid(maintenanceWindowLayout, getNotification())) {
|
||||
saveAllAssignments(managementUIState, actionTypeOptionGroupLayout, maintenanceWindowLayout,
|
||||
deploymentManagement, getNotification(), getEventBus(), getI18n(), this);
|
||||
} else {
|
||||
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,
|
||||
@@ -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
|
||||
protected List<String> hasMissingPermissionsForDrop() {
|
||||
return permissionChecker.hasUpdateTargetPermission() ? Collections.emptyList()
|
||||
|
||||
@@ -78,9 +78,25 @@ public abstract class AbstractActionTypeOptionGroupLayout extends HorizontalLayo
|
||||
softLabel.setCaption(i18n.getMessage(UIMessageIdProvider.CAPTION_ACTION_SOFT));
|
||||
softLabel.setDescription(i18n.getMessage(UIMessageIdProvider.TOOLTIP_SOFT_ITEM));
|
||||
softLabel.setStyleName("padding-right-style");
|
||||
softLabel.setIcon(FontAwesome.STEP_FORWARD);
|
||||
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.
|
||||
*/
|
||||
@@ -93,7 +109,8 @@ public abstract class AbstractActionTypeOptionGroupLayout extends HorizontalLayo
|
||||
*
|
||||
*/
|
||||
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;
|
||||
|
||||
|
||||
@@ -70,13 +70,17 @@ public class ActionTypeOptionGroupAssignmentLayout extends AbstractActionTypeOpt
|
||||
actionTypeOptionGroup.addItem(ActionTypeOption.SOFT);
|
||||
actionTypeOptionGroup.addItem(ActionTypeOption.FORCED);
|
||||
actionTypeOptionGroup.addItem(ActionTypeOption.AUTO_FORCED);
|
||||
actionTypeOptionGroup.addItem(ActionTypeOption.DOWNLOAD_ONLY);
|
||||
selectDefaultOption();
|
||||
|
||||
addForcedItemWithLabel();
|
||||
addSoftItemWithLabel();
|
||||
addAutoForceItemWithLabelAndDateField();
|
||||
addDownloadOnlyItemWithLabel();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void addAutoForceItemWithLabelAndDateField() {
|
||||
final FlexibleOptionGroupItemComponent autoForceItem = actionTypeOptionGroup
|
||||
.getItemComponent(ActionTypeOption.AUTO_FORCED);
|
||||
|
||||
@@ -32,9 +32,11 @@ public class ActionTypeOptionGroupAutoAssignmentLayout extends AbstractActionTyp
|
||||
actionTypeOptionGroup = new FlexibleOptionGroup();
|
||||
actionTypeOptionGroup.addItem(ActionTypeOption.SOFT);
|
||||
actionTypeOptionGroup.addItem(ActionTypeOption.FORCED);
|
||||
actionTypeOptionGroup.addItem(ActionTypeOption.DOWNLOAD_ONLY);
|
||||
selectDefaultOption();
|
||||
|
||||
addForcedItemWithLabel();
|
||||
addSoftItemWithLabel();
|
||||
addDownloadOnlyItemWithLabel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
*/
|
||||
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.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -19,6 +23,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
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.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.FilterParams;
|
||||
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetTagManagement;
|
||||
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.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
|
||||
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.UiProperties;
|
||||
import org.eclipse.hawkbit.ui.common.ConfirmationDialog;
|
||||
import org.eclipse.hawkbit.ui.common.ManagementEntityState;
|
||||
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.TargetIdName;
|
||||
import org.eclipse.hawkbit.ui.common.table.AbstractTable;
|
||||
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.management.event.ManagementUIEvent;
|
||||
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.TargetTableEvent;
|
||||
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.MaintenanceWindowLayout;
|
||||
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.ui.Button;
|
||||
import com.vaadin.ui.Button.ClickEvent;
|
||||
import com.vaadin.ui.CheckBox;
|
||||
import com.vaadin.ui.DragAndDropWrapper;
|
||||
import com.vaadin.ui.HorizontalLayout;
|
||||
import com.vaadin.ui.Label;
|
||||
import com.vaadin.ui.Link;
|
||||
import com.vaadin.ui.Table;
|
||||
import com.vaadin.ui.UI;
|
||||
import com.vaadin.ui.themes.ValoTheme;
|
||||
@@ -379,8 +372,7 @@ public class TargetTable extends AbstractTable<Target> {
|
||||
if (
|
||||
|
||||
isFilteredByTags()) {
|
||||
final List<String> list = new ArrayList<>();
|
||||
list.addAll(managementUIState.getTargetTableFilters().getClickedTargetTags());
|
||||
final List<String> list = new ArrayList<>(managementUIState.getTargetTableFilters().getClickedTargetTags());
|
||||
queryConfig.put(SPUIDefinitions.FILTER_BY_TAG, list.toArray(new String[list.size()]));
|
||||
}
|
||||
if (isFilteredByStatus()) {
|
||||
@@ -524,7 +516,7 @@ public class TargetTable extends AbstractTable<Target> {
|
||||
}
|
||||
|
||||
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(),
|
||||
SPUIDefinitions.TARGET_TAG_ID_PREFIXS);
|
||||
@@ -858,95 +850,6 @@ public class TargetTable extends AbstractTable<Target> {
|
||||
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) {
|
||||
final TableTransferable transferable = (TableTransferable) event.getTransferable();
|
||||
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(UIMessageIdProvider.BUTTON_OK),
|
||||
getI18n().getMessage(UIMessageIdProvider.BUTTON_CANCEL), ok -> {
|
||||
if (ok && isMaintenanceWindowValid()) {
|
||||
saveAllAssignments();
|
||||
if (ok && isMaintenanceWindowValid(maintenanceWindowLayout, getNotification())) {
|
||||
saveAllAssignments(managementUIState, actionTypeOptionGroupLayout, maintenanceWindowLayout,
|
||||
deploymentManagement, getNotification(), getEventBus(), getI18n(), this);
|
||||
} else {
|
||||
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());
|
||||
confirmDialog.getWindow().bringToFront();
|
||||
}
|
||||
|
||||
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);
|
||||
private Consumer<Boolean> saveButtonToggle() {
|
||||
return isEnabled -> confirmDialog.getOkButton().setEnabled(isEnabled);
|
||||
}
|
||||
|
||||
private void addNewTargetToAssignmentList(final TargetIdName createTargetIdName,
|
||||
|
||||
@@ -36,10 +36,6 @@ public class TargetTableLayout extends AbstractTableLayout<TargetTable> {
|
||||
|
||||
private final transient EventBus.UIEventBus eventBus;
|
||||
|
||||
private final TargetDetails targetDetails;
|
||||
|
||||
private final TargetTableHeader targetTableHeader;
|
||||
|
||||
public TargetTableLayout(final UIEventBus eventBus, final TargetTable targetTable,
|
||||
final TargetManagement targetManagement, final EntityFactory entityFactory, final VaadinMessageSource i18n,
|
||||
final UINotification uiNotification, final ManagementUIState managementUIState,
|
||||
@@ -50,10 +46,10 @@ public class TargetTableLayout extends AbstractTableLayout<TargetTable> {
|
||||
final TargetMetadataPopupLayout targetMetadataPopupLayout = new TargetMetadataPopupLayout(i18n, uiNotification,
|
||||
eventBus, targetManagement, entityFactory, permissionChecker);
|
||||
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,
|
||||
targetTable);
|
||||
this.targetTableHeader = new TargetTableHeader(i18n, permissionChecker, eventBus, uiNotification,
|
||||
TargetTableHeader targetTableHeader = new TargetTableHeader(i18n, permissionChecker, eventBus, uiNotification,
|
||||
managementUIState, managementViewClientCriterion, targetManagement, deploymentManagement, uiProperties,
|
||||
entityFactory, uiNotification, tagManagement, distributionSetManagement, uiExecutor, targetTable);
|
||||
|
||||
|
||||
@@ -392,6 +392,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||
private CommonDialogWindow createWindow() {
|
||||
return new WindowBuilder(SPUIDefinitions.CREATE_UPDATE_WINDOW)
|
||||
.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())
|
||||
.saveDialogCloseListener(new SaveOnDialogCloseListener()).buildCommonDialogWindow();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
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.TotalTargetCountStatus;
|
||||
import org.eclipse.hawkbit.ui.customrenderers.client.renderers.RolloutRendererData;
|
||||
@@ -45,6 +46,15 @@ public class ProxyRollout {
|
||||
private TotalTargetCountStatus totalTargetCountStatus;
|
||||
private String approvalDecidedBy;
|
||||
private String approvalRemark;
|
||||
private ActionType actionType;
|
||||
|
||||
public ActionType getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public void setActionType(final ActionType actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
public RolloutRendererData getRolloutRendererData() {
|
||||
return rolloutRendererData;
|
||||
|
||||
@@ -118,6 +118,7 @@ public class RolloutBeanQuery extends AbstractBeanQuery<ProxyRollout> {
|
||||
proxyRollout.setForcedTime(rollout.getForcedTime());
|
||||
proxyRollout.setId(rollout.getId());
|
||||
proxyRollout.setStatus(rollout.getStatus());
|
||||
proxyRollout.setActionType(rollout.getActionType());
|
||||
proxyRollout.setRolloutRendererData(new RolloutRendererData(rollout.getName(), rollout.getStatus().toString()));
|
||||
|
||||
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.TargetManagement;
|
||||
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.RolloutStatus;
|
||||
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.customrenderers.client.renderers.RolloutRendererData;
|
||||
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.HtmlLabelRenderer;
|
||||
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_COPY = "copy";
|
||||
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;
|
||||
|
||||
@@ -100,6 +106,8 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
|
||||
private final RolloutUIState rolloutUIState;
|
||||
|
||||
private final AlignCellStyleGenerator alignGenerator;
|
||||
|
||||
private static final List<RolloutStatus> DELETE_COPY_BUTTON_ENABLED = Arrays.asList(RolloutStatus.CREATING,
|
||||
RolloutStatus.ERROR_CREATING, RolloutStatus.ERROR_STARTING, RolloutStatus.PAUSED, RolloutStatus.READY,
|
||||
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.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,
|
||||
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 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_MODIFIED_BY, SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY,
|
||||
SPUILabelDefinitions.VAR_APPROVAL_REMARK, SPUILabelDefinitions.VAR_DESC);
|
||||
|
||||
private static final String[] centerAlignedColumns = new String[] { PROP_TYPE };
|
||||
|
||||
static {
|
||||
statusIconMap.put(RolloutStatus.FINISHED,
|
||||
new StatusFontIcon(FontAwesome.CHECK_CIRCLE, SPUIStyleDefinitions.STATUS_ICON_GREEN));
|
||||
@@ -163,6 +173,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
rolloutGroupManagement, quotaManagement);
|
||||
this.uiNotification = uiNotification;
|
||||
this.rolloutUIState = rolloutUIState;
|
||||
alignGenerator = new AlignCellStyleGenerator(null, centerAlignedColumns, null);
|
||||
|
||||
setGeneratedPropertySupport(new RolloutGeneratedPropertySupport());
|
||||
init();
|
||||
@@ -183,7 +194,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
refreshContainer();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +242,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
private void updateItem(final Rollout rollout, final Item item) {
|
||||
final TotalTargetCountStatus totalTargetCountStatus = rollout.getTotalTargetCountStatus();
|
||||
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);
|
||||
final Long groupCount = Long
|
||||
.valueOf((Integer) item.getItemProperty(SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS).getValue());
|
||||
@@ -270,6 +282,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
false);
|
||||
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY, String.class, null,
|
||||
false, false);
|
||||
rolloutGridContainer.addContainerProperty(PROP_TYPE, ActionType.class, null, false, false);
|
||||
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_APPROVAL_REMARK, String.class, null, false,
|
||||
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).setMaximumWidth(60);
|
||||
|
||||
getColumn(PROP_TYPE).setMinimumWidth(45);
|
||||
getColumn(PROP_TYPE).setMaximumWidth(45);
|
||||
|
||||
getColumn(VIRT_PROP_RUN).setMinimumWidth(25);
|
||||
getColumn(VIRT_PROP_RUN).setMaximumWidth(25);
|
||||
|
||||
@@ -335,6 +351,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
.setHeaderCaption(i18n.getMessage("header.distributionset"));
|
||||
getColumn(SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS).setHeaderCaption(i18n.getMessage("header.numberofgroups"));
|
||||
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_USER).setHeaderCaption(i18n.getMessage("header.createdBy"));
|
||||
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,
|
||||
SPUILabelDefinitions.VAR_DIST_NAME_VERSION, SPUILabelDefinitions.VAR_STATUS,
|
||||
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,
|
||||
SPUILabelDefinitions.VAR_CREATED_USER, SPUILabelDefinitions.VAR_MODIFIED_DATE,
|
||||
SPUILabelDefinitions.VAR_MODIFIED_BY, SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY,
|
||||
SPUILabelDefinitions.VAR_APPROVAL_REMARK, SPUILabelDefinitions.VAR_DESC);
|
||||
|
||||
setColumns(columnsToShowInOrder.toArray());
|
||||
setCellStyleGenerator(alignGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -403,7 +421,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addColumnRenderes() {
|
||||
protected void addColumnRenderers() {
|
||||
getColumn(SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS).setRenderer(new HtmlRenderer(),
|
||||
new TotalTargetGroupsConverter());
|
||||
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(PROP_TYPE).setRenderer(new HtmlLabelRenderer(),
|
||||
new RolloutTypeConverter(this::createTypeLabelAdapter));
|
||||
|
||||
final RolloutRenderer customObjectRenderer = new RolloutRenderer(RolloutRendererData.class);
|
||||
customObjectRenderer.addClickListener(this::onClickOfRolloutName);
|
||||
getColumn(ROLLOUT_RENDERER_DATA).setRenderer(customObjectRenderer);
|
||||
@@ -465,7 +486,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
|
||||
@Override
|
||||
public LazyQueryContainer getRawContainer() {
|
||||
return (LazyQueryContainer) (getDecoratedContainer()).getWrappedContainer();
|
||||
return (LazyQueryContainer) getDecoratedContainer().getWrappedContainer();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -550,7 +571,6 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
if (RolloutStatus.PAUSED.equals(rolloutStatus)) {
|
||||
rolloutManagement.resumeRollout(rolloutId);
|
||||
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);
|
||||
String rolloutDetailsMessage = "";
|
||||
if ((scheduledActions > 0) || (runningActions > 0)) {
|
||||
if (scheduledActions > 0 || runningActions > 0) {
|
||||
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
|
||||
* 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)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
@@ -233,7 +233,7 @@ public class RolloutGroupListGrid extends AbstractGrid<LazyQueryContainer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addColumnRenderes() {
|
||||
protected void addColumnRenderers() {
|
||||
getColumn(SPUILabelDefinitions.VAR_STATUS).setRenderer(new HtmlLabelRenderer(),
|
||||
new RolloutGroupStatusConverter());
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.util.EnumMap;
|
||||
import java.util.Locale;
|
||||
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.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
@@ -173,7 +174,7 @@ public class RolloutGroupTargetsListGrid extends AbstractGrid<LazyQueryContainer
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addColumnRenderes() {
|
||||
protected void addColumnRenderers() {
|
||||
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) {
|
||||
final StatusFontIcon statusFontIcon = statusIconMap.get(status);
|
||||
final StatusFontIcon statusFontIcon = mapActionStatusToPresentation(status);
|
||||
final String codePoint = HawkbitCommonUtil.getCodePoint(statusFontIcon);
|
||||
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() {
|
||||
final RolloutGroup rolloutGroup = rolloutUIState.getRolloutGroup().orElse(null);
|
||||
if (rolloutGroup != null && rolloutGroup.getStatus() == RolloutGroupStatus.READY) {
|
||||
|
||||
@@ -177,6 +177,10 @@ public final class UIComponentIdProvider {
|
||||
* Action history table cancel Id.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -256,7 +260,7 @@ public final class UIComponentIdProvider {
|
||||
/**
|
||||
* 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.
|
||||
@@ -1145,6 +1149,11 @@ public final class UIComponentIdProvider {
|
||||
*/
|
||||
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 .
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,8 @@ public final class UIMessageIdProvider {
|
||||
|
||||
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_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_DOWNLOAD_ONLY_ITEM = "tooltip.downloadonly.item";
|
||||
|
||||
public static final String TOOLTIP_FORCED_ITEM = "tooltip.forced.item";
|
||||
|
||||
public static final String TOOLTIP_TARGET_PIN = "tooltip.target.pin";
|
||||
|
||||
@@ -165,7 +165,9 @@ label.active = Active
|
||||
label.action.id = Action Id
|
||||
label.no.tag = NO TAG
|
||||
label.action.forced = Forced
|
||||
label.action.type = Type
|
||||
label.action.soft = Soft
|
||||
label.action.downloadonly = Download Only
|
||||
label.action.time.forced = Time Forced
|
||||
label.dist.details.type = Type :
|
||||
label.dist.details.name = Name :
|
||||
@@ -218,6 +220,7 @@ label.cancelled = Cancelled
|
||||
label.cancelling = Canceling
|
||||
label.retrieved = Retrieved
|
||||
label.download = Downloading
|
||||
label.downloaded = Downloaded
|
||||
label.unknown = Unknown
|
||||
label.target.id = Controller Id :
|
||||
label.target.ip = Controller IP :
|
||||
@@ -283,6 +286,7 @@ tooltip.status.overdue = Overdue
|
||||
tooltip.delete.module = Select and delete Software Module
|
||||
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.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.forced.in=Auto forcing in {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.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.downloadonly.action = DownloadOnly
|
||||
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.did.not.work = Action did not work. Please try again.
|
||||
@@ -570,6 +575,7 @@ header.name = Name
|
||||
header.vendor = Vendor
|
||||
header.version = Version
|
||||
header.description = Description
|
||||
header.type = Type
|
||||
header.createdBy = Created By
|
||||
header.createdDate = Created Date
|
||||
header.modifiedBy = Modified By
|
||||
|
||||
Reference in New Issue
Block a user