Fix cancel action status (#429)

* Fixed handling of action cancellation status updates.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Fix broken DDI test.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Code readibility.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Reduce access to default.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Added test for finished on action that is in cancelling.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Update MariaDb driver

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Wrong branch

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Remove duplicate status definition.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Use updated picture in docs.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Leverage existing statics.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Fix picture upload.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Revert change

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2017-02-07 09:57:57 +01:00
committed by GitHub
parent 21181bc6f8
commit 1ad9b915c4
19 changed files with 513 additions and 273 deletions

1
.gitattributes vendored
View File

@@ -2,6 +2,7 @@
# Denote all files that are truly binary and should not be modified. # Denote all files that are truly binary and should not be modified.
*.png binary *.png binary
*.jpg binary *.jpg binary
*.jpeg binary
*.pdf binary *.pdf binary
*.PNG binary *.PNG binary
*.pptx binary *.pptx binary

View File

@@ -4,11 +4,11 @@ title : "Interfaces"
series : "homepagetop" series : "homepagetop"
sequence: "2" sequence: "2"
order : 2 order : 2
imagename : "hawkBit_overview.png" imagename : "hawkBit_overview.jpeg"
style: "thumb1" style: "thumb1"
animatestyle: "fadeIn" animatestyle: "fadeIn"
fadestyle: "fadeIn" fadestyle: "fadeIn"
fadeduration: "4" fadeduration: "4"
--- ---
hawkBit offers a <b>direct device integration</b> via HTTP or an <b>device management federation API</b> which allows you to connect devices with different protocol adapter. Users can make use of the <b>graphical user interface</b> and other service can interact with hawkBit through the <b>RESTful management API</b>. hawkBit offers a <b>direct device integration</b> via HTTP or an <b>device management federation API</b> which allows you to connect devices with different protocol adapter. Users can make use of the <b>graphical user interface</b> and other service can interact with hawkBit through the <b>RESTful management API</b>.

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -439,7 +439,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
case REJECTED: case REJECTED:
LOG.info("Target rejected the cancelation request (actionid: {}, controllerId: {}).", actionid, LOG.info("Target rejected the cancelation request (actionid: {}, controllerId: {}).", actionid,
target.getControllerId()); target.getControllerId());
status = Status.WARNING; status = Status.CANCEL_REJECTED;
messages.add(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target rejected the cancelation request."); messages.add(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target rejected the cancelation request.");
break; break;
case CLOSED: case CLOSED:

View File

@@ -276,7 +276,7 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(5); assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(5);
assertThat(deploymentManagement.findActiveActionsByTarget(savedTarget.getControllerId())).hasSize(1); assertThat(deploymentManagement.findActiveActionsByTarget(savedTarget.getControllerId())).hasSize(1);
// cancelation canceled -> should remove the action from active // cancellation canceled -> should remove the action from active
assertThat(deploymentManagement.findActiveActionsByTarget(savedTarget.getControllerId())).hasSize(1); assertThat(deploymentManagement.findActiveActionsByTarget(savedTarget.getControllerId())).hasSize(1);
current = System.currentTimeMillis(); current = System.currentTimeMillis();
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/" mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
@@ -289,7 +289,8 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(6); assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(6);
assertThat(deploymentManagement.findActiveActionsByTarget(savedTarget.getControllerId())).hasSize(1); assertThat(deploymentManagement.findActiveActionsByTarget(savedTarget.getControllerId())).hasSize(1);
// cancelation rejected -> action still active until controller close it // cancellation rejected -> action still active until controller close
// it
// with finished or // with finished or
// error // error
assertThat(deploymentManagement.findActiveActionsByTarget(savedTarget.getControllerId())).hasSize(1); assertThat(deploymentManagement.findActiveActionsByTarget(savedTarget.getControllerId())).hasSize(1);
@@ -304,9 +305,9 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(7); assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(7);
assertThat(deploymentManagement.findActiveActionsByTarget(savedTarget.getControllerId())).hasSize(1); assertThat(deploymentManagement.findActiveActionsByTarget(savedTarget.getControllerId())).hasSize(1);
// cancelaction closed -> should remove the action from active // update closed -> should remove the action from active
current = System.currentTimeMillis(); current = System.currentTimeMillis();
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/" mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()) + cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed")) .content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)) .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))

View File

@@ -294,7 +294,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
private Status hanldeCancelRejectedState(final Message message, final Action action) { private Status hanldeCancelRejectedState(final Message message, final Action action) {
if (action.isCancelingOrCanceled()) { if (action.isCancelingOrCanceled()) {
return Status.WARNING; return Status.CANCEL_REJECTED;
} else { } else {
logAndThrowMessageError(message, logAndThrowMessageError(message,
"Cancel recjected message is not allowed, if action is on state: " + action.getStatus()); "Cancel recjected message is not allowed, if action is on state: " + action.getStatus());

View File

@@ -23,40 +23,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtActionStatus { public class MgmtActionStatus {
/**
* Action is finished successfully for this target.
*/
public static final String AS_FINISHED = "finished";
/**
* Action has failed for this target.
*/
public static final String AS_ERROR = "error";
/**
* Action is still running but with warnings.
*/
public static final String AS_WARNING = "warning";
/**
* Action is still running for this target.
*/
public static final String AS_RUNNING = "running";
/**
* Action has been canceled for this target.
*/
public static final String AS_CANCELED = "canceled";
/**
* Action has been presented to the target.
*/
public static final String AS_RETRIEVED = "retrieved";
/**
* Action has been canceled for this target.
*/
public static final String AS_CANCELING = "canceling";
@JsonProperty("id") @JsonProperty("id")
private Long statusId; private Long statusId;

View File

@@ -34,7 +34,6 @@ import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.PollStatus; import org.eclipse.hawkbit.repository.model.PollStatus;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.rest.data.ResponseList; import org.eclipse.hawkbit.rest.data.ResponseList;
import org.eclipse.hawkbit.rest.data.SortDirection; import org.eclipse.hawkbit.rest.data.SortDirection;
import org.eclipse.hawkbit.util.IpUtil; import org.eclipse.hawkbit.util.IpUtil;
@@ -112,7 +111,7 @@ public final class MgmtTargetMapper {
targetRest.setControllerId(target.getControllerId()); targetRest.setControllerId(target.getControllerId());
targetRest.setDescription(target.getDescription()); targetRest.setDescription(target.getDescription());
targetRest.setName(target.getName()); targetRest.setName(target.getName());
targetRest.setUpdateStatus(getUpdateStatusName(target.getTargetInfo().getUpdateStatus())); targetRest.setUpdateStatus(target.getTargetInfo().getUpdateStatus().name().toLowerCase());
final URI address = target.getTargetInfo().getAddress(); final URI address = target.getTargetInfo().getAddress();
if (address != null) { if (address != null) {
@@ -198,37 +197,6 @@ public final class MgmtTargetMapper {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private static String getNameOfActionStatusType(final Action.Status type) {
String result;
switch (type) {
case CANCELED:
result = MgmtActionStatus.AS_CANCELED;
break;
case ERROR:
result = MgmtActionStatus.AS_ERROR;
break;
case FINISHED:
result = MgmtActionStatus.AS_FINISHED;
break;
case RETRIEVED:
result = MgmtActionStatus.AS_RETRIEVED;
break;
case RUNNING:
result = MgmtActionStatus.AS_RUNNING;
break;
case WARNING:
result = MgmtActionStatus.AS_WARNING;
break;
default:
return type.name().toLowerCase();
}
return result;
}
private static String getType(final Action action) { private static String getType(final Action action) {
if (!action.isCancelingOrCanceled()) { if (!action.isCancelingOrCanceled()) {
return MgmtAction.ACTION_UPDATE; return MgmtAction.ACTION_UPDATE;
@@ -239,39 +207,13 @@ public final class MgmtTargetMapper {
return null; return null;
} }
private static String getUpdateStatusName(final TargetUpdateStatus updatestatus) {
String result;
switch (updatestatus) {
case ERROR:
result = "error";
break;
case IN_SYNC:
result = "in_sync";
break;
case PENDING:
result = "pending";
break;
case REGISTERED:
result = "registered";
break;
case UNKNOWN:
result = "unknown";
break;
default:
return updatestatus.name().toLowerCase();
}
return result;
}
private static MgmtActionStatus toResponse(final ActionStatus actionStatus) { private static MgmtActionStatus toResponse(final ActionStatus actionStatus) {
final MgmtActionStatus result = new MgmtActionStatus(); final MgmtActionStatus result = new MgmtActionStatus();
result.setMessages(actionStatus.getMessages()); result.setMessages(actionStatus.getMessages());
result.setReportedAt(actionStatus.getCreatedAt()); result.setReportedAt(actionStatus.getCreatedAt());
result.setStatusId(actionStatus.getId()); result.setStatusId(actionStatus.getId());
result.setType(getNameOfActionStatusType(actionStatus.getStatus())); result.setType(actionStatus.getStatus().name().toLowerCase());
return result; return result;
} }

View File

@@ -12,11 +12,10 @@ import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.exception.SpServerError;
/** /**
* Thrown if cancelation of actions is performened where the action is not * Thrown if cancellation of action is requested where the action cannot be
* cancelable, e.g. the action is not active or is already a cancel action. * cancelled (e.g. the action is not active or is already a canceled action) or
* * controller provides cancellation feedback on an action that is actually not
* * in caneling state.
*
* *
*/ */
public final class CancelActionNotAllowedException extends AbstractServerRtException { public final class CancelActionNotAllowedException extends AbstractServerRtException {

View File

@@ -172,7 +172,12 @@ public interface Action extends TenantAwareBaseEntity {
* Action is in waiting state, e.g. the action is scheduled in a rollout * Action is in waiting state, e.g. the action is scheduled in a rollout
* but not yet activated. * but not yet activated.
*/ */
SCHEDULED; SCHEDULED,
/**
* Cancellation has been rejected by the controller.
*/
CANCEL_REJECTED;
} }
/** /**

View File

@@ -28,6 +28,7 @@ import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent; import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent; import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException; import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException;
import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException; import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
@@ -101,9 +102,6 @@ public class JpaControllerManagement implements ControllerManagement {
@Autowired @Autowired
private RepositoryProperties repositoryProperties; private RepositoryProperties repositoryProperties;
@Autowired
private TenantConfigurationRepository tenantConfigurationRepository;
@Autowired @Autowired
private TenantConfigurationManagement tenantConfigurationManagement; private TenantConfigurationManagement tenantConfigurationManagement;
@@ -242,26 +240,29 @@ public class JpaControllerManagement implements ControllerManagement {
final JpaActionStatusCreate create = (JpaActionStatusCreate) c; final JpaActionStatusCreate create = (JpaActionStatusCreate) c;
final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId()); final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());
if (!action.isCancelingOrCanceled()) {
throw new CancelActionNotAllowedException("The action is not in canceling state.");
}
final JpaActionStatus actionStatus = create.build(); final JpaActionStatus actionStatus = create.build();
checkForToManyStatusEntries(action);
action.setStatus(actionStatus.getStatus());
switch (actionStatus.getStatus()) { switch (actionStatus.getStatus()) {
case WARNING:
case ERROR:
case RUNNING:
break;
case CANCELED: case CANCELED:
case FINISHED: case FINISHED:
handleFinishedCancelation(actionStatus, action); handleFinishedCancelation(actionStatus, action);
break; break;
case RETRIEVED: case ERROR:
actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Cancellation request retrieved."); case CANCEL_REJECTED:
// Cancellation rejected. Back to running.
action.setStatus(Status.RUNNING);
break; break;
default: default:
// do nothing // information status entry - check for a potential DOS attack
checkForToManyStatusEntries(action);
break;
} }
actionStatus.setAction(actionRepository.save(action)); actionStatus.setAction(actionRepository.save(action));
actionStatusRepository.save(actionStatus); actionStatusRepository.save(actionStatus);
@@ -306,17 +307,11 @@ public class JpaControllerManagement implements ControllerManagement {
/** /**
* Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}. * Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}.
*
* @param actionStatus
* @param action
* @return
*/ */
private Action handleAddUpdateActionStatus(final JpaActionStatus actionStatus, final JpaAction action) { private Action handleAddUpdateActionStatus(final JpaActionStatus actionStatus, final JpaAction action) {
LOG.debug("addUpdateActionStatus for action {}", action.getId()); LOG.debug("addUpdateActionStatus for action {}", action.getId());
JpaTarget target = (JpaTarget) action.getTarget(); JpaTarget target = (JpaTarget) action.getTarget();
// check for a potential DOS attack
checkForToManyStatusEntries(action);
switch (actionStatus.getStatus()) { switch (actionStatus.getStatus()) {
case ERROR: case ERROR:
@@ -327,10 +322,9 @@ public class JpaControllerManagement implements ControllerManagement {
case FINISHED: case FINISHED:
handleFinishedAndStoreInTargetStatus(target, action); handleFinishedAndStoreInTargetStatus(target, action);
break; break;
case CANCELED:
case WARNING:
case RUNNING:
default: default:
// information status entry - check for a potential DOS attack
checkForToManyStatusEntries(action);
break; break;
} }
@@ -339,8 +333,6 @@ public class JpaControllerManagement implements ControllerManagement {
LOG.debug("addUpdateActionStatus {} for target {} is finished.", action, target.getId()); LOG.debug("addUpdateActionStatus {} for target {} is finished.", action, target.getId());
action.setStatus(actionStatus.getStatus());
return actionRepository.save(action); return actionRepository.save(action);
} }

View File

@@ -12,6 +12,8 @@ import static org.fest.assertions.api.Assertions.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.util.List;
import java.util.Map;
import javax.validation.ConstraintViolationException; import javax.validation.ConstraintViolationException;
@@ -20,13 +22,15 @@ import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
@@ -37,8 +41,11 @@ import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import com.google.common.collect.Maps;
import ru.yandex.qatools.allure.annotations.Description; import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features; import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Step;
import ru.yandex.qatools.allure.annotations.Stories; import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Repository") @Features("Component Tests - Repository")
@@ -49,40 +56,256 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
private RepositoryProperties repositoryProperties; private RepositoryProperties repositoryProperties;
@Test @Test
@Description("Controller adds a new action status.") @Description("Controller confirms successfull update with FINISHED status.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerAddsActionStatus() { public void controllerConfirmsUpdateWithFinished() {
final DistributionSet ds = testdataFactory.createDistributionSet(""); final Long actionId = createTargetAndAssignDs();
Target savedTarget = testdataFactory.createTarget();
assertThat(savedTarget.getTargetInfo().getUpdateStatus()).isEqualTo(TargetUpdateStatus.UNKNOWN); simulateIntermediateStatusOnUpdate(actionId);
savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getAssignedEntity().iterator() controllerManagament
.next(); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
final JpaAction savedAction = (JpaAction) deploymentManagement assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
.findActiveActionsByTarget(savedTarget.getControllerId()).get(0); Action.Status.FINISHED, Action.Status.FINISHED, false);
assertThat(targetManagement.findTargetByControllerID(savedTarget.getControllerId()).getTargetInfo() assertThat(actionStatusRepository.count()).isEqualTo(6);
.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING); assertThat(deploymentManagement.findActionStatusByAction(pageReq, actionId).getNumberOfElements()).isEqualTo(6);
}
controllerManagament.addUpdateActionStatus( @Test
entityFactory.actionStatus().create(savedAction.getId()).status(Action.Status.RUNNING)); @Description("Controller confirms successfull update with FINISHED status on a action that is on canceling. "
+ "Reason: The decission to ignore the cancellation is in fact up to the controller.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerConfirmsUpdateWithFinishedAndIgnorsCancellationWithThat() {
final Long actionId = createTargetAndAssignDs();
deploymentManagement.cancelAction(actionId);
controllerManagament
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
Action.Status.FINISHED, Action.Status.FINISHED, false);
assertThat(actionStatusRepository.count()).isEqualTo(3);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, actionId).getNumberOfElements()).isEqualTo(3);
}
@Test
@Description("Update server rejects cancelation feedback if action is not in CANCELING state.")
@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 cancellationFeedbackRejectedIfActionIsNotInCanceling() {
final Long actionId = createTargetAndAssignDs();
try {
controllerManagament.addCancelActionStatus(
entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
fail("Expected " + CancelActionNotAllowedException.class.getName());
} catch (final CancelActionNotAllowedException e) {
// expected
}
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.RUNNING, true);
assertThat(actionStatusRepository.count()).isEqualTo(1);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, actionId).getNumberOfElements()).isEqualTo(1);
}
@Test
@Description("Controller confirms action cancelation with FINISHED 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 = 2),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerConfirmsActionCancelationWithFinished() {
final Long actionId = createTargetAndAssignDs();
deploymentManagement.cancelAction(actionId);
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.CANCELING, Action.Status.CANCELING, true);
simulateIntermediateStatusOnCancellation(actionId);
controllerManagament
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
Action.Status.CANCELED, Action.Status.FINISHED, false);
assertThat(actionStatusRepository.count()).isEqualTo(7);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, actionId).getNumberOfElements()).isEqualTo(7);
}
@Test
@Description("Controller confirms action cancelation with FINISHED 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 = 2),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerConfirmsActionCancelationWithCanceled() {
final Long actionId = createTargetAndAssignDs();
deploymentManagement.cancelAction(actionId);
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.CANCELING, Action.Status.CANCELING, true);
simulateIntermediateStatusOnCancellation(actionId);
controllerManagament
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.CANCELED));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
Action.Status.CANCELED, Action.Status.CANCELED, false);
assertThat(actionStatusRepository.count()).isEqualTo(7);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, actionId).getNumberOfElements()).isEqualTo(7);
}
@Test
@Description("Controller rejects action cancelation with CANCEL_REJECTED status. Action goes back to RUNNING status as it expects "
+ "that the controller will continue the original update.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 1),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerRejectsActionCancelationWithReject() {
final Long actionId = createTargetAndAssignDs();
deploymentManagement.cancelAction(actionId);
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.CANCELING, Action.Status.CANCELING, true);
simulateIntermediateStatusOnCancellation(actionId);
controllerManagament.addCancelActionStatus(
entityFactory.actionStatus().create(actionId).status(Action.Status.CANCEL_REJECTED));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.CANCEL_REJECTED, true);
assertThat(actionStatusRepository.count()).isEqualTo(7);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, actionId).getNumberOfElements()).isEqualTo(7);
}
@Test
@Description("Controller rejects action cancelation with ERROR status. Action goes back to RUNNING status as it expects "
+ "that the controller will continue the original update.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 1),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void controllerRejectsActionCancelationWithError() {
final Long actionId = createTargetAndAssignDs();
deploymentManagement.cancelAction(actionId);
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.CANCELING, Action.Status.CANCELING, true);
simulateIntermediateStatusOnCancellation(actionId);
controllerManagament
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.ERROR, true);
assertThat(actionStatusRepository.count()).isEqualTo(7);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, actionId).getNumberOfElements()).isEqualTo(7);
}
@Step
private Long createTargetAndAssignDs() {
final Long dsId = testdataFactory.createDistributionSet().getId();
testdataFactory.createTarget();
assignDistributionSet(dsId, TestdataFactory.DEFAULT_CONTROLLER_ID);
assertThat(targetManagement.findTargetByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).getTargetInfo() assertThat(targetManagement.findTargetByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).getTargetInfo()
.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING); .getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
controllerManagament.addUpdateActionStatus( return deploymentManagement.findActiveActionsByTarget(TestdataFactory.DEFAULT_CONTROLLER_ID).get(0).getId();
entityFactory.actionStatus().create(savedAction.getId()).status(Action.Status.FINISHED)); }
assertThat(targetManagement.findTargetByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).getTargetInfo()
.getUpdateStatus()).isEqualTo(TargetUpdateStatus.IN_SYNC); @Step
private void simulateIntermediateStatusOnCancellation(final Long actionId) {
controllerManagament
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.CANCELING, Action.Status.RUNNING, true);
controllerManagament
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOAD));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.CANCELING, Action.Status.DOWNLOAD, true);
controllerManagament
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RETRIEVED));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.CANCELING, Action.Status.RETRIEVED, true);
controllerManagament
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.CANCELING, Action.Status.WARNING, true);
}
@Step
private void simulateIntermediateStatusOnUpdate(final Long actionId) {
controllerManagament
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.RUNNING, true);
controllerManagament
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOAD));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.DOWNLOAD, true);
controllerManagament
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RETRIEVED));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.RETRIEVED, true);
controllerManagament
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.WARNING, true);
}
private void assertActionStatus(final Long actionId, final String controllerId,
final TargetUpdateStatus expectedTargetUpdateStatus, final Action.Status expectedActionActionStatus,
final Action.Status expectedActionStatus, final boolean actionActive) {
final TargetUpdateStatus targetStatus = targetManagement.findTargetByControllerID(controllerId).getTargetInfo()
.getUpdateStatus();
assertThat(targetStatus).isEqualTo(expectedTargetUpdateStatus);
final Action action = deploymentManagement.findAction(actionId);
assertThat(action.getStatus()).isEqualTo(expectedActionActionStatus);
assertThat(action.isActive()).isEqualTo(actionActive);
final List<ActionStatus> actionStatusList = deploymentManagement.findActionStatusByAction(pageReq, actionId)
.getContent();
assertThat(actionStatusList.get(actionStatusList.size() - 1).getStatus()).isEqualTo(expectedActionStatus);
assertThat(actionStatusRepository.findAll(pageReq).getNumberOfElements()).isEqualTo(3);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, savedAction.getId()).getNumberOfElements())
.isEqualTo(3);
} }
@Test @Test
@@ -123,7 +346,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Register a controller which does not exist") @Description("Register a controller which does not exist")
public void testfindOrRegisterTargetIfItDoesNotexist() { public void findOrRegisterTargetIfItDoesNotexist() {
final Target target = controllerManagament.findOrRegisterTargetIfItDoesNotexist("AA", null); final Target target = controllerManagament.findOrRegisterTargetIfItDoesNotexist("AA", null);
assertThat(target).as("target should not be null").isNotNull(); assertThat(target).as("target should not be null").isNotNull();
@@ -142,47 +365,94 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Controller trys to finish an update process after it has been finished by an error action status.") @Description("Controller trys to finish an update process after it has been finished by an error action status.")
public void tryToFinishUpdateProcessMoreThanOnce() { @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
final DistributionSet ds = testdataFactory.createDistributionSet(""); @Expect(type = DistributionSetCreatedEvent.class, count = 1),
Target savedTarget = testdataFactory.createTarget(); @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getAssignedEntity().iterator() @Expect(type = TargetUpdatedEvent.class, count = 2),
.next(); @Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
Action savedAction = deploymentManagement.findActiveActionsByTarget(savedTarget.getControllerId()).get(0); @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void tryToFinishWithErrorUpdateProcessMoreThanOnce() {
final Long actionId = createTargetAndAssignDs();
// test and verify // test and verify
savedAction = controllerManagament.addUpdateActionStatus( controllerManagament
entityFactory.actionStatus().create(savedAction.getId()).status(Action.Status.RUNNING)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
assertThat(targetManagement.findTargetByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).getTargetInfo() assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING); Action.Status.RUNNING, Action.Status.RUNNING, true);
savedAction = controllerManagament.addUpdateActionStatus( controllerManagament
entityFactory.actionStatus().create(savedAction.getId()).status(Action.Status.ERROR)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
assertThat(targetManagement.findTargetByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).getTargetInfo() assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
.getUpdateStatus()).isEqualTo(TargetUpdateStatus.ERROR); Action.Status.ERROR, Action.Status.ERROR, false);
// try with disabled late feedback // try with disabled late feedback
repositoryProperties.setRejectActionStatusForClosedAction(true); repositoryProperties.setRejectActionStatusForClosedAction(true);
savedAction = controllerManagament.addUpdateActionStatus( controllerManagament
entityFactory.actionStatus().create(savedAction.getId()).status(Action.Status.FINISHED)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
// test // test
assertThat(targetManagement.findTargetByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).getTargetInfo() assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
.getUpdateStatus()).isEqualTo(TargetUpdateStatus.ERROR); Action.Status.ERROR, Action.Status.ERROR, false);
// try with enabled late feedback // try with enabled late feedback - should not make a difference as it
// only allows intermediate feedbacks and not multiple close
repositoryProperties.setRejectActionStatusForClosedAction(false); repositoryProperties.setRejectActionStatusForClosedAction(false);
controllerManagament.addUpdateActionStatus( controllerManagament
entityFactory.actionStatus().create(savedAction.getId()).status(Action.Status.FINISHED)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
// test // test
assertThat(targetManagement.findTargetByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).getTargetInfo() assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
.getUpdateStatus()).isEqualTo(TargetUpdateStatus.ERROR); Action.Status.ERROR, Action.Status.ERROR, false);
assertThat(actionStatusRepository.count()).isEqualTo(3);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, actionId).getNumberOfElements()).isEqualTo(3);
}
@Test
@Description("Controller trys to finish an update process after it has been finished by an FINISHED 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),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void tryToFinishUpdateProcessMoreThanOnce() {
final Long actionId = prepareFinishedUpdate().getId();
// try with disabled late feedback
repositoryProperties.setRejectActionStatusForClosedAction(true);
controllerManagament
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
// test
assertActionStatus(actionId, TestdataFactory.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
// only allows intermediate feedbacks and not multiple close
repositoryProperties.setRejectActionStatusForClosedAction(false);
controllerManagament
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
// test
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
Action.Status.FINISHED, Action.Status.FINISHED, false);
assertThat(actionStatusRepository.count()).isEqualTo(3);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, actionId).getNumberOfElements()).isEqualTo(3);
} }
@Test @Test
@Description("Controller trys to send an update feedback after it has been finished which is reject as the repository is " @Description("Controller trys to send an update feedback after it has been finished which is reject as the repository is "
+ "configured to reject that.") + "configured to reject that.")
@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),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void sendUpdatesForFinishUpdateProcessDropedIfDisabled() { public void sendUpdatesForFinishUpdateProcessDropedIfDisabled() {
repositoryProperties.setRejectActionStatusForClosedAction(true); repositoryProperties.setRejectActionStatusForClosedAction(true);
@@ -194,14 +464,21 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
// nothing changed as "feedback after close" is disabled // nothing changed as "feedback after close" is disabled
assertThat(targetManagement.findTargetByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).getTargetInfo() assertThat(targetManagement.findTargetByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).getTargetInfo()
.getUpdateStatus()).isEqualTo(TargetUpdateStatus.IN_SYNC); .getUpdateStatus()).isEqualTo(TargetUpdateStatus.IN_SYNC);
assertThat(actionStatusRepository.findAll(pageReq).getNumberOfElements()).isEqualTo(3);
assertThat(actionStatusRepository.count()).isEqualTo(3);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, action.getId()).getNumberOfElements()) assertThat(deploymentManagement.findActionStatusByAction(pageReq, action.getId()).getNumberOfElements())
.isEqualTo(3); .isEqualTo(3);
} }
@Test @Test
@Description("Controller trys to send an update feedback after it has been finished which is actepted as the repository is " @Description("Controller trys to send an update feedback after it has been finished which is accepted as the repository is "
+ "configured to accept them.") + "configured to accept them.")
@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),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void sendUpdatesForFinishUpdateProcessAcceptedIfEnabled() { public void sendUpdatesForFinishUpdateProcessAcceptedIfEnabled() {
repositoryProperties.setRejectActionStatusForClosedAction(false); repositoryProperties.setRejectActionStatusForClosedAction(false);
@@ -212,9 +489,58 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
// nothing changed as "feedback after close" is disabled // nothing changed as "feedback after close" is disabled
assertThat(targetManagement.findTargetByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).getTargetInfo() assertThat(targetManagement.findTargetByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).getTargetInfo()
.getUpdateStatus()).isEqualTo(TargetUpdateStatus.IN_SYNC); .getUpdateStatus()).isEqualTo(TargetUpdateStatus.IN_SYNC);
// however, additional action status has been stored
assertThat(actionStatusRepository.findAll(pageReq).getNumberOfElements()).isEqualTo(4); assertThat(actionStatusRepository.findAll(pageReq).getNumberOfElements()).isEqualTo(4);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, action.getId()).getNumberOfElements()) assertThat(deploymentManagement.findActionStatusByAction(pageReq, action.getId()).getNumberOfElements())
.isEqualTo(4); .isEqualTo(4);
} }
@Test
@Description("Ensures that target attribute update is reflected by the repository.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 3) })
public void updateTargetAttributes() {
final String controllerId = "test123";
testdataFactory.createTarget(controllerId);
addAttributeAndVerify(controllerId);
addSecondAttributeAndVerify(controllerId);
updateAttributeAndVerify(controllerId);
}
@Step
private void addAttributeAndVerify(final String controllerId) {
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(1);
testData.put("test1", "testdata1");
controllerManagament.updateControllerAttributes(controllerId, testData);
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId);
assertThat(target.getTargetInfo().getControllerAttributes()).as("Controller Attributes are wrong")
.isEqualTo(testData);
}
@Step
private void addSecondAttributeAndVerify(final String controllerId) {
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(2);
testData.put("test2", "testdata20");
controllerManagament.updateControllerAttributes(controllerId, testData);
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId);
testData.put("test1", "testdata1");
assertThat(target.getTargetInfo().getControllerAttributes()).as("Controller Attributes are wrong")
.isEqualTo(testData);
}
@Step
private void updateAttributeAndVerify(final String controllerId) {
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(2);
testData.put("test1", "testdata12");
controllerManagament.updateControllerAttributes(controllerId, testData);
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId);
testData.put("test2", "testdata20");
assertThat(target.getTargetInfo().getControllerAttributes()).as("Controller Attributes are wrong")
.isEqualTo(testData);
}
} }

View File

@@ -54,11 +54,9 @@ import org.springframework.data.domain.PageRequest;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import ru.yandex.qatools.allure.annotations.Description; import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features; import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Step;
import ru.yandex.qatools.allure.annotations.Stories; import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Repository") @Features("Component Tests - Repository")
@@ -245,54 +243,6 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
assertThat(targetManagement.countTargetsAll()).as("target count is wrong").isEqualTo(0); assertThat(targetManagement.countTargetsAll()).as("target count is wrong").isEqualTo(0);
} }
@Test
@Description("Ensures that target attribute update is reflected by the repository.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 3) })
public void updateTargetAttributes() {
final String controllerId = "test123";
testdataFactory.createTarget(controllerId);
addAttributeAndVerify(controllerId);
addSecondAttributeAndVerify(controllerId);
updateAttributeAndVerify(controllerId);
}
@Step
private void addAttributeAndVerify(final String controllerId) {
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(1);
testData.put("test1", "testdata1");
controllerManagament.updateControllerAttributes(controllerId, testData);
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId);
assertThat(target.getTargetInfo().getControllerAttributes()).as("Controller Attributes are wrong")
.isEqualTo(testData);
}
@Step
private void addSecondAttributeAndVerify(final String controllerId) {
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(2);
testData.put("test2", "testdata20");
controllerManagament.updateControllerAttributes(controllerId, testData);
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId);
testData.put("test1", "testdata1");
assertThat(target.getTargetInfo().getControllerAttributes()).as("Controller Attributes are wrong")
.isEqualTo(testData);
}
@Step
private void updateAttributeAndVerify(final String controllerId) {
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(2);
testData.put("test1", "testdata12");
controllerManagament.updateControllerAttributes(controllerId, testData);
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId);
testData.put("test2", "testdata20");
assertThat(target.getTargetInfo().getControllerAttributes()).as("Controller Attributes are wrong")
.isEqualTo(testData);
}
private Target createTargetWithAttributes(final String controllerId) { private Target createTargetWithAttributes(final String controllerId) {
final Map<String, String> testData = new HashMap<>(); final Map<String, String> testData = new HashMap<>();
testData.put("test1", "testdata1"); testData.put("test1", "testdata1");

View File

@@ -78,6 +78,8 @@ import org.springframework.core.env.Environment;
import org.springframework.data.auditing.AuditingHandler; import org.springframework.data.auditing.AuditingHandler;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.MediaTypes;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.annotation.DirtiesContext.ClassMode;
@@ -104,7 +106,7 @@ import com.google.common.collect.Lists;
public abstract class AbstractIntegrationTest implements EnvironmentAware { public abstract class AbstractIntegrationTest implements EnvironmentAware {
private static final Logger LOG = LoggerFactory.getLogger(AbstractIntegrationTest.class); private static final Logger LOG = LoggerFactory.getLogger(AbstractIntegrationTest.class);
protected static final Pageable pageReq = new PageRequest(0, 400); protected static final Pageable pageReq = new PageRequest(0, 400, new Sort(Direction.ASC, "id"));
/** /**
* Constant for MediaType HAL with encoding UTF-8. Necessary since Spring * Constant for MediaType HAL with encoding UTF-8. Necessary since Spring

View File

@@ -8,6 +8,8 @@
*/ */
package org.eclipse.hawkbit.repository.test.util; package org.eclipse.hawkbit.repository.test.util;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.SecureRandom; import java.security.SecureRandom;
@@ -44,6 +46,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
@@ -498,7 +501,9 @@ public class TestdataFactory {
* @return persisted {@link Target} * @return persisted {@link Target}
*/ */
public Target createTarget(final String controllerId) { public Target createTarget(final String controllerId) {
return targetManagement.createTarget(entityFactory.target().create().controllerId(controllerId)); final Target target = targetManagement.createTarget(entityFactory.target().create().controllerId(controllerId));
assertThat(target.getTargetInfo().getUpdateStatus()).isEqualTo(TargetUpdateStatus.UNKNOWN);
return target;
} }
/** /**

View File

@@ -33,6 +33,7 @@ import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.I18N;
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil; import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.UINotification; import org.eclipse.hawkbit.ui.utils.UINotification;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -72,7 +73,6 @@ public class ActionHistoryTable extends TreeTable {
private static final Logger LOG = LoggerFactory.getLogger(ActionHistoryTable.class); private static final Logger LOG = LoggerFactory.getLogger(ActionHistoryTable.class);
private static final String BUTTON_CANCEL = "button.cancel"; private static final String BUTTON_CANCEL = "button.cancel";
private static final String BUTTON_OK = "button.ok"; private static final String BUTTON_OK = "button.ok";
private static final String STATUS_ICON_GREEN = "statusIconGreen";
private final I18N i18n; private final I18N i18n;
private final transient DeploymentManagement deploymentManagement; private final transient DeploymentManagement deploymentManagement;
@@ -504,50 +504,21 @@ public class ActionHistoryTable extends TreeTable {
*/ */
private Label getStatusIcon(final Action.Status status) { private Label getStatusIcon(final Action.Status status) {
final Label label = new LabelBuilder().name("").buildLabel(); final Label label = new LabelBuilder().name("").buildLabel();
final String statusIconPending = "statusIconPending";
label.setContentMode(ContentMode.HTML); label.setContentMode(ContentMode.HTML);
if (Action.Status.FINISHED == status) {
label.setDescription(i18n.get("label.finished")); final ActionStatusIconMapper mapping = ActionStatusIconMapper.MAPPINGS.get(status);
label.setStyleName(STATUS_ICON_GREEN);
label.setValue(FontAwesome.CHECK_CIRCLE.getHtml()); if (mapping == null) {
} else if (Action.Status.ERROR == status) { LOG.error("Unknown status icon mapping");
label.setDescription(i18n.get("label.error"));
label.setStyleName("statusIconRed");
label.setValue(FontAwesome.EXCLAMATION_CIRCLE.getHtml());
} else if (Action.Status.WARNING == status) {
label.setStyleName("statusIconOrange");
label.setDescription(i18n.get("label.warning"));
label.setValue(FontAwesome.EXCLAMATION_CIRCLE.getHtml());
} else if (Action.Status.RUNNING == status) {
// dynamic spinner
label.setStyleName(statusIconPending);
label.setDescription(i18n.get("label.running"));
label.setValue(FontAwesome.ADJUST.getHtml());
} else if (Action.Status.CANCELING == status) {
label.setStyleName(statusIconPending);
label.setDescription(i18n.get("label.cancelling"));
label.setValue(FontAwesome.TIMES_CIRCLE.getHtml());
} else if (Action.Status.CANCELED == status) {
label.setStyleName(statusIconPending);
label.setDescription(i18n.get("label.cancelled"));
label.setStyleName(STATUS_ICON_GREEN);
label.setValue(FontAwesome.TIMES_CIRCLE.getHtml());
} else if (Action.Status.RETRIEVED == status) {
label.setStyleName(statusIconPending);
label.setDescription(i18n.get("label.retrieved"));
label.setValue(FontAwesome.CIRCLE_O.getHtml());
} else if (Action.Status.DOWNLOAD == status) {
label.setStyleName(statusIconPending);
label.setDescription(i18n.get("label.download"));
label.setValue(FontAwesome.CLOUD_DOWNLOAD.getHtml());
} else if (Action.Status.SCHEDULED == status) {
label.setStyleName(statusIconPending);
label.setDescription(i18n.get("label.scheduled"));
label.setValue(FontAwesome.HOURGLASS_1.getHtml());
} else {
label.setDescription(""); label.setDescription("");
label.setValue(""); label.setValue("");
return label;
} }
label.setDescription(i18n.get(mapping.getDescriptionI18N()));
label.setStyleName(mapping.getStyleName());
label.setValue(mapping.getIcon().getHtml());
return label; return label;
} }
@@ -568,7 +539,7 @@ public class ActionHistoryTable extends TreeTable {
if (actionWithActiveStatus.isHitAutoForceTime(currentTimeMillis)) { if (actionWithActiveStatus.isHitAutoForceTime(currentTimeMillis)) {
autoForceLabel.setDescription("autoforced"); autoForceLabel.setDescription("autoforced");
autoForceLabel.setStyleName(STATUS_ICON_GREEN); autoForceLabel.setStyleName(SPUIStyleDefinitions.STATUS_ICON_GREEN);
autoForceLabel.setDescription("auto forced since " + SPDateTimeUtil autoForceLabel.setDescription("auto forced since " + SPDateTimeUtil
.getDurationFormattedString(actionWithActiveStatus.getForcedTime(), currentTimeMillis, i18n)); .getDurationFormattedString(actionWithActiveStatus.getForcedTime(), currentTimeMillis, i18n));
} else { } else {

View File

@@ -0,0 +1,75 @@
/**
* 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.actionhistory;
import java.util.Map;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import com.google.common.collect.Maps;
import com.vaadin.server.FontAwesome;
/**
* Mapping utility for {@link ActionStatus} to icon in action history table.
*
*/
public final class ActionStatusIconMapper {
static final Map<Action.Status, ActionStatusIconMapper> MAPPINGS = Maps.newHashMapWithExpectedSize(10);
static {
MAPPINGS.put(Action.Status.FINISHED, new ActionStatusIconMapper("label.finished",
SPUIStyleDefinitions.STATUS_ICON_GREEN, FontAwesome.CHECK_CIRCLE));
MAPPINGS.put(Action.Status.CANCELED, new ActionStatusIconMapper("label.cancelled",
SPUIStyleDefinitions.STATUS_ICON_GREEN, FontAwesome.TIMES_CIRCLE));
MAPPINGS.put(Action.Status.ERROR, new ActionStatusIconMapper("label.error",
SPUIStyleDefinitions.STATUS_ICON_RED, FontAwesome.EXCLAMATION_CIRCLE));
MAPPINGS.put(Action.Status.WARNING, new ActionStatusIconMapper("label.warning",
SPUIStyleDefinitions.STATUS_ICON_ORANGE, FontAwesome.EXCLAMATION_CIRCLE));
MAPPINGS.put(Action.Status.CANCEL_REJECTED, new ActionStatusIconMapper("label.warning",
SPUIStyleDefinitions.STATUS_ICON_ORANGE, FontAwesome.EXCLAMATION_CIRCLE));
MAPPINGS.put(Action.Status.RUNNING, new ActionStatusIconMapper("label.running",
SPUIStyleDefinitions.STATUS_ICON_PENDING, FontAwesome.ADJUST));
MAPPINGS.put(Action.Status.CANCELING, new ActionStatusIconMapper("label.cancelling",
SPUIStyleDefinitions.STATUS_ICON_PENDING, FontAwesome.TIMES_CIRCLE));
MAPPINGS.put(Action.Status.RETRIEVED, new ActionStatusIconMapper("label.retrieved",
SPUIStyleDefinitions.STATUS_ICON_PENDING, FontAwesome.CIRCLE_O));
MAPPINGS.put(Action.Status.DOWNLOAD, new ActionStatusIconMapper("label.download",
SPUIStyleDefinitions.STATUS_ICON_PENDING, FontAwesome.CLOUD_DOWNLOAD));
MAPPINGS.put(Action.Status.SCHEDULED, new ActionStatusIconMapper("label.scheduled",
SPUIStyleDefinitions.STATUS_ICON_PENDING, FontAwesome.HOURGLASS_1));
}
private final String descriptionI18N;
private final String styleName;
private final FontAwesome icon;
private ActionStatusIconMapper(final String descriptionI18N, final String styleName, final FontAwesome icon) {
this.descriptionI18N = descriptionI18N;
this.styleName = styleName;
this.icon = icon;
}
String getDescriptionI18N() {
return descriptionI18N;
}
String getStyleName() {
return styleName;
}
FontAwesome getIcon() {
return icon;
}
}

View File

@@ -183,6 +183,11 @@ public final class SPUIStyleDefinitions {
*/ */
public static final String STATUS_ICON_RED = "statusIconRed"; public static final String STATUS_ICON_RED = "statusIconRed";
/**
* Status icon style - orange color.
*/
public static final String STATUS_ICON_ORANGE = "statusIconOrange";
/** /**
* Status icon style - green color. * Status icon style - green color.
*/ */