Merge with master
Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.rest.api.DdiRestConstants;
|
||||
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.Target;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
@@ -108,16 +108,18 @@ public final class DataConversionHelper {
|
||||
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 DdiControllerBase result = new DdiControllerBase(
|
||||
new DdiConfig(new DdiPolling(defaultControllerPollTime)));
|
||||
|
||||
boolean addedUpdate = false;
|
||||
boolean addedCancel = false;
|
||||
final long countCancelingActions = actions.stream().filter(a -> a.getStatus() == Status.CANCELING).count();
|
||||
for (final Action action : actions) {
|
||||
if (countCancelingActions <= 0 && !action.isCancelingOrCanceled() && !addedUpdate) {
|
||||
if (action.isPresent()) {
|
||||
if (action.get().isCancelingOrCanceled()) {
|
||||
result.add(linkTo(
|
||||
methodOn(DdiRootController.class, tenantAware.getCurrentTenant()).getControllerCancelAction(
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), action.get().getId()))
|
||||
.withRel(DdiRestConstants.CANCEL_ACTION));
|
||||
} else {
|
||||
// we need to add the hashcode here of the actionWithStatus
|
||||
// because the action might
|
||||
// have changed from 'soft' to 'forced' type and we need to
|
||||
@@ -125,15 +127,8 @@ public final class DataConversionHelper {
|
||||
// response because of eTags.
|
||||
result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
|
||||
.getControllerBasedeploymentAction(tenantAware.getCurrentTenant(), target.getControllerId(),
|
||||
action.getId(), calculateEtag(action)))
|
||||
action.get().getId(), calculateEtag(action.get())))
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
}
|
||||
|
||||
return new ResponseEntity<>(
|
||||
DataConversionHelper.fromTarget(target, controllerManagement.findActionByTargetAndActive(target),
|
||||
DataConversionHelper.fromTarget(target, controllerManagement.findOldestActiveActionByTarget(target),
|
||||
controllerManagement.getPollingTime(), tenantAware),
|
||||
HttpStatus.OK);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
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.TenantSecurityToken;
|
||||
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.TenantAwareAuthenticationDetails;
|
||||
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.exception.EntityNotFoundException;
|
||||
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.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
@@ -155,7 +157,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
return handleAuthentifiactionMessage(message);
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
throw new AmqpRejectAndDontRequeueException("Invalid message!", ex);
|
||||
} catch (final TenantNotExistException | ToManyStatusEntriesException e) {
|
||||
} catch (final TenantNotExistException | TooManyStatusEntriesException e) {
|
||||
throw new AmqpRejectAndDontRequeueException(e);
|
||||
} finally {
|
||||
SecurityContextHolder.setContext(oldContext);
|
||||
@@ -196,8 +198,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
}
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
throw new AmqpRejectAndDontRequeueException("Invalid message!", ex);
|
||||
} catch (final TenantNotExistException teex) {
|
||||
throw new AmqpRejectAndDontRequeueException(teex);
|
||||
} catch (final TenantNotExistException | TooManyStatusEntriesException e) {
|
||||
throw new AmqpRejectAndDontRequeueException(e);
|
||||
} finally {
|
||||
SecurityContextHolder.setContext(oldContext);
|
||||
}
|
||||
@@ -344,18 +346,24 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
}
|
||||
|
||||
private void lookIfUpdateAvailable(final Target target) {
|
||||
final List<Action> actions = controllerManagement.findActionByTargetAndActive(target);
|
||||
if (actions.isEmpty()) {
|
||||
final Optional<Action> action = controllerManagement.findOldestActiveActionByTarget(target);
|
||||
if (!action.isPresent()) {
|
||||
return;
|
||||
}
|
||||
// action are ordered by ASC
|
||||
final Action action = actions.get(0);
|
||||
final DistributionSet distributionSet = action.getDistributionSet();
|
||||
|
||||
if (action.get().isCancelingOrCanceled()) {
|
||||
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
|
||||
.findSoftwareModulesByDistributionSet(distributionSet);
|
||||
final String targetSecurityToken = systemSecurityContext.runAsSystem(() -> target.getSecurityToken());
|
||||
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));
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.api.HostnameResolver;
|
||||
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
|
||||
@@ -155,6 +156,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
final ArgumentCaptor<URI> uriCaptor = ArgumentCaptor.forClass(URI.class);
|
||||
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotexist(targetIdCaptor.capture(),
|
||||
uriCaptor.capture())).thenReturn(null);
|
||||
when(controllerManagementMock.findOldestActiveActionByTarget(Matchers.any())).thenReturn(Optional.empty());
|
||||
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
|
||||
|
||||
@@ -362,9 +364,8 @@ public class AmqpMessageHandlerServiceTest {
|
||||
when(controllerManagementMock.addUpdateActionStatus(Matchers.any())).thenReturn(action);
|
||||
when(entityFactoryMock.generateActionStatus()).thenReturn(new JpaActionStatus());
|
||||
// for the test the same action can be used
|
||||
final List<Action> actionList = new ArrayList<>();
|
||||
actionList.add(action);
|
||||
when(controllerManagementMock.findActionByTargetAndActive(Matchers.any())).thenReturn(actionList);
|
||||
when(controllerManagementMock.findOldestActiveActionByTarget(Matchers.any()))
|
||||
.thenReturn(Optional.of(action));
|
||||
|
||||
final List<SoftwareModule> softwareModuleList = createSoftwareModuleList();
|
||||
when(controllerManagementMock.findSoftwareModulesByDistributionSet(Matchers.any()))
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
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.EntityNotFoundException;
|
||||
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.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
@@ -95,7 +96,7 @@ public interface ControllerManagement {
|
||||
*
|
||||
* @throws EntityAlreadyExistsException
|
||||
* if a given entity already exists
|
||||
* @throws ToManyStatusEntriesException
|
||||
* @throws TooManyStatusEntriesException
|
||||
* if more than the allowed number of status entries are
|
||||
* inserted
|
||||
*/
|
||||
@@ -111,7 +112,18 @@ public interface ControllerManagement {
|
||||
* @return a list of actions assigned to given target which are active
|
||||
*/
|
||||
@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
|
||||
|
||||
@@ -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
|
||||
* {@link SpServerError#SP_REST_BODY_NOT_READABLE} error.
|
||||
*/
|
||||
public ToManyStatusEntriesException() {
|
||||
public TooManyStatusEntriesException() {
|
||||
super(SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public final class ToManyStatusEntriesException extends AbstractServerRtExceptio
|
||||
* @param cause
|
||||
* for the exception
|
||||
*/
|
||||
public ToManyStatusEntriesException(final Throwable cause) {
|
||||
public TooManyStatusEntriesException(final Throwable cause) {
|
||||
super(SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES, cause);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class ToManyStatusEntriesException extends AbstractServerRtExceptio
|
||||
* @param message
|
||||
* of the error
|
||||
*/
|
||||
public ToManyStatusEntriesException(final String message) {
|
||||
public TooManyStatusEntriesException(final String message) {
|
||||
super(message, SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
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);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Action}s which are active and referring the given
|
||||
* {@link Target} in a specified order. Loads also the lazy
|
||||
* {@link Action#getDistributionSet()} field.
|
||||
* Retrieves all {@link Action}s which are active and referring to the given
|
||||
* {@link Target} order by ID ascending.
|
||||
*
|
||||
* @param pageable
|
||||
* page parameters
|
||||
* @param target
|
||||
* the target to find assigned actions
|
||||
* @param active
|
||||
* the action active flag
|
||||
* @return the found {@link Action}s
|
||||
*/
|
||||
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
|
||||
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
|
||||
* {@link SoftwareModule}.
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
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.exception.EntityNotFoundException;
|
||||
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.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
@@ -156,10 +157,15 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> findActionByTargetAndActive(final Target target) {
|
||||
public List<Action> findActiveActionByTarget(final Target target) {
|
||||
return actionRepository.findByTargetAndActiveOrderByIdAsc((JpaTarget) target, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Action> findOldestActiveActionByTarget(final Target target) {
|
||||
return actionRepository.findFirstByTargetAndActiveOrderByIdAsc((JpaTarget) target, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SoftwareModule> findSoftwareModulesByDistributionSet(final DistributionSet distributionSet) {
|
||||
return new ArrayList<>(softwareModuleRepository.findByAssignedTo((JpaDistributionSet) distributionSet));
|
||||
@@ -326,7 +332,7 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
LOG_DOS.error(
|
||||
"Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!",
|
||||
securityProperties.getDos().getMaxStatusEntriesPerAction());
|
||||
throw new ToManyStatusEntriesException(
|
||||
throw new TooManyStatusEntriesException(
|
||||
String.valueOf(securityProperties.getDos().getMaxStatusEntriesPerAction()));
|
||||
}
|
||||
}
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -96,7 +96,7 @@
|
||||
<org.powermock.version>1.5.4</org.powermock.version>
|
||||
<pl.pragmatists.version>1.0.2</pl.pragmatists.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>
|
||||
<javax.el-api.version>2.2.4</javax.el-api.version>
|
||||
<corn-cps.version>1.1.7</corn-cps.version>
|
||||
|
||||
Reference in New Issue
Block a user