Added configurable late feedback functionality, i.e. action feedback

still allowed even for closed action.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
kaizimmerm
2016-06-20 08:46:50 +02:00
parent eac44899f2
commit fb59dca168
5 changed files with 161 additions and 7 deletions

View File

@@ -0,0 +1,29 @@
package org.eclipse.hawkbit.repository;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for the repository.
*
*/
@ConfigurationProperties("hawkbit.server.repository")
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.
*/
private boolean rejectActionStatusForClosedAction = false;
public boolean isRejectActionStatusForClosedAction() {
return rejectActionStatusForClosedAction;
}
public void setRejectActionStatusForClosedAction(final boolean rejectActionStatusForClosedAction) {
this.rejectActionStatusForClosedAction = rejectActionStatusForClosedAction;
}
}

View File

@@ -136,6 +136,14 @@
<artifactId>fest-assert</artifactId> <artifactId>fest-assert</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hornetq</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hornetq</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler; import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler;
@@ -27,6 +28,7 @@ import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -40,7 +42,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
/** /**
* General configuration for the SP Repository. * General configuration for hawlBit's Repository.
* *
*/ */
@EnableJpaRepositories(basePackages = { "org.eclipse.hawkbit.repository.jpa" }) @EnableJpaRepositories(basePackages = { "org.eclipse.hawkbit.repository.jpa" })
@@ -50,6 +52,7 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
@Configuration @Configuration
@ComponentScan @ComponentScan
@EnableAutoConfiguration @EnableAutoConfiguration
@EnableConfigurationProperties(RepositoryProperties.class)
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* @return the {@link SystemSecurityContext} singleton bean which make it * @return the {@link SystemSecurityContext} singleton bean which make it

View File

@@ -21,6 +21,7 @@ import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants; import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -94,6 +95,9 @@ public class JpaControllerManagement implements ControllerManagement {
@Autowired @Autowired
private HawkbitSecurityProperties securityProperties; private HawkbitSecurityProperties securityProperties;
@Autowired
private RepositoryProperties repositoryProperties;
@Autowired @Autowired
private TenantConfigurationRepository tenantConfigurationRepository; private TenantConfigurationRepository tenantConfigurationRepository;
@@ -251,7 +255,13 @@ public class JpaControllerManagement implements ControllerManagement {
public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus) { public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus) {
final JpaAction action = (JpaAction) actionStatus.getAction(); final JpaAction action = (JpaAction) actionStatus.getAction();
if (!action.isActive()) { // TODO: test
// if action is already closed we accept further status updates on if
// permitted so by configuration. This is especially use full 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)) {
LOG.debug("Update of actionStatus {} for action {} not possible since action not active anymore.", LOG.debug("Update of actionStatus {} for action {} not possible since action not active anymore.",
actionStatus.getId(), action.getId()); actionStatus.getId(), action.getId());
return action; return action;
@@ -259,6 +269,12 @@ public class JpaControllerManagement implements ControllerManagement {
return handleAddUpdateActionStatus((JpaActionStatus) actionStatus, action); return handleAddUpdateActionStatus((JpaActionStatus) actionStatus, action);
} }
private boolean actionIsNotActiveButIntermediateFeedbackStillAllowed(final ActionStatus actionStatus,
final JpaAction action) {
return !action.isActive() && (repositoryProperties.isRejectActionStatusForClosedAction()
|| (Status.ERROR.equals(actionStatus.getStatus()) || Status.FINISHED.equals(actionStatus.getStatus())));
}
/** /**
* Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}. * Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}.
* *
@@ -286,8 +302,7 @@ public class JpaControllerManagement implements ControllerManagement {
case CANCELED: case CANCELED:
case WARNING: case WARNING:
case RUNNING: case RUNNING:
DeploymentHelper.updateTargetInfo(mergedTarget, TargetUpdateStatus.PENDING, false, targetInfoRepository, handleIntermediateFeedback(mergedAction, mergedTarget);
entityManager);
break; break;
default: default:
break; break;
@@ -300,6 +315,16 @@ public class JpaControllerManagement implements ControllerManagement {
return actionRepository.save(mergedAction); return actionRepository.save(mergedAction);
} }
private void handleIntermediateFeedback(final JpaAction mergedAction, final JpaTarget mergedTarget) {
// we change the target state only if the action is still running
// otherwise this is considered as late feedback that does not have
// an impact on the state anymore.
if (mergedAction.isActive()) {
DeploymentHelper.updateTargetInfo(mergedTarget, TargetUpdateStatus.PENDING, false, targetInfoRepository,
entityManager);
}
}
private void handleErrorOnAction(final JpaAction mergedAction, final JpaTarget mergedTarget) { private void handleErrorOnAction(final JpaAction mergedAction, final JpaTarget mergedTarget) {
mergedAction.setActive(false); mergedAction.setActive(false);
mergedAction.setStatus(Status.ERROR); mergedAction.setStatus(Status.ERROR);

View File

@@ -17,6 +17,7 @@ import java.util.List;
import javax.validation.ConstraintViolationException; import javax.validation.ConstraintViolationException;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
@@ -26,6 +27,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
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;
@@ -34,6 +36,8 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Repository") @Features("Component Tests - Repository")
@Stories("Controller Management") @Stories("Controller Management")
public class ControllerManagementTest extends AbstractJpaIntegrationTest { public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Autowired
private RepositoryProperties repositoryProperties;
@Test @Test
@Description("Controller adds a new action status.") @Description("Controller adds a new action status.")
@@ -94,7 +98,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Controller trys to finish an update process after it has been finished by an error action status.") @Description("Controller trys to finish an update process after it has been finished by an error action status.")
public void tryToFinishUpdateProcessMoreThenOnce() { public void tryToFinishUpdateProcessMoreThanOnce() {
// mock // mock
final Target target = new JpaTarget("Rabbit"); final Target target = new JpaTarget("Rabbit");
@@ -120,16 +124,101 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus()) assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.ERROR); .isEqualTo(TargetUpdateStatus.ERROR);
// try with disabled late feedback
repositoryProperties.setRejectActionStatusForClosedAction(true);
final ActionStatus actionStatusMessage3 = new JpaActionStatus(savedAction, Action.Status.FINISHED, final ActionStatus actionStatusMessage3 = new JpaActionStatus(savedAction, Action.Status.FINISHED,
System.currentTimeMillis()); System.currentTimeMillis());
actionStatusMessage3.addMessage("finish"); actionStatusMessage3.addMessage("finish");
controllerManagament.addUpdateActionStatus(actionStatusMessage3); savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage3);
targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus(); // test
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.ERROR);
// try with enabled late feedback
repositoryProperties.setRejectActionStatusForClosedAction(false);
final ActionStatus actionStatusMessage4 = new JpaActionStatus(savedAction, Action.Status.FINISHED,
System.currentTimeMillis());
actionStatusMessage4.addMessage("finish");
controllerManagament.addUpdateActionStatus(actionStatusMessage3);
// test // test
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus()) assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.ERROR); .isEqualTo(TargetUpdateStatus.ERROR);
} }
@Test
@Description("Controller trys to send an update feedback after it has been finished which is reject as the repository is "
+ "configured to reject that.")
public void sendUpdatesForFinishUpdateProcessDropedIfDisabled() {
repositoryProperties.setRejectActionStatusForClosedAction(true);
final Action action = prepareFinishedUpdate("Rabbit");
final ActionStatus actionStatusMessage1 = new JpaActionStatus(action, Action.Status.RUNNING,
System.currentTimeMillis());
actionStatusMessage1.addMessage("got some additional feedback");
controllerManagament.addUpdateActionStatus(actionStatusMessage1);
// nothing changed as "feedback after close" is disabled
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.IN_SYNC);
assertThat(actionStatusRepository.findAll(pageReq).getNumberOfElements()).isEqualTo(3);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, action).getNumberOfElements()).isEqualTo(3);
}
@Test
@Description("Controller trys to send an update feedback after it has been finished which is actepted as the repository is "
+ "configured to accept them.")
public void sendUpdatesForFinishUpdateProcessAcceptedIfEnabled() {
repositoryProperties.setRejectActionStatusForClosedAction(false);
Action action = prepareFinishedUpdate("Rabbit");
final ActionStatus actionStatusMessage1 = new JpaActionStatus(action, Action.Status.RUNNING,
System.currentTimeMillis());
actionStatusMessage1.addMessage("got some additional feedback");
action = controllerManagament.addUpdateActionStatus(actionStatusMessage1);
// nothing changed as "feedback after close" is disabled
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.IN_SYNC);
assertThat(actionStatusRepository.findAll(pageReq).getNumberOfElements()).isEqualTo(4);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, action).getNumberOfElements()).isEqualTo(4);
}
private Action prepareFinishedUpdate(final String controllerId) {
// mock
final Target target = new JpaTarget(controllerId);
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = targetManagement.createTarget(target);
final List<Target> toAssign = new ArrayList<>();
toAssign.add(savedTarget);
savedTarget = deploymentManagement.assignDistributionSet(ds, toAssign).getAssignedEntity().iterator().next();
Action savedAction = deploymentManagement.findActiveActionsByTarget(savedTarget).get(0);
// test and verify
final ActionStatus actionStatusMessage = new JpaActionStatus(savedAction, Action.Status.RUNNING,
System.currentTimeMillis());
actionStatusMessage.addMessage("running");
savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage);
assertThat(targetManagement.findTargetByControllerID(controllerId).getTargetInfo().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.PENDING);
final ActionStatus actionStatusMessage2 = new JpaActionStatus(savedAction, Action.Status.FINISHED,
System.currentTimeMillis());
actionStatusMessage2.addMessage("finish");
savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage2);
// test
assertThat(targetManagement.findTargetByControllerID(controllerId).getTargetInfo().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.IN_SYNC);
assertThat(actionStatusRepository.findAll(pageReq).getNumberOfElements()).isEqualTo(3);
assertThat(deploymentManagement.findActionStatusByAction(pageReq, savedAction).getNumberOfElements())
.isEqualTo(3);
return savedAction;
}
} }