Merge with master

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
kaizimmerm
2016-09-14 11:05:05 +02:00
9 changed files with 79 additions and 46 deletions

View File

@@ -14,6 +14,7 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@@ -29,7 +30,6 @@ import org.eclipse.hawkbit.ddi.json.model.DdiControllerBase;
import org.eclipse.hawkbit.ddi.json.model.DdiPolling; import org.eclipse.hawkbit.ddi.json.model.DdiPolling;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants; import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
@@ -108,16 +108,18 @@ public final class DataConversionHelper {
return file; return file;
} }
static DdiControllerBase fromTarget(final Target target, final List<Action> actions, static DdiControllerBase fromTarget(final Target target, final Optional<Action> action,
final String defaultControllerPollTime, final TenantAware tenantAware) { final String defaultControllerPollTime, final TenantAware tenantAware) {
final DdiControllerBase result = new DdiControllerBase( final DdiControllerBase result = new DdiControllerBase(
new DdiConfig(new DdiPolling(defaultControllerPollTime))); new DdiConfig(new DdiPolling(defaultControllerPollTime)));
boolean addedUpdate = false; if (action.isPresent()) {
boolean addedCancel = false; if (action.get().isCancelingOrCanceled()) {
final long countCancelingActions = actions.stream().filter(a -> a.getStatus() == Status.CANCELING).count(); result.add(linkTo(
for (final Action action : actions) { methodOn(DdiRootController.class, tenantAware.getCurrentTenant()).getControllerCancelAction(
if (countCancelingActions <= 0 && !action.isCancelingOrCanceled() && !addedUpdate) { tenantAware.getCurrentTenant(), target.getControllerId(), action.get().getId()))
.withRel(DdiRestConstants.CANCEL_ACTION));
} else {
// we need to add the hashcode here of the actionWithStatus // we need to add the hashcode here of the actionWithStatus
// because the action might // because the action might
// have changed from 'soft' to 'forced' type and we need to // have changed from 'soft' to 'forced' type and we need to
@@ -125,15 +127,8 @@ public final class DataConversionHelper {
// response because of eTags. // response because of eTags.
result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant()) result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.getControllerBasedeploymentAction(tenantAware.getCurrentTenant(), target.getControllerId(), .getControllerBasedeploymentAction(tenantAware.getCurrentTenant(), target.getControllerId(),
action.getId(), calculateEtag(action))) action.get().getId(), calculateEtag(action.get())))
.withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION)); .withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION));
addedUpdate = true;
} else if (action.isCancelingOrCanceled() && !addedCancel) {
result.add(linkTo(
methodOn(DdiRootController.class, tenantAware.getCurrentTenant()).getControllerCancelAction(
tenantAware.getCurrentTenant(), target.getControllerId(), action.getId()))
.withRel(DdiRestConstants.CANCEL_ACTION));
addedCancel = true;
} }
} }

View File

@@ -131,7 +131,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
} }
return new ResponseEntity<>( return new ResponseEntity<>(
DataConversionHelper.fromTarget(target, controllerManagement.findActionByTargetAndActive(target), DataConversionHelper.fromTarget(target, controllerManagement.findOldestActiveActionByTarget(target),
controllerManagement.getPollingTime(), tenantAware), controllerManagement.getPollingTime(), tenantAware),
HttpStatus.OK); HttpStatus.OK);
} }

View File

@@ -13,6 +13,7 @@ import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
@@ -32,6 +33,7 @@ import org.eclipse.hawkbit.dmf.json.model.ArtifactHash;
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse; import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ArtifactManagement;
@@ -41,7 +43,7 @@ import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.TenantNotExistException; import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
import org.eclipse.hawkbit.repository.exception.ToManyStatusEntriesException; import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
@@ -155,7 +157,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
return handleAuthentifiactionMessage(message); return handleAuthentifiactionMessage(message);
} catch (final IllegalArgumentException ex) { } catch (final IllegalArgumentException ex) {
throw new AmqpRejectAndDontRequeueException("Invalid message!", ex); throw new AmqpRejectAndDontRequeueException("Invalid message!", ex);
} catch (final TenantNotExistException | ToManyStatusEntriesException e) { } catch (final TenantNotExistException | TooManyStatusEntriesException e) {
throw new AmqpRejectAndDontRequeueException(e); throw new AmqpRejectAndDontRequeueException(e);
} finally { } finally {
SecurityContextHolder.setContext(oldContext); SecurityContextHolder.setContext(oldContext);
@@ -196,8 +198,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
} }
} catch (final IllegalArgumentException ex) { } catch (final IllegalArgumentException ex) {
throw new AmqpRejectAndDontRequeueException("Invalid message!", ex); throw new AmqpRejectAndDontRequeueException("Invalid message!", ex);
} catch (final TenantNotExistException teex) { } catch (final TenantNotExistException | TooManyStatusEntriesException e) {
throw new AmqpRejectAndDontRequeueException(teex); throw new AmqpRejectAndDontRequeueException(e);
} finally { } finally {
SecurityContextHolder.setContext(oldContext); SecurityContextHolder.setContext(oldContext);
} }
@@ -344,18 +346,24 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
} }
private void lookIfUpdateAvailable(final Target target) { private void lookIfUpdateAvailable(final Target target) {
final List<Action> actions = controllerManagement.findActionByTargetAndActive(target); final Optional<Action> action = controllerManagement.findOldestActiveActionByTarget(target);
if (actions.isEmpty()) { if (!action.isPresent()) {
return; return;
} }
// action are ordered by ASC
final Action action = actions.get(0); if (action.get().isCancelingOrCanceled()) {
final DistributionSet distributionSet = action.getDistributionSet(); amqpMessageDispatcherService.targetCancelAssignmentToDistributionSet(
new CancelTargetAssignmentEvent(target.getOptLockRevision(), target.getTenant(),
target.getControllerId(), action.get().getId(), target.getTargetInfo().getAddress()));
return;
}
final DistributionSet distributionSet = action.get().getDistributionSet();
final List<SoftwareModule> softwareModuleList = controllerManagement final List<SoftwareModule> softwareModuleList = controllerManagement
.findSoftwareModulesByDistributionSet(distributionSet); .findSoftwareModulesByDistributionSet(distributionSet);
final String targetSecurityToken = systemSecurityContext.runAsSystem(() -> target.getSecurityToken()); final String targetSecurityToken = systemSecurityContext.runAsSystem(() -> target.getSecurityToken());
amqpMessageDispatcherService.targetAssignDistributionSet(new TargetAssignDistributionSetEvent( amqpMessageDispatcherService.targetAssignDistributionSet(new TargetAssignDistributionSetEvent(
target.getOptLockRevision(), target.getTenant(), target.getControllerId(), action.getId(), target.getOptLockRevision(), target.getTenant(), target.getControllerId(), action.get().getId(),
softwareModuleList, target.getTargetInfo().getAddress(), targetSecurityToken)); softwareModuleList, target.getTargetInfo().getAddress(), targetSecurityToken));
} }

View File

@@ -23,6 +23,7 @@ import java.net.URI;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.eclipse.hawkbit.api.HostnameResolver; import org.eclipse.hawkbit.api.HostnameResolver;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
@@ -155,6 +156,7 @@ public class AmqpMessageHandlerServiceTest {
final ArgumentCaptor<URI> uriCaptor = ArgumentCaptor.forClass(URI.class); final ArgumentCaptor<URI> uriCaptor = ArgumentCaptor.forClass(URI.class);
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotexist(targetIdCaptor.capture(), when(controllerManagementMock.findOrRegisterTargetIfItDoesNotexist(targetIdCaptor.capture(),
uriCaptor.capture())).thenReturn(null); uriCaptor.capture())).thenReturn(null);
when(controllerManagementMock.findOldestActiveActionByTarget(Matchers.any())).thenReturn(Optional.empty());
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost"); amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
@@ -362,9 +364,8 @@ public class AmqpMessageHandlerServiceTest {
when(controllerManagementMock.addUpdateActionStatus(Matchers.any())).thenReturn(action); when(controllerManagementMock.addUpdateActionStatus(Matchers.any())).thenReturn(action);
when(entityFactoryMock.generateActionStatus()).thenReturn(new JpaActionStatus()); when(entityFactoryMock.generateActionStatus()).thenReturn(new JpaActionStatus());
// for the test the same action can be used // for the test the same action can be used
final List<Action> actionList = new ArrayList<>(); when(controllerManagementMock.findOldestActiveActionByTarget(Matchers.any()))
actionList.add(action); .thenReturn(Optional.of(action));
when(controllerManagementMock.findActionByTargetAndActive(Matchers.any())).thenReturn(actionList);
final List<SoftwareModule> softwareModuleList = createSoftwareModuleList(); final List<SoftwareModule> softwareModuleList = createSoftwareModuleList();
when(controllerManagementMock.findSoftwareModulesByDistributionSet(Matchers.any())) when(controllerManagementMock.findSoftwareModulesByDistributionSet(Matchers.any()))

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository;
import java.net.URI; import java.net.URI;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@@ -19,7 +20,7 @@ import org.eclipse.hawkbit.repository.eventbus.event.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
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.ToManyStatusEntriesException; import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
@@ -95,7 +96,7 @@ public interface ControllerManagement {
* *
* @throws EntityAlreadyExistsException * @throws EntityAlreadyExistsException
* if a given entity already exists * if a given entity already exists
* @throws ToManyStatusEntriesException * @throws TooManyStatusEntriesException
* if more than the allowed number of status entries are * if more than the allowed number of status entries are
* inserted * inserted
*/ */
@@ -111,7 +112,18 @@ public interface ControllerManagement {
* @return a list of actions assigned to given target which are active * @return a list of actions assigned to given target which are active
*/ */
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
List<Action> findActionByTargetAndActive(@NotNull Target target); List<Action> findActiveActionByTarget(@NotNull Target target);
/**
* Retrieves oldest {@link Action} that is active and assigned to a
* {@link Target}.
*
* @param target
* the target to retrieve the actions from
* @return a list of actions assigned to given target which are active
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Optional<Action> findOldestActiveActionByTarget(@NotNull Target target);
/** /**
* Get the {@link Action} entity for given actionId with all lazy * Get the {@link Action} entity for given actionId with all lazy

View File

@@ -18,7 +18,7 @@ import org.eclipse.hawkbit.exception.AbstractServerRtException;
* *
* *
*/ */
public final class ToManyStatusEntriesException extends AbstractServerRtException { public final class TooManyStatusEntriesException extends AbstractServerRtException {
/** /**
* *
*/ */
@@ -28,7 +28,7 @@ public final class ToManyStatusEntriesException extends AbstractServerRtExceptio
* Creates a new FileUploadFailedException with * Creates a new FileUploadFailedException with
* {@link SpServerError#SP_REST_BODY_NOT_READABLE} error. * {@link SpServerError#SP_REST_BODY_NOT_READABLE} error.
*/ */
public ToManyStatusEntriesException() { public TooManyStatusEntriesException() {
super(SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES); super(SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES);
} }
@@ -36,7 +36,7 @@ public final class ToManyStatusEntriesException extends AbstractServerRtExceptio
* @param cause * @param cause
* for the exception * for the exception
*/ */
public ToManyStatusEntriesException(final Throwable cause) { public TooManyStatusEntriesException(final Throwable cause) {
super(SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES, cause); super(SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES, cause);
} }
@@ -44,7 +44,7 @@ public final class ToManyStatusEntriesException extends AbstractServerRtExceptio
* @param message * @param message
* of the error * of the error
*/ */
public ToManyStatusEntriesException(final String message) { public TooManyStatusEntriesException(final String message) {
super(message, SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES); super(message, SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES);
} }
} }

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
@@ -79,21 +80,31 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
Slice<Action> findByTarget(Pageable pageable, JpaTarget target); Slice<Action> findByTarget(Pageable pageable, JpaTarget target);
/** /**
* Retrieves all {@link Action}s which are active and referring the given * Retrieves all {@link Action}s which are active and referring to the given
* {@link Target} in a specified order. Loads also the lazy * {@link Target} order by ID ascending.
* {@link Action#getDistributionSet()} field.
* *
* @param pageable
* page parameters
* @param target * @param target
* the target to find assigned actions * the target to find assigned actions
* @param active * @param active
* the action active flag * the action active flag
* @return the found {@link Action}s * @return the found {@link Action}s
*/ */
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
List<Action> findByTargetAndActiveOrderByIdAsc(final JpaTarget target, boolean active); List<Action> findByTargetAndActiveOrderByIdAsc(final JpaTarget target, boolean active);
/**
* Retrieves the oldest {@link Action} that is active and referring to the
* given {@link Target}.
*
* @param target
* the target to find assigned actions
* @param active
* the action active flag
*
* @return the found {@link Action}
*/
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
Optional<Action> findFirstByTargetAndActiveOrderByIdAsc(final JpaTarget target, boolean active);
/** /**
* Retrieves latest {@link UpdateAction} for given target and * Retrieves latest {@link UpdateAction} for given target and
* {@link SoftwareModule}. * {@link SoftwareModule}.

View File

@@ -12,6 +12,7 @@ import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaBuilder;
@@ -26,7 +27,7 @@ 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;
import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException; import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException;
import org.eclipse.hawkbit.repository.exception.ToManyStatusEntriesException; import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
import org.eclipse.hawkbit.repository.jpa.cache.CacheWriteNotify; import org.eclipse.hawkbit.repository.jpa.cache.CacheWriteNotify;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
@@ -156,10 +157,15 @@ public class JpaControllerManagement implements ControllerManagement {
} }
@Override @Override
public List<Action> findActionByTargetAndActive(final Target target) { public List<Action> findActiveActionByTarget(final Target target) {
return actionRepository.findByTargetAndActiveOrderByIdAsc((JpaTarget) target, true); return actionRepository.findByTargetAndActiveOrderByIdAsc((JpaTarget) target, true);
} }
@Override
public Optional<Action> findOldestActiveActionByTarget(final Target target) {
return actionRepository.findFirstByTargetAndActiveOrderByIdAsc((JpaTarget) target, true);
}
@Override @Override
public List<SoftwareModule> findSoftwareModulesByDistributionSet(final DistributionSet distributionSet) { public List<SoftwareModule> findSoftwareModulesByDistributionSet(final DistributionSet distributionSet) {
return new ArrayList<>(softwareModuleRepository.findByAssignedTo((JpaDistributionSet) distributionSet)); return new ArrayList<>(softwareModuleRepository.findByAssignedTo((JpaDistributionSet) distributionSet));
@@ -326,7 +332,7 @@ public class JpaControllerManagement implements ControllerManagement {
LOG_DOS.error( LOG_DOS.error(
"Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!", "Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!",
securityProperties.getDos().getMaxStatusEntriesPerAction()); securityProperties.getDos().getMaxStatusEntriesPerAction());
throw new ToManyStatusEntriesException( throw new TooManyStatusEntriesException(
String.valueOf(securityProperties.getDos().getMaxStatusEntriesPerAction())); String.valueOf(securityProperties.getDos().getMaxStatusEntriesPerAction()));
} }
} }

View File

@@ -96,7 +96,7 @@
<org.powermock.version>1.5.4</org.powermock.version> <org.powermock.version>1.5.4</org.powermock.version>
<pl.pragmatists.version>1.0.2</pl.pragmatists.version> <pl.pragmatists.version>1.0.2</pl.pragmatists.version>
<guava.version>19.0</guava.version> <guava.version>19.0</guava.version>
<mariadb-java-client.version>1.4.3</mariadb-java-client.version> <mariadb-java-client.version>1.5.2</mariadb-java-client.version>
<embedded-mongo.version>1.50.5</embedded-mongo.version> <embedded-mongo.version>1.50.5</embedded-mongo.version>
<javax.el-api.version>2.2.4</javax.el-api.version> <javax.el-api.version>2.2.4</javax.el-api.version>
<corn-cps.version>1.1.7</corn-cps.version> <corn-cps.version>1.1.7</corn-cps.version>