Rollouts can be deleted (#436)

* Management UI

Signed-off-by: Melanie Retter <melanie.retter@bosch-si.com>

* Repository

Signed-off-by: Michael Hirsch <michael.hirsch@bosch-si.com>

* Optimisations and scheduler deleting enabled

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Melanie Retter
2017-02-18 07:19:28 +01:00
committed by Kai Zimmermann
parent 804522f966
commit 5628d625e8
159 changed files with 3029 additions and 1737 deletions

View File

@@ -15,7 +15,6 @@ import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.ArtifactBinaryNotFoundException;
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
@@ -58,7 +57,10 @@ public interface ArtifactManagement {
*
* @return uploaded {@link Artifact}
*
* @throw ArtifactUploadFailedException if upload fails
* @throws ArtifactUploadFailedException
* if upload fails
* @throws EntityNotFoundException
* if given software module does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
Artifact createArtifact(@NotNull InputStream inputStream, @NotNull Long moduleId, final String filename,
@@ -101,16 +103,20 @@ public interface ArtifactManagement {
String providedMd5Sum, String providedSha1Sum, boolean overrideExisting, String contentType);
/**
* Garbage collects local artifact binary file if only referenced by given
* {@link Artifact} metadata object.
* Garbage collects artifact binaries if only referenced by given
* {@link SoftwareModule#getId()} or {@link SoftwareModules} that are marged
* as deleted.
*
*
* @param artifactId
* the related local artifact
* @param artifactSha1Hash
* no longer needed
* @param moduleId
* the garbage colelction call is made for
*
* @return <code>true</code> if an binary was actually garbage collected
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
boolean clearArtifactBinary(@NotNull Long artifactId);
boolean clearArtifactBinary(@NotEmpty String artifactSha1Hash, @NotNull Long moduleId);
/**
* Deletes {@link Artifact} based on given id.
@@ -144,6 +150,9 @@ public interface ArtifactManagement {
* @param softwareModuleId
* software module id.
* @return found {@link Artifact}
*
* @throws EntityNotFoundException
* if software module with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
@@ -179,6 +188,9 @@ public interface ArtifactManagement {
* @param swId
* software module id
* @return Page<Artifact>
*
* @throws EntityNotFoundException
* if software module with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<Artifact> findArtifactBySoftwareModule(@NotNull Pageable pageReq, @NotNull Long swId);
@@ -190,11 +202,9 @@ public interface ArtifactManagement {
* to search for
* @return loaded {@link DbArtifact}
*
* @throws ArtifactBinaryNotFoundException
* if file could not be found in store
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_CONTROLLER_DOWNLOAD)
DbArtifact loadArtifactBinary(@NotEmpty String sha1Hash);
Optional<DbArtifact> loadArtifactBinary(@NotEmpty String sha1Hash);
}

View File

@@ -1,61 +0,0 @@
/**
* 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.repository;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Rollout Management properties.
*
*/
@ConfigurationProperties("hawkbit.autoassign")
public class AutoAssignProperties {
/**
* Autoassign scheduler configuration.
*/
public static class Scheduler {
// used by @Scheduled annotation which needs constant
public static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.autoassign.scheduler.fixedDelay:60000}";
/**
* Schedule where the autoassign scheduler looks necessary state changes
* in milliseconds.
*/
private long fixedDelay = 60000L;
/**
* Set to true to run the autoassign scheduler.
*/
private boolean enabled = true;
public long getFixedDelay() {
return fixedDelay;
}
public void setFixedDelay(final long fixedDelay) {
this.fixedDelay = fixedDelay;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
}
private final Scheduler scheduler = new Scheduler();
public Scheduler getScheduler() {
return scheduler;
}
}

View File

@@ -120,6 +120,9 @@ public interface ControllerManagement {
* @param controllerId
* identifies the target to retrieve the actions from
* @return a list of actions assigned to given target which are active
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Optional<Action> findOldestActiveActionByTarget(@NotNull String controllerId);
@@ -157,6 +160,9 @@ public interface ControllerManagement {
* of the the {@link SoftwareModule} that should be assigned to
* the target
* @return last {@link Action} for given combination
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
@@ -184,6 +190,9 @@ public interface ControllerManagement {
* @return {@code true} if the given target has currently or had ever a
* relation to the given artifact through the action history,
* otherwise {@code false}
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
boolean hasTargetArtifactAssigned(@NotEmpty String controllerId, @NotEmpty String sha1Hash);
@@ -203,6 +212,9 @@ public interface ControllerManagement {
* @return {@code true} if the given target has currently or had ever a
* relation to the given artifact through the action history,
* otherwise {@code false}
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
boolean hasTargetArtifactAssigned(@NotNull Long targetId, @NotEmpty String sha1Hash);

View File

@@ -149,6 +149,8 @@ public interface DeploymentManagement {
* given {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException
* if the RSQL syntax is wrong
* @throws EntityNotFoundException
* if target with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Long countActionsByTarget(@NotNull String rsqlParam, @NotEmpty String controllerId);
@@ -171,6 +173,9 @@ public interface DeploymentManagement {
* @param controllerId
* the target associated to the actions to count
* @return the count value of found actions associated to the target
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Long countActionsByTarget(@NotEmpty String controllerId);
@@ -309,6 +314,9 @@ public interface DeploymentManagement {
* @param controllerId
* the target associated with the actions
* @return a list of actions associated with the given target
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
List<Action> findActiveActionsByTarget(@NotEmpty String controllerId);
@@ -320,6 +328,9 @@ public interface DeploymentManagement {
* @param controllerId
* the target associated with the actions
* @return a list of actions associated with the given target
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
List<Action> findInActiveActionsByTarget(@NotEmpty String controllerId);

View File

@@ -21,7 +21,6 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetWithActionStatus;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -40,6 +39,9 @@ public interface RolloutGroupManagement {
* @param pageable
* the page request to sort and limit the result
* @return a page of found {@link RolloutGroup}s
*
* @throws EntityNotFoundException
* of rollout with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Page<RolloutGroup> findAllRolloutGroupsWithDetailedStatus(@NotNull Long rolloutId, @NotNull Pageable pageable);
@@ -52,14 +54,16 @@ public interface RolloutGroupManagement {
* distribution set we do not create an action for it but the target is in
* the result list of the rollout-group.
*
* @param pageRequest
* @param pageable
* the page request to sort and limit the result
* @param rolloutGroupId
* rollout group
* @return {@link TargetWithActionStatus} target with action status
* @throws EntityNotFoundException
* if rollout group with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ)
Page<TargetWithActionStatus> findAllTargetsWithActionStatus(@NotNull PageRequest pageRequest,
Page<TargetWithActionStatus> findAllTargetsWithActionStatus(@NotNull Pageable pageable,
@NotNull Long rolloutGroupId);
/**
@@ -165,6 +169,9 @@ public interface RolloutGroupManagement {
* @param rolloutGroupId
* the rollout group id for the count
* @return the target rollout group count
*
* @throws EntityNotFoundException
* if rollout group with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Long countTargetsOfRolloutsGroup(@NotNull Long rolloutGroupId);

View File

@@ -29,6 +29,7 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation;
import org.eclipse.hawkbit.repository.model.Target;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -44,72 +45,37 @@ import org.springframework.util.concurrent.ListenableFuture;
public interface RolloutManagement {
/**
* Checking running rollouts. Rollouts which are checked updating the
* lastCheck to indicate that the current instance is handling the specific
* rollout. This code should run as system-code.
*
* <pre>
* {@code
* SystemSecurityContext.runAsSystem(new Callable<Void>() {
* public Void call() throws Exception {
* //run system-code
* }
* });
* }
* </pre>
*
* This method is intended to be called by a scheduler. And must be running
* in an transaction so it's splitted from the scheduler.
*
* Rollouts which are currently running are investigated, by means the
* error- and finish condition of running groups in this rollout are
* evaluated.
*
* @param delayBetweenChecks
* the time in milliseconds of the delay between the further and
* this check. This check is only applied if the last check is
* less than (lastcheck-delay).
* Process rollout based on its current {@link Rollout#getStatus()}.
*
* For {@link RolloutStatus#CREATING} that means creating the
* {@link RolloutGroup}s with {@link Target}s and when finished switch to
* {@link RolloutStatus#READY}.
*
* For {@link RolloutStatus#READY} that means switching to
* {@link RolloutStatus#STARTING} if the {@link Rollout#getStartAt()} is set
* and time of calling this method is beyond this point in time. This auto
* start mechanism is optional. Call {@link #startRollout(Long)} otherwise.
*
* For {@link RolloutStatus#STARTING} that means starting the first
* {@link RolloutGroup}s in line and when finished switch to
* {@link RolloutStatus#RUNNING}.
*
* For {@link RolloutStatus#RUNNING} that means checking to activate further
* groups based on the defined thresholds. Switched to
* {@link RolloutStatus#FINISHED} is all groups are finished.
*
* For {@link RolloutStatus#DELETING} that means either soft delete in case
* rollout was already {@link RolloutStatus#RUNNING} which results in status
* change {@link RolloutStatus#DELETED} or hard delete from the persistence
* otherwise.
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void checkRunningRollouts(long delayBetweenChecks);
void handleRollouts();
/**
* Checking Rollouts that are currently being created with asynchronous
* assignment of targets to the Rollout Groups.
*
* @param delayBetweenChecks
* the time in milliseconds of the delay between the further and
* this check. This check is only applied if the last check is
* less than (lastcheck-delay).
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void checkCreatingRollouts(long delayBetweenChecks);
/**
* Checking Rollouts that are currently being started with asynchronous
* creation of actions to the targets of a group.
*
* @param delayBetweenChecks
* the time in milliseconds of the delay between the further and
* this check. This check is only applied if the last check is
* less than (lastcheck-delay).
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void checkStartingRollouts(long delayBetweenChecks);
/**
* Checking Rollouts that are currently ready for an auto start.
*
* @param delayBetweenChecks
* the time in milliseconds of the delay between the further and
* this check. This check is only applied if the last check is
* less than (lastcheck-delay).
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void checkReadyRollouts(long delayBetweenChecks);
/**
* Counts all {@link Rollout}s in the repository.
* Counts all {@link Rollout}s in the repository that are not marked as
* deleted.
*
* @return number of roll outs
*/
@@ -212,44 +178,30 @@ public interface RolloutManagement {
ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(List<RolloutGroupCreate> groups,
String targetFilter, Long createdAt);
/**
* Can be called on a Rollout in {@link RolloutStatus#CREATING} to
* automatically fill it with targets.
*
* Works through all Rollout groups in {@link RolloutGroupStatus#CREATING}
* and fills them with remaining targets until the supposed amount of
* targets for the group is reached. Targets are added to a group when they
* match the overall {@link Rollout#getTargetFilterQuery()} and the
* {@link RolloutGroup#getTargetFilterQuery()} and not more than
* {@link RolloutGroup#getTargetPercentage()} are assigned to the group.
*
* @param rollout
* the rollout
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void fillRolloutGroupsWithTargets(@NotNull Long rollout);
/**
* Retrieves all rollouts.
*
* @param pageable
* the page request to sort and limit the result
* @param deleted
* flag if deleted rollouts should be included
* @return a page of found rollouts
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Page<Rollout> findAll(@NotNull Pageable pageable);
Page<Rollout> findAll(@NotNull Pageable pageable, boolean deleted);
/**
* Get count of targets in different status in rollout.
*
* @param pageable
* the page request to sort and limit the result
* @param deleted
* flag if deleted rollouts should be included
* @return a list of rollouts with details of targets count for different
* statuses
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Page<Rollout> findAllRolloutsWithDetailedStatus(@NotNull Pageable pageable);
Page<Rollout> findAllRolloutsWithDetailedStatus(@NotNull Pageable pageable, boolean deleted);
/**
* Retrieves all rollouts found by the given specification.
@@ -258,6 +210,8 @@ public interface RolloutManagement {
* the specification to filter rollouts
* @param pageable
* the page request to sort and limit the result
* @param deleted
* flag if deleted rollouts should be included
* @return a page of found rollouts
*
* @throws RSQLParameterUnsupportedFieldException
@@ -267,7 +221,7 @@ public interface RolloutManagement {
* if the RSQL syntax is wrong
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Page<Rollout> findAllByPredicate(@NotNull String rsqlParam, @NotNull Pageable pageable);
Page<Rollout> findAllByPredicate(@NotNull String rsqlParam, @NotNull Pageable pageable, boolean deleted);
/**
* Finds rollouts by given text in name or description.
@@ -276,11 +230,14 @@ public interface RolloutManagement {
* the page request to sort and limit the result
* @param searchText
* search text which matches name or description of rollout
* @param deleted
* flag if deleted rollouts should be included
* @return the founded rollout or {@code null} if rollout with given ID does
* not exists
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Slice<Rollout> findRolloutWithDetailedStatusByFilters(@NotNull Pageable pageable, @NotEmpty String searchText);
Slice<Rollout> findRolloutWithDetailedStatusByFilters(@NotNull Pageable pageable, @NotEmpty String searchText,
boolean deleted);
/**
* Retrieves a specific rollout by its ID.
@@ -309,6 +266,8 @@ public interface RolloutManagement {
*
* @param rolloutId
* rollout id
* @param deleted
* flag if deleted rollouts should be included
* @return rollout details of targets count for different statuses
*
*
@@ -417,4 +376,15 @@ public interface RolloutManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
Rollout updateRollout(@NotNull RolloutUpdate update);
/**
* Deletes a rollout. A rollout might be deleted asynchronously by
* indicating the rollout by {@link RolloutStatus#DELETING}
*
*
* @param rolloutId
* the ID of the rollout to be deleted
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void deleteRollout(long rolloutId);
}

View File

@@ -1,88 +0,0 @@
/**
* 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.repository;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Rollout Management properties.
*
*/
@ConfigurationProperties("hawkbit.rollout")
public class RolloutProperties {
// used by @Scheduled annotation which needs constant
public static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.rollout.scheduler.fixedDelay:30000}";
// used by @Scheduled annotation which needs constant
public static final String PROP_CREATING_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.rollout.creatingScheduler.fixedDelay:2000}";
// used by @Scheduled annotation which needs constant
public static final String PROP_STARTING_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.rollout.startingScheduler.fixedDelay:2000}";
// used by @Scheduled annotation which needs constant
public static final String PROP_READY_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.rollout.readyScheduler.fixedDelay:30000}";
/**
* Rollout scheduler configuration.
*/
public static class Scheduler {
/**
* Schedule where the rollout scheduler looks necessary state changes in
* milliseconds.
*/
private long fixedDelay;
private boolean enabled = true;
public Scheduler(final long fixedDelay) {
this.fixedDelay = fixedDelay;
}
public long getFixedDelay() {
return fixedDelay;
}
public void setFixedDelay(final long fixedDelay) {
this.fixedDelay = fixedDelay;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
}
private final Scheduler scheduler = new Scheduler(30000L);
private final Scheduler creatingScheduler = new Scheduler(2000L);
private final Scheduler startingScheduler = new Scheduler(2000L);
private final Scheduler readyScheduler = new Scheduler(30000L);
public Scheduler getScheduler() {
return scheduler;
}
public Scheduler getCreatingScheduler() {
return creatingScheduler;
}
public Scheduler getStartingScheduler() {
return startingScheduler;
}
public Scheduler getReadyScheduler() {
return readyScheduler;
}
}

View File

@@ -0,0 +1,46 @@
/**
* 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.repository.event.remote;
import org.eclipse.hawkbit.repository.event.remote.RemoteIdEvent;
import org.eclipse.hawkbit.repository.model.Rollout;
/**
*
* Defines the remote event of deleting a {@link Rollout}.
*/
public class RolloutDeletedEvent extends RemoteIdEvent {
private static final long serialVersionUID = 1L;
/**
* Default constructor.
*/
public RolloutDeletedEvent() {
// for serialization libs like jackson
}
/**
* Constructor for json serialization.
*
* @param tenant
* the tenant
* @param entityId
* the entity id
* @param entityClass
* the entity class
* @param applicationId
* the origin application id
*/
public RolloutDeletedEvent(final String tenant, final Long entityId, final String entityClass,
final String applicationId) {
super(entityId, tenant, entityClass, applicationId);
}
}

View File

@@ -0,0 +1,56 @@
/**
* 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.repository.event.remote.entity;
import org.eclipse.hawkbit.repository.model.Action;
/**
* Defines the remote event of creating a new {@link Action}.
*/
public abstract class AbstractActionEvent extends RemoteEntityEvent<Action> {
private static final long serialVersionUID = 1L;
private Long rolloutId;
private Long rolloutGroupId;
/**
* Default constructor.
*/
public AbstractActionEvent() {
// for serialization libs like jackson
}
/**
* Constructor
*
* @param action
* the created action
* @param rolloutId
* rollout identifier (optional)
* @param rolloutGroupId
* rollout group identifier (optional)
* @param applicationId
* the origin application id
*/
public AbstractActionEvent(final Action action, final Long rolloutId, final Long rolloutGroupId,
final String applicationId) {
super(action, applicationId);
this.rolloutId = rolloutId;
this.rolloutGroupId = rolloutGroupId;
}
public Long getRolloutId() {
return rolloutId;
}
public Long getRolloutGroupId() {
return rolloutGroupId;
}
}

View File

@@ -0,0 +1,40 @@
/**
* 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.repository.event.remote.entity;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
/**
* TenantAwareEvent definition which is been published in case a rollout group
* has been created for a specific rollout or updated.
*
*/
public abstract class AbstractRolloutGroupEvent extends RemoteEntityEvent<RolloutGroup> {
private static final long serialVersionUID = 1L;
private Long rolloutId;
/**
* Default constructor.
*/
public AbstractRolloutGroupEvent() {
// for serialization libs like jackson
}
public AbstractRolloutGroupEvent(final RolloutGroup rolloutGroup, final Long rolloutId,
final String applicationId) {
super(rolloutGroup, applicationId);
this.rolloutId = rolloutId;
}
public Long getRolloutId() {
return rolloutId;
}
}

View File

@@ -13,8 +13,8 @@ import org.eclipse.hawkbit.repository.model.Action;
/**
* Defines the remote event of creating a new {@link Action}.
*/
public class ActionCreatedEvent extends RemoteEntityEvent<Action> {
private static final long serialVersionUID = 1L;
public class ActionCreatedEvent extends AbstractActionEvent {
private static final long serialVersionUID = 2L;
/**
* Default constructor.
@@ -28,11 +28,16 @@ public class ActionCreatedEvent extends RemoteEntityEvent<Action> {
*
* @param action
* the created action
* @param rolloutId
* rollout identifier (optional)
* @param rolloutGroupId
* rollout group identifier (optional)
* @param applicationId
* the origin application id
*/
public ActionCreatedEvent(final Action action, final String applicationId) {
super(action, applicationId);
public ActionCreatedEvent(final Action action, final Long rolloutId, final Long rolloutGroupId,
final String applicationId) {
super(action, rolloutId, rolloutGroupId, applicationId);
}
}

View File

@@ -13,8 +13,8 @@ import org.eclipse.hawkbit.repository.model.Action;
/**
* Defines the remote event of updated a {@link Action}.
*/
public class ActionUpdatedEvent extends RemoteEntityEvent<Action> {
private static final long serialVersionUID = 1L;
public class ActionUpdatedEvent extends AbstractActionEvent {
private static final long serialVersionUID = 2L;
/**
* Default constructor.
@@ -28,11 +28,16 @@ public class ActionUpdatedEvent extends RemoteEntityEvent<Action> {
*
* @param action
* the updated action
* @param rolloutId
* rollout identifier (optional)
* @param rolloutGroupId
* rollout group identifier (optional)
* @param applicationId
* the origin application id
*/
public ActionUpdatedEvent(final Action action, final String applicationId) {
super(action, applicationId);
public ActionUpdatedEvent(final Action action, final Long rolloutId, final Long rolloutGroupId,
final String applicationId) {
super(action, rolloutId, rolloutGroupId, applicationId);
}
}

View File

@@ -15,12 +15,9 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
* has been created for a specific rollout.
*
*/
public class RolloutGroupCreatedEvent extends RemoteEntityEvent<RolloutGroup> {
public class RolloutGroupCreatedEvent extends AbstractRolloutGroupEvent {
private static final long serialVersionUID = 1L;
private Long rolloutId;
/**
* Default constructor.
*/
@@ -33,16 +30,12 @@ public class RolloutGroupCreatedEvent extends RemoteEntityEvent<RolloutGroup> {
*
* @param rolloutGroup
* the updated rolloutGroup
* @param rolloutId
* of the related rollout
* @param applicationId
* the origin application id
*/
public RolloutGroupCreatedEvent(final RolloutGroup rolloutGroup, final String applicationId) {
super(rolloutGroup, applicationId);
this.rolloutId = rolloutGroup.getRollout().getId();
public RolloutGroupCreatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId, final String applicationId) {
super(rolloutGroup, rolloutId, applicationId);
}
public Long getRolloutId() {
return rolloutId;
}
}

View File

@@ -13,9 +13,9 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
/**
* Defines the remote event of updated a {@link RolloutGroup}.
*/
public class RolloutGroupUpdatedEvent extends RemoteEntityEvent<RolloutGroup> {
public class RolloutGroupUpdatedEvent extends AbstractRolloutGroupEvent {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 2L;
/**
* Default constructor.
@@ -29,11 +29,13 @@ public class RolloutGroupUpdatedEvent extends RemoteEntityEvent<RolloutGroup> {
*
* @param rolloutGroup
* the updated rolloutGroup
* @param rolloutId
* of the related rollout
* @param applicationId
* the origin application id
*/
public RolloutGroupUpdatedEvent(final RolloutGroup rolloutGroup, final String applicationId) {
super(rolloutGroup, applicationId);
public RolloutGroupUpdatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId, final String applicationId) {
super(rolloutGroup, rolloutId, applicationId);
}
}

View File

@@ -18,9 +18,6 @@ import org.eclipse.hawkbit.exception.SpServerError;
*/
public final class ArtifactUploadFailedException extends AbstractServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**

View File

@@ -19,6 +19,10 @@ import org.springframework.hateoas.Identifiable;
*/
public interface BaseEntity extends Serializable, Identifiable<Long> {
static Long getIdOrNull(final BaseEntity entity) {
return entity == null ? null : entity.getId();
}
/**
* @return time in {@link TimeUnit#MILLISECONDS} when the {@link BaseEntity}
* was created.

View File

@@ -26,6 +26,12 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus.Status;
*/
public interface Rollout extends NamedEntity {
/**
* @return <code>true</code> if the rollout is deleted and only kept for
* history purposes.
*/
boolean isDeleted();
/**
* @return {@link DistributionSet} that is rolled out
*/
@@ -60,11 +66,11 @@ public interface Rollout extends NamedEntity {
long getForcedTime();
/**
* @return Timestamp when the rollout should be started automatically. Can be null.
* @return Timestamp when the rollout should be started automatically. Can
* be null.
*/
Long getStartAt();
/**
* @return number of {@link Target}s in this rollout.
*/
@@ -123,9 +129,22 @@ public interface Rollout extends NamedEntity {
*/
FINISHED,
/**
* Rollout is under deletion.
*/
DELETING,
/**
* Rollout has been deleted. This state is only set in case of a
* soft-deletion of the rollout which keeps references, in case of an
* hard-deletion of a rollout the rollout-entry itself is deleted.
*/
DELETED,
/**
* Rollout could not be created due to errors, might be a database
* problem during asynchronous creating.
*
* @deprecated legacy status is not used anymore
*/
@Deprecated
@@ -134,6 +153,7 @@ public interface Rollout extends NamedEntity {
/**
* Rollout could not be started due to errors, might be database problem
* during asynchronous starting.
*
* @deprecated legacy status is not used anymore
*/
@Deprecated

View File

@@ -15,6 +15,7 @@ import java.util.Optional;
import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.DistributionSetTagDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
@@ -91,12 +92,13 @@ public class EventType {
// download
TYPES.put(20, DownloadProgressEvent.class);
TYPES.put(21, SoftwareModuleCreatedEvent.class);
TYPES.put(22, SoftwareModuleDeletedEvent.class);
TYPES.put(23, SoftwareModuleUpdatedEvent.class);
TYPES.put(24, TargetPollEvent.class);
TYPES.put(25, RolloutDeletedEvent.class);
}
private int value;

View File

@@ -0,0 +1,175 @@
/**
* 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.repository;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.validation.ConstraintDeclarationException;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Core functionality for {@link RolloutManagement} implementations.
*
*/
public abstract class AbstractRolloutManagement implements RolloutManagement {
protected final TargetManagement targetManagement;
protected final DeploymentManagement deploymentManagement;
protected final RolloutGroupManagement rolloutGroupManagement;
protected final DistributionSetManagement distributionSetManagement;
protected final ApplicationContext context;
protected final ApplicationEventPublisher eventPublisher;
protected final VirtualPropertyReplacer virtualPropertyReplacer;
protected final PlatformTransactionManager txManager;
protected final TenantAware tenantAware;
protected final LockRegistry lockRegistry;
protected AbstractRolloutManagement(final TargetManagement targetManagement,
final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
final ApplicationEventPublisher eventPublisher, final VirtualPropertyReplacer virtualPropertyReplacer,
final PlatformTransactionManager txManager, final TenantAware tenantAware,
final LockRegistry lockRegistry) {
this.targetManagement = targetManagement;
this.deploymentManagement = deploymentManagement;
this.rolloutGroupManagement = rolloutGroupManagement;
this.distributionSetManagement = distributionSetManagement;
this.context = context;
this.eventPublisher = eventPublisher;
this.virtualPropertyReplacer = virtualPropertyReplacer;
this.txManager = txManager;
this.tenantAware = tenantAware;
this.lockRegistry = lockRegistry;
}
protected int runInNewTransaction(final String transactionName, final TransactionCallback<Integer> action) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName(transactionName);
def.setReadOnly(false);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
def.setIsolationLevel(Isolation.READ_UNCOMMITTED.value());
return new TransactionTemplate(txManager, def).execute(action);
}
protected RolloutGroupsValidation validateTargetsInGroups(final List<RolloutGroup> groups, final String baseFilter,
final long totalTargets) {
final List<Long> groupTargetCounts = new ArrayList<>(groups.size());
final Map<String, Long> targetFilterCounts = groups.stream()
.map(group -> RolloutHelper.getGroupTargetFilter(baseFilter, group)).distinct()
.collect(Collectors.toMap(Function.identity(), targetManagement::countTargetByTargetFilterQuery));
long unusedTargetsCount = 0;
for (int i = 0; i < groups.size(); i++) {
final RolloutGroup group = groups.get(i);
final String groupTargetFilter = RolloutHelper.getGroupTargetFilter(baseFilter, group);
RolloutHelper.verifyRolloutGroupTargetPercentage(group.getTargetPercentage());
final long targetsInGroupFilter = targetFilterCounts.get(groupTargetFilter);
final long overlappingTargets = countOverlappingTargetsWithPreviousGroups(baseFilter, groups, group, i,
targetFilterCounts);
final long realTargetsInGroup;
// Assume that targets which were not used in the previous groups
// are used in this group
if (overlappingTargets > 0 && unusedTargetsCount > 0) {
realTargetsInGroup = targetsInGroupFilter - overlappingTargets + unusedTargetsCount;
unusedTargetsCount = 0;
} else {
realTargetsInGroup = targetsInGroupFilter - overlappingTargets;
}
final long reducedTargetsInGroup = Math
.round(group.getTargetPercentage() / 100 * (double) realTargetsInGroup);
groupTargetCounts.add(reducedTargetsInGroup);
unusedTargetsCount += realTargetsInGroup - reducedTargetsInGroup;
}
return new RolloutGroupsValidation(totalTargets, groupTargetCounts);
}
private long countOverlappingTargetsWithPreviousGroups(final String baseFilter, final List<RolloutGroup> groups,
final RolloutGroup group, final int groupIndex, final Map<String, Long> targetFilterCounts) {
// there can't be overlapping targets in the first group
if (groupIndex == 0) {
return 0;
}
final List<RolloutGroup> previousGroups = groups.subList(0, groupIndex);
final String overlappingTargetsFilter = RolloutHelper.getOverlappingWithGroupsTargetFilter(baseFilter,
previousGroups, group);
if (targetFilterCounts.containsKey(overlappingTargetsFilter)) {
return targetFilterCounts.get(overlappingTargetsFilter);
} else {
final long overlappingTargets = targetManagement.countTargetByTargetFilterQuery(overlappingTargetsFilter);
targetFilterCounts.put(overlappingTargetsFilter, overlappingTargets);
return overlappingTargets;
}
}
protected long calculateRemainingTargets(final List<RolloutGroup> groups, final String targetFilter,
final Long createdAt) {
final String baseFilter = RolloutHelper.getTargetFilterQuery(targetFilter, createdAt);
final long totalTargets = targetManagement.countTargetByTargetFilterQuery(baseFilter);
if (totalTargets == 0) {
throw new ConstraintDeclarationException("Rollout target filter does not match any targets");
}
final RolloutGroupsValidation validation = validateTargetsInGroups(groups, baseFilter, totalTargets);
return totalTargets - validation.getTargetsInGroups();
}
@Override
@Async
public ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(final List<RolloutGroupCreate> groups,
final String targetFilter, final Long createdAt) {
final String baseFilter = RolloutHelper.getTargetFilterQuery(targetFilter, createdAt);
final long totalTargets = targetManagement.countTargetByTargetFilterQuery(baseFilter);
if (totalTargets == 0) {
throw new ConstraintDeclarationException("Rollout target filter does not match any targets");
}
return new AsyncResult<>(validateTargetsInGroups(
groups.stream().map(RolloutGroupCreate::build).collect(Collectors.toList()), baseFilter, totalTargets));
}
}

View File

@@ -6,34 +6,23 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa;
package org.eclipse.hawkbit.repository;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
import org.eclipse.hawkbit.repository.exception.ConstraintViolationException;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
/**
* A collection of static helper methods for the {@link JpaRolloutManagement}
* A collection of static helper methods for the {@link RolloutManagement}
*/
final class RolloutHelper {
public final class RolloutHelper {
private RolloutHelper() {
}
@@ -43,7 +32,7 @@ final class RolloutHelper {
* @param conditions
* input conditions and actions
*/
static void verifyRolloutGroupConditions(final RolloutGroupConditions conditions) {
public static void verifyRolloutGroupConditions(final RolloutGroupConditions conditions) {
if (conditions.getSuccessCondition() == null) {
throw new ConstraintViolationException("Rollout group is missing success condition");
}
@@ -60,7 +49,7 @@ final class RolloutHelper {
* the input group
* @return the verified group
*/
static RolloutGroup verifyRolloutGroupHasConditions(final RolloutGroup group) {
public static RolloutGroup verifyRolloutGroupHasConditions(final RolloutGroup group) {
if (group.getTargetPercentage() < 1F || group.getTargetPercentage() > 100F) {
throw new ConstraintViolationException("Target percentage has to be between 1 and 100");
}
@@ -74,55 +63,13 @@ final class RolloutHelper {
return group;
}
/**
* In case the given group is missing conditions or actions, they will be
* set from the supplied default conditions.
*
* @param create
* group to check
* @param conditions
* default conditions and actions
*/
static JpaRolloutGroup prepareRolloutGroupWithDefaultConditions(final RolloutGroupCreate create,
final RolloutGroupConditions conditions) {
final JpaRolloutGroup group = ((JpaRolloutGroupCreate) create).build();
if (group.getSuccessCondition() == null) {
group.setSuccessCondition(conditions.getSuccessCondition());
}
if (group.getSuccessConditionExp() == null) {
group.setSuccessConditionExp(conditions.getSuccessConditionExp());
}
if (group.getSuccessAction() == null) {
group.setSuccessAction(conditions.getSuccessAction());
}
if (group.getSuccessActionExp() == null) {
group.setSuccessActionExp(conditions.getSuccessActionExp());
}
if (group.getErrorCondition() == null) {
group.setErrorCondition(conditions.getErrorCondition());
}
if (group.getErrorConditionExp() == null) {
group.setErrorConditionExp(conditions.getErrorConditionExp());
}
if (group.getErrorAction() == null) {
group.setErrorAction(conditions.getErrorAction());
}
if (group.getErrorActionExp() == null) {
group.setErrorActionExp(conditions.getErrorActionExp());
}
return group;
}
/**
* Verify if the supplied amount of groups is in range
*
* @param amountGroup
* amount of groups
*/
static void verifyRolloutGroupParameter(final int amountGroup) {
public static void verifyRolloutGroupParameter(final int amountGroup) {
if (amountGroup <= 0) {
throw new ConstraintViolationException("the amountGroup must be greater than zero");
} else if (amountGroup > 500) {
@@ -136,7 +83,7 @@ final class RolloutHelper {
* @param percentage
* the percentage
*/
static void verifyRolloutGroupTargetPercentage(final float percentage) {
public static void verifyRolloutGroupTargetPercentage(final float percentage) {
if (percentage <= 0) {
throw new ConstraintViolationException("the percentage must be greater than zero");
} else if (percentage > 100) {
@@ -152,7 +99,7 @@ final class RolloutHelper {
* Rollout to derive the filter from
* @return resulting target filter query
*/
static String getTargetFilterQuery(final Rollout rollout) {
public static String getTargetFilterQuery(final Rollout rollout) {
return getTargetFilterQuery(rollout.getTargetFilterQuery(), rollout.getCreatedAt());
}
@@ -164,7 +111,7 @@ final class RolloutHelper {
* @return a target filter query that only matches targets that were created
* after the provided timestamp.
*/
static String getTargetFilterQuery(final String targetFilter, final Long createdAt) {
public static String getTargetFilterQuery(final String targetFilter, final Long createdAt) {
if (createdAt != null) {
return targetFilter + ";createdat=le=" + createdAt.toString();
}
@@ -179,7 +126,7 @@ final class RolloutHelper {
* @param status
* the Status
*/
static void verifyRolloutInStatus(final Rollout rollout, final Rollout.RolloutStatus status) {
public static void verifyRolloutInStatus(final Rollout rollout, final Rollout.RolloutStatus status) {
if (!rollout.getStatus().equals(status)) {
throw new RolloutIllegalStateException("Rollout is not in status " + status.toString());
}
@@ -197,7 +144,7 @@ final class RolloutHelper {
* the group to add
* @return list of groups
*/
static List<Long> getGroupsByStatusIncludingGroup(final Rollout rollout,
public static List<Long> getGroupsByStatusIncludingGroup(final Rollout rollout,
final RolloutGroup.RolloutGroupStatus status, final RolloutGroup group) {
return rollout.getRolloutGroups().stream()
.filter(innerGroup -> innerGroup.getStatus().equals(status) || innerGroup.equals(group))
@@ -211,7 +158,7 @@ final class RolloutHelper {
* the rollout
* @return ordered list of groups
*/
static List<RolloutGroup> getOrderedGroups(final Rollout rollout) {
public static List<RolloutGroup> getOrderedGroups(final Rollout rollout) {
return rollout.getRolloutGroups().stream().sorted((group1, group2) -> {
if (group1.getId() < group2.getId()) {
return -1;
@@ -232,7 +179,7 @@ final class RolloutHelper {
* @return RSQL string without base filter of the Rollout. Can be an empty
* string.
*/
static String getAllGroupsTargetFilter(final List<RolloutGroup> groups) {
public static String getAllGroupsTargetFilter(final List<RolloutGroup> groups) {
if (groups.stream().anyMatch(group -> StringUtils.isEmpty(group.getTargetFilterQuery()))) {
return "";
}
@@ -254,7 +201,7 @@ final class RolloutHelper {
* @return RSQL string without base filter of the Rollout. Can be an empty
* string.
*/
static String getOverlappingWithGroupsTargetFilter(final String baseFilter, final List<RolloutGroup> groups,
public static String getOverlappingWithGroupsTargetFilter(final String baseFilter, final List<RolloutGroup> groups,
final RolloutGroup group) {
final String groupFilter = group.getTargetFilterQuery();
// when any previous group has the same filter as the target group the
@@ -283,7 +230,7 @@ final class RolloutHelper {
&& prevGroup.getTargetFilterQuery().equals(groupFilter));
}
private static String concatAndTargetFilters(String... filters) {
private static String concatAndTargetFilters(final String... filters) {
return "(" + Arrays.stream(filters).collect(Collectors.joining(");(")) + ")";
}
@@ -308,7 +255,7 @@ final class RolloutHelper {
* @param targetCount
* the count of left targets
*/
static void verifyRemainingTargets(final long targetCount) {
public static void verifyRemainingTargets(final long targetCount) {
if (targetCount > 0) {
throw new ConstraintViolationException(
"Rollout groups don't match all targets that are targeted by the rollout");
@@ -318,35 +265,11 @@ final class RolloutHelper {
}
}
/**
* @param searchText
* search string
* @return criteria specification with a query for name or description of a
* rollout
*/
static Specification<JpaRollout> likeNameOrDescription(final String searchText) {
return (rolloutRoot, query, criteriaBuilder) -> {
final String searchTextToLower = searchText.toLowerCase();
return criteriaBuilder.or(
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(JpaRollout_.name)), searchTextToLower),
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(JpaRollout_.description)),
searchTextToLower));
};
}
static void checkIfRolloutCanStarted(final Rollout rollout, final Rollout mergedRollout) {
public static void checkIfRolloutCanStarted(final Rollout rollout, final Rollout mergedRollout) {
if (!(Rollout.RolloutStatus.READY.equals(mergedRollout.getStatus()))) {
throw new RolloutIllegalStateException("Rollout can only be started in state ready but current state is "
+ rollout.getStatus().name().toLowerCase());
}
}
static Page<Rollout> convertPage(final Page<JpaRollout> findAll, final Pageable pageable) {
return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements());
}
static Slice<Rollout> convertPage(final Slice<JpaRollout> findAll, final Pageable pageable) {
return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, 0);
}
}

View File

@@ -195,6 +195,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
* the status which the actions should not have
* @return the found list of {@link Action}s
*/
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
@Query("SELECT a FROM JpaAction a WHERE a.active = true AND a.distributionSet.requiredMigrationStep = false AND a.target IN ?1 AND a.status != ?2")
List<JpaAction> findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(
Collection<Long> targetIds, Action.Status notStatus);
@@ -264,6 +265,44 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
*/
Long countByRolloutIdAndRolloutGroupIdAndStatus(Long rolloutId, Long rolloutGroupId, Action.Status status);
/**
* Counts all actions referring to a given rollout and status.
*
* @param rolloutId
* the ID of the rollout the actions belong to
* @param status
* the status the actions should have
* @return the count of actions referring to a rollout and are in a given
* status
*/
Long countByRolloutIdAndStatus(Long rolloutId, Action.Status status);
/**
* Returns {@code true} if actions for the given rollout exists, otherwise
* {@code false}
*
* @param rolloutId
* the ID of the rollout the actions belong to
* @return {@code true} if actions for the given rollout exists, otherwise
* {@code false}
*/
@Query("SELECT CASE WHEN COUNT(a)>0 THEN 'true' ELSE 'false' END FROM JpaAction a WHERE a.rollout.id=:rolloutId")
boolean existsByRolloutId(@Param("rolloutId") Long rolloutId);
/**
* Returns {@code true} if actions for the given rollout exists, otherwise
* {@code false}
*
* @param rolloutId
* the ID of the rollout the actions belong to
* @param status
* the action is not to be in
* @return {@code true} if actions for the given rollout exists, otherwise
* {@code false}
*/
@Query("SELECT CASE WHEN COUNT(a)>0 THEN 'true' ELSE 'false' END FROM JpaAction a WHERE a.rollout.id=:rolloutId AND a.status != :status")
boolean existsByRolloutIdAndStatusNotIn(@Param("rolloutId") Long rolloutId, @Param("status") Status status);
/**
* Retrieving all actions referring to a given rollout with a specific
* action as parent reference and a specific status.
@@ -281,6 +320,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
* @return the actions referring a specific rollout and a specific parent
* rolloutgroup in a specific status
*/
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
Page<Action> findByRolloutAndRolloutGroupParentAndStatus(Pageable pageable, JpaRollout rollout,
JpaRolloutGroup rolloutGroupParent, Status actionStatus);
@@ -296,19 +336,22 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
* @return the actions referring a specific rollout and a specific parent
* rolloutgroup in a specific status
*/
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
Page<Action> findByRolloutAndRolloutGroupParentIsNullAndStatus(Pageable pageable, JpaRollout rollout,
Status actionStatus);
/**
* Retrieves all actions for a specific rollout and in a specific status.
*
*
* @param pageable
* page parameters
* @param rolloutId
* the rollout the actions beglong to
* @param actionStatus
* the status of the actions
* @return the actions referring a specific rollout an in a specific status
*/
List<Action> findByRolloutIdAndStatus(Long rolloutId, Status actionStatus);
Page<JpaAction> findByRolloutIdAndStatus(Pageable pageable, Long rolloutId, Status actionStatus);
/**
* Get list of objects which has details of status and count of targets in
@@ -354,4 +397,15 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rolloutGroup.id, a.status , COUNT(a.target)) FROM JpaAction a WHERE a.rolloutGroup.id IN ?1 GROUP BY a.rolloutGroup.id, a.status")
List<TotalTargetCountActionStatus> getStatusCountByRolloutGroupId(List<Long> rolloutGroupId);
/**
* Deletes all actions with the given IDs.
*
* @param actionIDs
* the IDs of the actions to be deleted.
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("DELETE FROM JpaAction a WHERE a.id IN ?1")
void deleteByIdIn(final Collection<Long> actionIDs);
}

View File

@@ -12,7 +12,6 @@ import java.util.Collection;
import java.util.List;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
@@ -72,11 +71,11 @@ public interface DistributionSetRepository
* Finds {@link DistributionSet}s where given {@link SoftwareModule} is
* assigned.
*
* @param module
* @param moduleId
* to search for
* @return {@link List} of found {@link DistributionSet}s
*/
Long countByModules(JpaSoftwareModule module);
Long countByModulesId(Long moduleId);
/**
* Finds {@link DistributionSet}s based on given ID that are assigned yet to

View File

@@ -17,7 +17,6 @@ import org.eclipse.hawkbit.artifact.repository.HashNotMatchException;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.exception.ArtifactBinaryNotFoundException;
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
@@ -104,23 +103,16 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public boolean clearArtifactBinary(final Long artifactId) {
return clearArtifactBinary(localArtifactRepository.findById(artifactId)
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, artifactId)));
}
public boolean clearArtifactBinary(final String sha1Hash, final Long moduleId) {
private boolean clearArtifactBinary(final JpaArtifact existing) {
for (final Artifact lArtifact : localArtifactRepository.findBySha1Hash(existing.getSha1Hash())) {
if (!lArtifact.getSoftwareModule().isDeleted()
&& Long.compare(lArtifact.getSoftwareModule().getId(), existing.getSoftwareModule().getId()) != 0) {
return false;
}
if (localArtifactRepository.existsWithSha1HashAndSoftwareModuleIdIsNot(sha1Hash, moduleId)) {
// there are still other artifacts that need the binary
return false;
}
try {
LOG.debug("deleting artifact from repository {}", existing.getSha1Hash());
artifactRepository.deleteBySha1(existing.getSha1Hash());
LOG.debug("deleting artifact from repository {}", sha1Hash);
artifactRepository.deleteBySha1(sha1Hash);
return true;
} catch (final ArtifactStoreException e) {
throw new ArtifactDeleteFailedException(e);
@@ -134,7 +126,7 @@ public class JpaArtifactManagement implements ArtifactManagement {
final JpaArtifact existing = (JpaArtifact) findArtifact(id)
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, id));
clearArtifactBinary(existing);
clearArtifactBinary(existing.getSha1Hash(), existing.getSoftwareModule().getId());
((JpaSoftwareModule) existing.getSoftwareModule()).removeArtifact(existing);
softwareModuleRepository.save((JpaSoftwareModule) existing.getSoftwareModule());
@@ -148,6 +140,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override
public Optional<Artifact> findByFilenameAndSoftwareModule(final String filename, final Long softwareModuleId) {
throwExceptionIfSoftwareModuleDoesNotExist(softwareModuleId);
return localArtifactRepository.findFirstByFilenameAndSoftwareModuleId(filename, softwareModuleId);
}
@@ -163,13 +157,20 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override
public Page<Artifact> findArtifactBySoftwareModule(final Pageable pageReq, final Long swId) {
throwExceptionIfSoftwareModuleDoesNotExist(swId);
return localArtifactRepository.findBySoftwareModuleId(pageReq, swId);
}
private void throwExceptionIfSoftwareModuleDoesNotExist(final Long swId) {
if (!softwareModuleRepository.exists(swId)) {
throw new EntityNotFoundException(SoftwareModule.class, swId);
}
}
@Override
public DbArtifact loadArtifactBinary(final String sha1Hash) {
return Optional.ofNullable(artifactRepository.getArtifactBySha1(sha1Hash))
.orElseThrow(() -> new ArtifactBinaryNotFoundException(sha1Hash));
public Optional<DbArtifact> loadArtifactBinary(final String sha1Hash) {
return Optional.ofNullable(artifactRepository.getArtifactBySha1(sha1Hash));
}
private Artifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename,

View File

@@ -46,6 +46,7 @@ import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
@@ -85,6 +86,9 @@ public class JpaControllerManagement implements ControllerManagement {
@Autowired
private TargetRepository targetRepository;
@Autowired
private SoftwareModuleRepository softwareModuleRepository;
@Autowired
private TargetManagement targetManagement;
@@ -140,6 +144,9 @@ public class JpaControllerManagement implements ControllerManagement {
@Override
public Optional<Action> getActionForDownloadByTargetAndSoftwareModule(final String controllerId,
final Long moduleId) {
throwExceptionIfTargetDoesNotExist(controllerId);
throwExceptionIfSoftwareModuleDoesNotExist(moduleId);
final List<Action> action = actionRepository.findActionByTargetAndSoftwareModule(controllerId, moduleId);
if (action.isEmpty() || action.get(0).isCancelingOrCanceled()) {
@@ -149,26 +156,40 @@ public class JpaControllerManagement implements ControllerManagement {
return Optional.ofNullable(action.get(0));
}
private void throwExceptionIfTargetDoesNotExist(final String controllerId) {
if (!targetRepository.existsByControllerId(controllerId)) {
throw new EntityNotFoundException(Target.class, controllerId);
}
}
private void throwExceptionIfTargetDoesNotExist(final Long targetId) {
if (!targetRepository.exists(targetId)) {
throw new EntityNotFoundException(Target.class, targetId);
}
}
private void throwExceptionIfSoftwareModuleDoesNotExist(final Long moduleId) {
if (!softwareModuleRepository.exists(moduleId)) {
throw new EntityNotFoundException(SoftwareModule.class, moduleId);
}
}
@Override
public boolean hasTargetArtifactAssigned(final String controllerId, final String sha1Hash) {
final Optional<Target> target = targetRepository.findByControllerId(controllerId);
if (!target.isPresent()) {
return false;
}
return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target.get(), sha1Hash)) > 0;
throwExceptionIfTargetDoesNotExist(controllerId);
return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(controllerId, sha1Hash)) > 0;
}
@Override
public boolean hasTargetArtifactAssigned(final Long targetId, final String sha1Hash) {
final Target target = targetRepository.findOne(targetId);
if (target == null) {
return false;
}
return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target, sha1Hash)) > 0;
throwExceptionIfTargetDoesNotExist(targetId);
return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(targetId, sha1Hash)) > 0;
}
@Override
public Optional<Action> findOldestActiveActionByTarget(final String controllerId) {
throwExceptionIfTargetDoesNotExist(controllerId);
// used in favorite to findFirstByTargetAndActiveOrderByIdAsc due to
// DATAJPA-841 issue.
return actionRepository.findFirstByTargetControllerIdAndActive(new Sort(Direction.ASC, "id"), controllerId,

View File

@@ -225,7 +225,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
final List<JpaTarget> targets = Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream()
.map(ids -> targetRepository
.findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, set.getId())))
.flatMap(t -> t.stream()).collect(Collectors.toList());
.flatMap(List::stream).collect(Collectors.toList());
if (targets.isEmpty()) {
// detaching as it is not necessary to persist the set itself
@@ -456,6 +456,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
final RolloutGroup rolloutGroupParent, final int limit) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("startScheduledActions");
def.setReadOnly(false);
def.setIsolationLevel(Isolation.READ_UNCOMMITTED.value());
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
return new TransactionTemplate(txManager, def).execute(status -> {
final Page<Action> rolloutGroupActions = findActionsByRolloutAndRolloutGroupParent(rollout,
@@ -599,24 +601,38 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override
public List<Action> findActiveActionsByTarget(final String controllerId) {
throwExceptionIfTargetFoesNotExist(controllerId);
return actionRepository.findByActiveAndTarget(controllerId, true);
}
@Override
public List<Action> findInActiveActionsByTarget(final String controllerId) {
throwExceptionIfTargetFoesNotExist(controllerId);
return actionRepository.findByActiveAndTarget(controllerId, false);
}
@Override
public Long countActionsByTarget(final String controllerId) {
throwExceptionIfTargetFoesNotExist(controllerId);
return actionRepository.countByTargetControllerId(controllerId);
}
@Override
public Long countActionsByTarget(final String rsqlParam, final String controllerId) {
throwExceptionIfTargetFoesNotExist(controllerId);
return actionRepository.count(createSpecificationFor(controllerId, rsqlParam));
}
private void throwExceptionIfTargetFoesNotExist(final String controllerId) {
if (!targetRepository.existsByControllerId(controllerId)) {
throw new EntityNotFoundException(Target.class, controllerId);
}
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)

View File

@@ -38,6 +38,7 @@ import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup_;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target;
@@ -48,7 +49,6 @@ import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Isolation;
@@ -65,6 +65,9 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
@Autowired
private RolloutGroupRepository rolloutGroupRepository;
@Autowired
private RolloutRepository rolloutRepository;
@Autowired
private ActionRepository actionRepository;
@@ -115,9 +118,19 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
@Override
public Page<RolloutGroup> findAllRolloutGroupsWithDetailedStatus(final Long rolloutId, final Pageable pageable) {
if (!rolloutRepository.exists(rolloutId)) {
throw new EntityNotFoundException(Rollout.class, rolloutId);
}
final Page<JpaRolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutId(rolloutId, pageable);
final List<Long> rolloutGroupIds = rolloutGroups.getContent().stream().map(rollout -> rollout.getId())
final List<Long> rolloutGroupIds = rolloutGroups.getContent().stream().map(RolloutGroup::getId)
.collect(Collectors.toList());
if (rolloutGroupIds.isEmpty()) {
// groups might already deleted, so return empty list.
return new PageImpl<>(Collections.emptyList());
}
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRolloutGroup(
rolloutGroupIds);
@@ -192,8 +205,9 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
}
@Override
public Page<TargetWithActionStatus> findAllTargetsWithActionStatus(final PageRequest pageRequest,
public Page<TargetWithActionStatus> findAllTargetsWithActionStatus(final Pageable pageRequest,
final Long rolloutGroupId) {
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
@@ -218,11 +232,14 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
.setFirstResult(pageRequest.getOffset()).setMaxResults(pageRequest.getPageSize()).getResultList()
.stream().map(o -> new TargetWithActionStatus((Target) o[0], (Action.Status) o[1]))
.collect(Collectors.toList());
return new PageImpl<>(targetWithActionStatus, pageRequest, totalCount);
}
@Override
public Long countTargetsOfRolloutsGroup(@NotNull final Long rolloutGroupId) {
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
final Root<RolloutTargetGroup> countQueryFrom = countQuery.from(RolloutTargetGroup.class);
@@ -231,4 +248,10 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
return entityManager.createQuery(countQuery).getSingleResult();
}
private void throwExceptionIfRolloutGroupDoesNotExist(final Long rolloutGroupId) {
if (!rolloutGroupRepository.exists(rolloutGroupId)) {
throw new EntityNotFoundException(RolloutGroup.class, rolloutGroupId);
}
}
}

View File

@@ -0,0 +1,106 @@
/**
* 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.repository.jpa;
import java.util.Collections;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
/**
* A collection of static helper methods for the {@link JpaRolloutManagement}
*/
final class JpaRolloutHelper {
private JpaRolloutHelper() {
}
/**
* In case the given group is missing conditions or actions, they will be
* set from the supplied default conditions.
*
* @param create
* group to check
* @param conditions
* default conditions and actions
*/
static JpaRolloutGroup prepareRolloutGroupWithDefaultConditions(final RolloutGroupCreate create,
final RolloutGroupConditions conditions) {
final JpaRolloutGroup group = ((JpaRolloutGroupCreate) create).build();
if (group.getSuccessCondition() == null) {
group.setSuccessCondition(conditions.getSuccessCondition());
}
if (group.getSuccessConditionExp() == null) {
group.setSuccessConditionExp(conditions.getSuccessConditionExp());
}
if (group.getSuccessAction() == null) {
group.setSuccessAction(conditions.getSuccessAction());
}
if (group.getSuccessActionExp() == null) {
group.setSuccessActionExp(conditions.getSuccessActionExp());
}
if (group.getErrorCondition() == null) {
group.setErrorCondition(conditions.getErrorCondition());
}
if (group.getErrorConditionExp() == null) {
group.setErrorConditionExp(conditions.getErrorConditionExp());
}
if (group.getErrorAction() == null) {
group.setErrorAction(conditions.getErrorAction());
}
if (group.getErrorActionExp() == null) {
group.setErrorActionExp(conditions.getErrorActionExp());
}
return group;
}
/**
* Builds a {@link Specification} to search a rollout by name or
* description.
*
* @param searchText
* search string
* @param isDeleted
* <code>true</code> if deleted rollouts should be included in
* the search. Otherwise <code>false</code>
* @return criteria specification with a query for name or description of a
* rollout
*/
static Specification<JpaRollout> likeNameOrDescription(final String searchText, final boolean isDeleted) {
return (rolloutRoot, query, criteriaBuilder) -> {
final String searchTextToLower = searchText.toLowerCase();
return criteriaBuilder.and(criteriaBuilder.or(
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(JpaRollout_.name)), searchTextToLower),
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(JpaRollout_.description)),
searchTextToLower)),
criteriaBuilder.equal(rolloutRoot.get(JpaRollout_.deleted), isDeleted));
};
}
static Page<Rollout> convertPage(final Page<JpaRollout> findAll, final Pageable pageable) {
return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements());
}
static Slice<Rollout> convertPage(final Slice<JpaRollout> findAll, final Pageable pageable) {
return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, 0);
}
}

View File

@@ -8,22 +8,23 @@
*/
package org.eclipse.hawkbit.repository.jpa;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.validation.ConstraintDeclarationException;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.repository.AbstractRolloutManagement;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.RolloutFields;
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutHelper;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.builder.GenericRolloutUpdate;
@@ -31,6 +32,7 @@ import org.eclipse.hawkbit.repository.builder.RolloutCreate;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
import org.eclipse.hawkbit.repository.builder.RolloutUpdate;
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.ConstraintViolationException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -43,6 +45,8 @@ import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupActionEvaluator;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupConditionEvaluator;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -58,7 +62,9 @@ import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
@@ -71,87 +77,87 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.validation.annotation.Validated;
import com.google.common.collect.Lists;
/**
* JPA implementation of {@link RolloutManagement}.
*/
@Validated
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public class JpaRolloutManagement implements RolloutManagement {
private static final Logger LOGGER = LoggerFactory.getLogger(RolloutManagement.class);
public class JpaRolloutManagement extends AbstractRolloutManagement {
private static final Logger LOGGER = LoggerFactory.getLogger(JpaRolloutManagement.class);
/**
* Max amount of targets that are handled in one transaction.
*/
private static final int TRANSACTION_TARGETS = 1000;
private static final int TRANSACTION_TARGETS = 5_000;
/**
* Maximum amount of actions that are deleted in one transaction.
*/
private static final int TRANSACTION_ACTIONS = 5_000;
@Autowired
private RolloutRepository rolloutRepository;
@Autowired
private TargetManagement targetManagement;
@Autowired
private RolloutGroupRepository rolloutGroupRepository;
@Autowired
private DeploymentManagement deploymentManagement;
@Autowired
private RolloutTargetGroupRepository rolloutTargetGroupRepository;
@Autowired
private RolloutGroupManagement rolloutGroupManagement;
@Autowired
private DistributionSetManagement distributionSetManagement;
@Autowired
private ActionRepository actionRepository;
@Autowired
private ApplicationContext context;
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private NoCountPagingRepository criteriaNoCountDao;
@Autowired
private PlatformTransactionManager txManager;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Autowired
private AfterTransactionCommitExecutor afterCommit;
@Override
public Page<Rollout> findAll(final Pageable pageable) {
return RolloutHelper.convertPage(rolloutRepository.findAll(pageable), pageable);
JpaRolloutManagement(final TargetManagement targetManagement, final DeploymentManagement deploymentManagement,
final RolloutGroupManagement rolloutGroupManagement,
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
final ApplicationEventPublisher eventPublisher, final VirtualPropertyReplacer virtualPropertyReplacer,
final PlatformTransactionManager txManager, final TenantAware tenantAware,
final LockRegistry lockRegistry) {
super(targetManagement, deploymentManagement, rolloutGroupManagement, distributionSetManagement, context,
eventPublisher, virtualPropertyReplacer, txManager, tenantAware, lockRegistry);
}
@Override
public Page<Rollout> findAllByPredicate(final String rsqlParam, final Pageable pageable) {
final Specification<JpaRollout> specification = RSQLUtility.parse(rsqlParam, RolloutFields.class,
virtualPropertyReplacer);
public Page<Rollout> findAll(final Pageable pageable, final boolean deleted) {
final Specification<JpaRollout> spec = RolloutSpecification.isDeleted(deleted);
return JpaRolloutHelper.convertPage(rolloutRepository.findAll(spec, pageable), pageable);
}
final Page<JpaRollout> findAll = rolloutRepository.findAll(specification, pageable);
return RolloutHelper.convertPage(findAll, pageable);
@Override
public Page<Rollout> findAllByPredicate(final String rsqlParam, final Pageable pageable, final boolean deleted) {
final List<Specification<JpaRollout>> specList = Lists.newArrayListWithExpectedSize(2);
specList.add(RSQLUtility.parse(rsqlParam, RolloutFields.class, virtualPropertyReplacer));
specList.add(RolloutSpecification.isDeleted(deleted));
return JpaRolloutHelper.convertPage(findByCriteriaAPI(pageable, specList), pageable);
}
/**
* Executes findAll with the given {@link Rollout} {@link Specification}s.
*/
private Page<JpaRollout> findByCriteriaAPI(final Pageable pageable,
final List<Specification<JpaRollout>> specList) {
if (specList == null || specList.isEmpty()) {
return rolloutRepository.findAll(pageable);
}
return rolloutRepository.findAll(SpecificationsBuilder.combineWithAnd(specList), pageable);
}
@Override
@@ -225,8 +231,7 @@ public class JpaRolloutManagement implements RolloutManagement {
group.setTargetPercentage(1.0F / (amountOfGroups - i) * 100);
lastSavedGroup = rolloutGroupRepository.save(group);
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup);
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup, rollout);
}
savedRollout.setRolloutGroupsCreated(amountOfGroups);
@@ -240,7 +245,7 @@ public class JpaRolloutManagement implements RolloutManagement {
// Preparing the groups
final List<RolloutGroup> groups = groupList.stream()
.map(group -> RolloutHelper.prepareRolloutGroupWithDefaultConditions(group, conditions))
.map(group -> JpaRolloutHelper.prepareRolloutGroupWithDefaultConditions(group, conditions))
.collect(Collectors.toList());
groups.forEach(RolloutHelper::verifyRolloutGroupHasConditions);
@@ -261,7 +266,7 @@ public class JpaRolloutManagement implements RolloutManagement {
if (srcGroup.getTargetFilterQuery() != null) {
group.setTargetFilterQuery(srcGroup.getTargetFilterQuery());
} else {
group.setTargetFilterQuery("");
group.setTargetFilterQuery(StringUtils.EMPTY);
}
group.setSuccessCondition(srcGroup.getSuccessCondition());
@@ -277,25 +282,20 @@ public class JpaRolloutManagement implements RolloutManagement {
group.setErrorActionExp(srcGroup.getErrorActionExp());
lastSavedGroup = rolloutGroupRepository.save(group);
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup);
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup, rollout);
}
savedRollout.setRolloutGroupsCreated(groups.size());
return rolloutRepository.save(savedRollout);
}
private void publishRolloutGroupCreatedEventAfterCommit(final RolloutGroup group) {
afterCommit
.afterCommit(() -> eventPublisher.publishEvent(new RolloutGroupCreatedEvent(group, context.getId())));
private void publishRolloutGroupCreatedEventAfterCommit(final RolloutGroup group, final Rollout rollout) {
afterCommit.afterCommit(() -> eventPublisher
.publishEvent(new RolloutGroupCreatedEvent(group, rollout.getId(), context.getId())));
}
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public void fillRolloutGroupsWithTargets(final Long rolloutId) {
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.CREATING);
private void handleCreateRollout(final JpaRollout rollout) {
LOGGER.debug("handleCreateRollout called for rollout {}", rollout.getId());
final List<RolloutGroup> rolloutGroups = RolloutHelper.getOrderedGroups(rollout);
int readyGroups = 0;
@@ -317,6 +317,7 @@ public class JpaRolloutManagement implements RolloutManagement {
// When all groups are ready the rollout status can be changed to be
// ready, too.
if (readyGroups == rolloutGroups.size()) {
LOGGER.debug("rollout {} creatin done. Switch to READY.", rollout.getId());
rollout.setStatus(RolloutStatus.READY);
rollout.setLastCheck(0);
rollout.setTotalTargets(totalTargets);
@@ -372,17 +373,10 @@ public class JpaRolloutManagement implements RolloutManagement {
}
}
private int runInNewCountingTransaction(final String transactionName, final TransactionCallback<Integer> action) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName(transactionName);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
return new TransactionTemplate(txManager, def).execute(action);
}
private Integer assignTargetsToGroupInNewTransaction(final Rollout rollout, final RolloutGroup group,
final String targetFilter, final long limit) {
return runInNewCountingTransaction("assignTargetsToRolloutGroup", status -> {
return runInNewTransaction("assignTargetsToRolloutGroup", status -> {
final PageRequest pageRequest = new PageRequest(0, Math.toIntExact(limit));
final List<Long> readyGroups = RolloutHelper.getGroupsByStatusIncludingGroup(rollout,
RolloutGroupStatus.READY, group);
@@ -399,19 +393,6 @@ public class JpaRolloutManagement implements RolloutManagement {
targets.forEach(target -> rolloutTargetGroupRepository.save(new RolloutTargetGroup(group, target)));
}
private long calculateRemainingTargets(final List<RolloutGroup> groups, final String targetFilter,
final Long createdAt) {
final String baseFilter = RolloutHelper.getTargetFilterQuery(targetFilter, createdAt);
final long totalTargets = targetManagement.countTargetByTargetFilterQuery(baseFilter);
if (totalTargets == 0) {
throw new ConstraintDeclarationException("Rollout target filter does not match any targets");
}
final RolloutGroupsValidation validation = validateTargetsInGroups(groups, baseFilter, totalTargets);
return totalTargets - validation.getTargetsInGroups();
}
@Override
@Async
public ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(final List<RolloutGroupCreate> groups,
@@ -427,67 +408,12 @@ public class JpaRolloutManagement implements RolloutManagement {
groups.stream().map(RolloutGroupCreate::build).collect(Collectors.toList()), baseFilter, totalTargets));
}
private RolloutGroupsValidation validateTargetsInGroups(final List<RolloutGroup> groups, final String baseFilter,
final long totalTargets) {
final List<Long> groupTargetCounts = new ArrayList<>(groups.size());
final Map<String, Long> targetFilterCounts = groups.stream()
.map(group -> RolloutHelper.getGroupTargetFilter(baseFilter, group)).distinct()
.collect(Collectors.toMap(Function.identity(), targetManagement::countTargetByTargetFilterQuery));
long unusedTargetsCount = 0;
for (int i = 0; i < groups.size(); i++) {
final RolloutGroup group = groups.get(i);
final String groupTargetFilter = RolloutHelper.getGroupTargetFilter(baseFilter, group);
RolloutHelper.verifyRolloutGroupTargetPercentage(group.getTargetPercentage());
final long targetsInGroupFilter = targetFilterCounts.get(groupTargetFilter);
final long overlappingTargets = countOverlappingTargetsWithPreviousGroups(baseFilter, groups, group, i,
targetFilterCounts);
final long realTargetsInGroup;
// Assume that targets which were not used in the previous groups
// are used in this group
if (overlappingTargets > 0 && unusedTargetsCount > 0) {
realTargetsInGroup = targetsInGroupFilter - overlappingTargets + unusedTargetsCount;
unusedTargetsCount = 0;
} else {
realTargetsInGroup = targetsInGroupFilter - overlappingTargets;
}
final long reducedTargetsInGroup = Math
.round(group.getTargetPercentage() / 100 * (double) realTargetsInGroup);
groupTargetCounts.add(reducedTargetsInGroup);
unusedTargetsCount += realTargetsInGroup - reducedTargetsInGroup;
}
return new RolloutGroupsValidation(totalTargets, groupTargetCounts);
}
private long countOverlappingTargetsWithPreviousGroups(final String baseFilter, final List<RolloutGroup> groups,
final RolloutGroup group, final int groupIndex, final Map<String, Long> targetFilterCounts) {
// there can't be overlapping targets in the first group
if (groupIndex == 0) {
return 0;
}
final List<RolloutGroup> previousGroups = groups.subList(0, groupIndex);
final String overlappingTargetsFilter = RolloutHelper.getOverlappingWithGroupsTargetFilter(baseFilter,
previousGroups, group);
if (targetFilterCounts.containsKey(overlappingTargetsFilter)) {
return targetFilterCounts.get(overlappingTargetsFilter);
} else {
final long overlappingTargets = targetManagement.countTargetByTargetFilterQuery(overlappingTargetsFilter);
targetFilterCounts.put(overlappingTargetsFilter, overlappingTargets);
return overlappingTargets;
}
}
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public Rollout startRollout(final Long rolloutId) {
LOGGER.debug("startRollout called for rollout {}", rolloutId);
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
RolloutHelper.checkIfRolloutCanStarted(rollout, rollout);
rollout.setStatus(RolloutStatus.STARTING);
@@ -496,6 +422,7 @@ public class JpaRolloutManagement implements RolloutManagement {
}
private void startFirstRolloutGroup(final Rollout rollout) {
LOGGER.debug("startFirstRolloutGroup called for rollout {}", rollout.getId());
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.STARTING);
final JpaRollout jpaRollout = (JpaRollout) rollout;
@@ -516,7 +443,6 @@ public class JpaRolloutManagement implements RolloutManagement {
}
private boolean ensureAllGroupsAreScheduled(final Rollout rollout) {
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.STARTING);
final JpaRollout jpaRollout = (JpaRollout) rollout;
final List<JpaRolloutGroup> groupsToBeScheduled = rolloutGroupRepository.findByRolloutAndStatus(rollout,
@@ -566,7 +492,7 @@ public class JpaRolloutManagement implements RolloutManagement {
}
private Integer createActionsForTargetsInNewTransaction(final long rolloutId, final long groupId, final int limit) {
return runInNewCountingTransaction("createActionsForTargets", status -> {
return runInNewTransaction("createActionsForTargets", status -> {
final PageRequest pageRequest = new PageRequest(0, limit);
final Rollout rollout = rolloutRepository.findOne(rolloutId);
final RolloutGroup group = rolloutGroupRepository.findOne(groupId);
@@ -618,7 +544,7 @@ public class JpaRolloutManagement implements RolloutManagement {
@Modifying
public void pauseRollout(final Long rolloutId) {
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
if (rollout.getStatus() != RolloutStatus.RUNNING) {
if (!RolloutStatus.RUNNING.equals(rollout.getStatus())) {
throw new RolloutIllegalStateException("Rollout can only be paused in state running but current state is "
+ rollout.getStatus().name().toLowerCase());
}
@@ -644,39 +570,27 @@ public class JpaRolloutManagement implements RolloutManagement {
rolloutRepository.save(rollout);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public void checkRunningRollouts(final long delayBetweenChecks) {
final List<JpaRollout> rolloutsToCheck = getRolloutsToCheckForStatus(delayBetweenChecks, RolloutStatus.RUNNING);
if (rolloutsToCheck.isEmpty()) {
return;
private void handleRunningRollout(final JpaRollout rollout) {
LOGGER.debug("handleRunningRollout called for rollout {}", rollout.getId());
final List<JpaRolloutGroup> rolloutGroupsRunning = rolloutGroupRepository.findByRolloutAndStatus(rollout,
RolloutGroupStatus.RUNNING);
if (rolloutGroupsRunning.isEmpty()) {
// no running rollouts, probably there was an error
// somewhere at the latest group. And the latest group has
// been switched from running into error state. So we need
// to find the latest group which
executeLatestRolloutGroup(rollout);
} else {
LOGGER.debug("Rollout {} has {} running groups", rollout.getId(), rolloutGroupsRunning.size());
executeRolloutGroups(rollout, rolloutGroupsRunning);
}
LOGGER.info("Found {} running rollouts to check", rolloutsToCheck.size());
for (final JpaRollout rollout : rolloutsToCheck) {
LOGGER.debug("Checking rollout {}", rollout);
final List<JpaRolloutGroup> rolloutGroupsRunning = rolloutGroupRepository.findByRolloutAndStatus(rollout,
RolloutGroupStatus.RUNNING);
if (rolloutGroupsRunning.isEmpty()) {
// no running rollouts, probably there was an error
// somewhere at the latest group. And the latest group has
// been switched from running into error state. So we need
// to find the latest group which
executeLatestRolloutGroup(rollout);
} else {
LOGGER.debug("Rollout {} has {} running groups", rollout.getId(), rolloutGroupsRunning.size());
executeRolloutGroups(rollout, rolloutGroupsRunning);
}
if (isRolloutComplete(rollout)) {
LOGGER.info("Rollout {} is finished, setting finished status", rollout);
rollout.setStatus(RolloutStatus.FINISHED);
rolloutRepository.save(rollout);
}
if (isRolloutComplete(rollout)) {
LOGGER.info("Rollout {} is finished, setting FINISHED status", rollout);
rollout.setStatus(RolloutStatus.FINISHED);
rolloutRepository.save(rollout);
}
}
@@ -741,7 +655,7 @@ public class JpaRolloutManagement implements RolloutManagement {
}
private boolean isRolloutComplete(final JpaRollout rollout) {
final Long groupsActiveLeft = rolloutGroupRepository.countByRolloutAndStatusOrStatus(rollout,
final Long groupsActiveLeft = rolloutGroupRepository.countByRolloutIdAndStatusOrStatus(rollout.getId(),
RolloutGroupStatus.RUNNING, RolloutGroupStatus.SCHEDULED);
return groupsActiveLeft == 0;
}
@@ -798,90 +712,170 @@ public class JpaRolloutManagement implements RolloutManagement {
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public void checkCreatingRollouts(final long delayBetweenChecks) {
final List<Long> rolloutsToCheck = getRolloutsToCheckForStatus(delayBetweenChecks, RolloutStatus.CREATING)
.stream().map(Rollout::getId).collect(Collectors.toList());
if (rolloutsToCheck.isEmpty()) {
// No transaction, will be created per handled rollout
@Transactional(propagation = Propagation.NEVER)
public void handleRollouts() {
rolloutRepository
.findByStatusIn(Lists.newArrayList(RolloutStatus.CREATING, RolloutStatus.DELETING,
RolloutStatus.STARTING, RolloutStatus.READY, RolloutStatus.RUNNING))
.forEach(this::handleRollout);
}
private void handleRollout(final Long rolloutId) {
LOGGER.debug("handleRollout called for rollout {}", rolloutId);
final String tenant = tenantAware.getCurrentTenant();
final String handlerId = tenant + "-rollout-" + rolloutId;
final Lock lock = lockRegistry.obtain(handlerId);
if (!lock.tryLock()) {
return;
}
LOGGER.info("Found {} creating rollouts to check", rolloutsToCheck.size());
try {
runInNewTransaction(handlerId, status -> executeFittingHandler(rolloutId));
} finally {
lock.unlock();
}
}
rolloutsToCheck.forEach(this::fillRolloutGroupsWithTargets);
private int executeFittingHandler(final Long rolloutId) {
final JpaRollout rollout = rolloutRepository.findOne(rolloutId);
switch (rollout.getStatus()) {
case CREATING:
handleCreateRollout(rollout);
break;
case DELETING:
handleDeleteRollout(rollout);
break;
case READY:
handleReadyRollout(rollout);
break;
case STARTING:
handleStartingRollout(rollout);
break;
case RUNNING:
handleRunningRollout(rollout);
break;
default:
LOGGER.error("Rollout in status {} not supposed to be handled!", rollout.getStatus());
break;
}
return 0;
}
private void handleStartingRollout(final Rollout rollout) {
LOGGER.debug("handleStartingRollout called for rollout {}", rollout.getId());
if (ensureAllGroupsAreScheduled(rollout)) {
startFirstRolloutGroup(rollout);
}
}
private void handleReadyRollout(final Rollout rollout) {
if (rollout.getStartAt() != null && rollout.getStartAt() <= System.currentTimeMillis()) {
LOGGER.debug(
"handleReadyRollout called for rollout {} with autostart beyond define time. Switch to STARTING",
rollout.getId());
startRollout(rollout.getId());
}
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public void checkStartingRollouts(final long delayBetweenChecks) {
final List<JpaRollout> rolloutsToCheck = getRolloutsToCheckForStatus(delayBetweenChecks,
RolloutStatus.STARTING);
if (rolloutsToCheck.isEmpty()) {
public void deleteRollout(final long rolloutId) {
final JpaRollout jpaRollout = rolloutRepository.findOne(rolloutId);
if (RolloutStatus.DELETING.equals(jpaRollout.getStatus())) {
return;
}
LOGGER.info("Found {} starting rollouts to check", rolloutsToCheck.size());
rolloutsToCheck.forEach(rollout -> {
if (ensureAllGroupsAreScheduled(rollout)) {
startFirstRolloutGroup(rollout);
}
});
jpaRollout.setStatus(RolloutStatus.DELETING);
rolloutRepository.save(jpaRollout);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public void checkReadyRollouts(final long delayBetweenChecks) {
final List<JpaRollout> rolloutsToCheck = getRolloutsToCheckForStatus(delayBetweenChecks, RolloutStatus.READY);
if (rolloutsToCheck.isEmpty()) {
private void handleDeleteRollout(final JpaRollout rollout) {
LOGGER.debug("handleDeleteRollout called for {}", rollout.getId());
// check if there are actions beyond schedule
boolean hardDeleteRolloutGroups = !actionRepository.existsByRolloutIdAndStatusNotIn(rollout.getId(),
Status.SCHEDULED);
if (hardDeleteRolloutGroups) {
LOGGER.debug("Rollout {} has no actions other than scheduled -> hard delete", rollout.getId());
hardDeleteRollout(rollout);
return;
}
// clean up all scheduled actions
final Slice<JpaAction> scheduledActions = findScheduledActionsByRollout(rollout);
deleteScheduledActions(rollout, scheduledActions);
// avoid another scheduler round and re-check if all scheduled actions
// has been cleaned up
final boolean hasScheduledActionsLeft = findScheduledActionsByRollout(rollout).getNumberOfElements() > 0;
if (hasScheduledActionsLeft) {
return;
}
LOGGER.info("Found {} ready rollouts to check", rolloutsToCheck.size());
final long now = System.currentTimeMillis();
rolloutsToCheck.forEach(rollout -> {
if (rollout.getStartAt() != null && rollout.getStartAt() <= now) {
startRollout(rollout.getId());
}
});
}
private List<JpaRollout> getRolloutsToCheckForStatus(final long delayBetweenChecks, final RolloutStatus status) {
final long lastCheck = System.currentTimeMillis();
final int updated = rolloutRepository.updateLastCheck(lastCheck, delayBetweenChecks, status);
if (updated == 0) {
// nothing to check, maybe another instance already checked in
// between
LOGGER.debug("No rollouts starting check necessary for current scheduled check {}, next check at {}",
lastCheck, lastCheck + delayBetweenChecks);
return Collections.emptyList();
// only hard delete the rollout if no actions are left for the rollout.
// In case actions are left, they are probably are running or were
// running before, so only soft delete.
hardDeleteRolloutGroups = !actionRepository.existsByRolloutId(rollout.getId());
if (hardDeleteRolloutGroups) {
hardDeleteRollout(rollout);
return;
}
return rolloutRepository.findByLastCheckAndStatus(lastCheck, status);
// set soft delete
rollout.setStatus(RolloutStatus.DELETED);
rollout.setDeleted(true);
rolloutRepository.save(rollout);
}
private void hardDeleteRollout(final JpaRollout rollout) {
rolloutRepository.delete(rollout);
}
private void deleteScheduledActions(final JpaRollout rollout, final Slice<JpaAction> scheduledActions) {
final boolean hasScheduledActions = scheduledActions.getNumberOfElements() > 0;
if (hasScheduledActions) {
try {
final Iterable<JpaAction> iterable = scheduledActions::iterator;
final List<Long> actionIds = StreamSupport.stream(iterable.spliterator(), false).map(Action::getId)
.collect(Collectors.toList());
actionRepository.deleteByIdIn(actionIds);
afterCommit.afterCommit(() -> eventPublisher.publishEvent(
new RolloutUpdatedEvent(rollout, EventPublisherHolder.getInstance().getApplicationId())));
} catch (final RuntimeException e) {
LOGGER.error("Exception during deletion of actions of rollout {}", rollout, e);
}
}
}
private Slice<JpaAction> findScheduledActionsByRollout(final JpaRollout rollout) {
return actionRepository.findByRolloutIdAndStatus(new PageRequest(0, TRANSACTION_ACTIONS), rollout.getId(),
Status.SCHEDULED);
}
@Override
public Long countRolloutsAll() {
return rolloutRepository.count();
return rolloutRepository.count(RolloutSpecification.isDeleted(false));
}
@Override
public Long countRolloutsAllByFilters(final String searchText) {
return rolloutRepository.count(RolloutHelper.likeNameOrDescription(searchText));
return rolloutRepository.count(JpaRolloutHelper.likeNameOrDescription(searchText, false));
}
@Override
public Slice<Rollout> findRolloutWithDetailedStatusByFilters(final Pageable pageable, final String searchText) {
final Specification<JpaRollout> specs = RolloutHelper.likeNameOrDescription(searchText);
final Slice<JpaRollout> findAll = criteriaNoCountDao.findAll(specs, pageable, JpaRollout.class);
public Slice<Rollout> findRolloutWithDetailedStatusByFilters(final Pageable pageable, final String searchText,
final boolean deleted) {
final Slice<JpaRollout> findAll = findByCriteriaAPI(pageable,
Lists.newArrayList(JpaRolloutHelper.likeNameOrDescription(searchText, deleted)));
setRolloutStatusDetails(findAll);
return RolloutHelper.convertPage(findAll, pageable);
return JpaRolloutHelper.convertPage(findAll, pageable);
}
@Override
@@ -917,10 +911,12 @@ public class JpaRolloutManagement implements RolloutManagement {
}
@Override
public Page<Rollout> findAllRolloutsWithDetailedStatus(final Pageable pageable) {
final Page<JpaRollout> rollouts = rolloutRepository.findAll(pageable);
public Page<Rollout> findAllRolloutsWithDetailedStatus(final Pageable pageable, final boolean deleted) {
Page<JpaRollout> rollouts;
final Specification<JpaRollout> spec = RolloutSpecification.isDeleted(deleted);
rollouts = rolloutRepository.findAll(spec, pageable);
setRolloutStatusDetails(rollouts);
return RolloutHelper.convertPage(rollouts, pageable);
return JpaRolloutHelper.convertPage(rollouts, pageable);
}
@Override
@@ -940,8 +936,12 @@ public class JpaRolloutManagement implements RolloutManagement {
}
private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRollout(final List<Long> rolloutIds) {
final List<TotalTargetCountActionStatus> resultList = actionRepository.getStatusCountByRolloutId(rolloutIds);
return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
if (!rolloutIds.isEmpty()) {
final List<TotalTargetCountActionStatus> resultList = actionRepository
.getStatusCountByRolloutId(rolloutIds);
return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
}
return null;
}
private void setRolloutStatusDetails(final Slice<JpaRollout> rollouts) {
@@ -949,10 +949,12 @@ public class JpaRolloutManagement implements RolloutManagement {
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRollout(
rolloutIds);
for (final Rollout rollout : rollouts) {
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets());
((JpaRollout) rollout).setTotalTargetCountStatus(totalTargetCountStatus);
if (allStatesForRollout != null) {
rollouts.forEach(rollout -> {
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets());
rollout.setTotalTargetCountStatus(totalTargetCountStatus);
});
}
}

View File

@@ -209,8 +209,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
return softwareModuleRepository.findOneByNameAndVersionAndTypeId(name, version, typeId);
}
private boolean isUnassigned(final JpaSoftwareModule bsmMerged) {
return distributionSetRepository.countByModules(bsmMerged) <= 0;
private boolean isUnassigned(final Long moduleId) {
return distributionSetRepository.countByModulesId(moduleId) <= 0;
}
private Slice<JpaSoftwareModule> findSwModuleByCriteriaAPI(final Pageable pageable,
@@ -225,7 +225,7 @@ public class JpaSoftwareManagement implements SoftwareManagement {
private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) {
for (final Artifact localArtifact : swModule.getArtifacts()) {
artifactManagement.clearArtifactBinary(localArtifact.getId());
artifactManagement.clearArtifactBinary(localArtifact.getSha1Hash(), swModule.getId());
}
}
@@ -246,12 +246,9 @@ public class JpaSoftwareManagement implements SoftwareManagement {
// delete binary data of artifacts
deleteGridFsArtifacts(swModule);
if (isUnassigned(swModule)) {
softwareModuleRepository.delete(swModule);
if (isUnassigned(swModule.getId())) {
softwareModuleRepository.delete(swModule.getId());
} else {
assignedModuleIds.add(swModule.getId());
}
});

View File

@@ -218,8 +218,6 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
tenantConfigurationRepository.deleteByTenantIgnoreCase(tenant);
targetRepository.deleteByTenantIgnoreCase(tenant);
targetFilterQueryRepository.deleteByTenantIgnoreCase(tenant);
actionRepository.deleteByTenantIgnoreCase(tenant);
rolloutGroupRepository.deleteByTenantIgnoreCase(tenant);
rolloutRepository.deleteByTenantIgnoreCase(tenant);
artifactRepository.deleteByTenantIgnoreCase(tenant);
targetTagRepository.deleteByTenantIgnoreCase(tenant);

View File

@@ -607,7 +607,7 @@ public class JpaTargetManagement implements TargetManagement {
final JpaTarget target = create.build();
if (targetRepository.findByControllerId(target.getControllerId()).isPresent()) {
if (targetRepository.existsByControllerId(target.getControllerId())) {
throw new EntityAlreadyExistsException();
}

View File

@@ -13,9 +13,11 @@ import java.util.Optional;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@@ -54,6 +56,21 @@ public interface LocalArtifactRepository extends BaseEntityRepository<JpaArtifac
*/
List<Artifact> findBySha1Hash(String sha1Hash);
/**
* Verifies if an artifact exists that has given hash and is still related
* to a {@link SoftwareModule} other than a given one and not
* {@link SoftwareModule#isDeleted()}.
*
* @param sha1
* to search for
* @param moduleId
* to ignore in relationship check
*
* @return <code>true</code> if such an artifact exists
*/
@Query("SELECT CASE WHEN COUNT(a)>0 THEN 'true' ELSE 'false' END FROM JpaArtifact a WHERE a.sha1Hash = :sha1 AND a.softwareModule.id != :moduleId AND a.softwareModule.deleted = 0")
boolean existsWithSha1HashAndSoftwareModuleIdIsNot(@Param("sha1") String sha1, @Param("moduleId") Long moduleId);
/**
* Searches for a {@link Artifact} based on given gridFsFileName.
*

View File

@@ -9,6 +9,7 @@
package org.eclipse.hawkbit.repository.jpa;
import java.util.Map;
import java.util.concurrent.Executor;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
@@ -23,7 +24,6 @@ import org.eclipse.hawkbit.repository.ReportManagement;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.RolloutProperties;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TagManagement;
@@ -53,6 +53,7 @@ import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
import org.eclipse.hawkbit.repository.jpa.rollout.RolloutScheduler;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParserValidationOracle;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
@@ -69,18 +70,23 @@ import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityScan;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
import org.springframework.scheduling.annotation.EnableScheduling;
@@ -101,7 +107,7 @@ import com.google.common.collect.Maps;
@EnableAspectJAutoProxy
@Configuration
@ComponentScan
@EnableConfigurationProperties({ RepositoryProperties.class, ControllerPollProperties.class, RolloutProperties.class,
@EnableConfigurationProperties({ RepositoryProperties.class, ControllerPollProperties.class,
TenantConfigurationProperties.class })
@EnableScheduling
@EntityScan("org.eclipse.hawkbit.repository.jpa.model")
@@ -115,7 +121,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Bean
@ConditionalOnMissingBean
public RsqlValidationOracle rsqlValidationOracle() {
RsqlValidationOracle rsqlValidationOracle() {
return new RsqlParserValidationOracle();
}
@@ -127,7 +133,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return DistributionSetBuilder bean
*/
@Bean
public DistributionSetBuilder distributionSetBuilder(final DistributionSetManagement distributionSetManagement,
DistributionSetBuilder distributionSetBuilder(final DistributionSetManagement distributionSetManagement,
final SoftwareManagement softwareManagement) {
return new JpaDistributionSetBuilder(distributionSetManagement, softwareManagement);
}
@@ -140,7 +146,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return DistributionSetTypeBuilder bean
*/
@Bean
public DistributionSetTypeBuilder distributionSetTypeBuilder(final SoftwareManagement softwareManagement) {
DistributionSetTypeBuilder distributionSetTypeBuilder(final SoftwareManagement softwareManagement) {
return new JpaDistributionSetTypeBuilder(softwareManagement);
}
@@ -150,7 +156,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return SoftwareModuleBuilder bean
*/
@Bean
public SoftwareModuleBuilder softwareModuleBuilder(final SoftwareManagement softwareManagement) {
SoftwareModuleBuilder softwareModuleBuilder(final SoftwareManagement softwareManagement) {
return new JpaSoftwareModuleBuilder(softwareManagement);
}
@@ -160,7 +166,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return RolloutBuilder bean
*/
@Bean
public RolloutBuilder rolloutBuilder(final DistributionSetManagement distributionSetManagement) {
RolloutBuilder rolloutBuilder(final DistributionSetManagement distributionSetManagement) {
return new JpaRolloutBuilder(distributionSetManagement);
}
@@ -171,8 +177,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return TargetFilterQueryBuilder bean
*/
@Bean
public TargetFilterQueryBuilder targetFilterQueryBuilder(
final DistributionSetManagement distributionSetManagement) {
TargetFilterQueryBuilder targetFilterQueryBuilder(final DistributionSetManagement distributionSetManagement) {
return new JpaTargetFilterQueryBuilder(distributionSetManagement);
}
@@ -182,7 +187,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* e.g. JPA entities.
*/
@Bean
public SystemSecurityContextHolder systemSecurityContextHolder() {
SystemSecurityContextHolder systemSecurityContextHolder() {
return SystemSecurityContextHolder.getInstance();
}
@@ -192,7 +197,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* directly, e.g. JPA entities.
*/
@Bean
public TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
return TenantConfigurationManagementHolder.getInstance();
}
@@ -203,7 +208,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* entities.
*/
@Bean
public SystemManagementHolder systemManagementHolder() {
SystemManagementHolder systemManagementHolder() {
return SystemManagementHolder.getInstance();
}
@@ -214,7 +219,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* entities.
*/
@Bean
public TenantAwareHolder tenantAwareHolder() {
TenantAwareHolder tenantAwareHolder() {
return TenantAwareHolder.getInstance();
}
@@ -225,7 +230,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* injection
*/
@Bean
public SecurityTokenGeneratorHolder securityTokenGeneratorHolder() {
SecurityTokenGeneratorHolder securityTokenGeneratorHolder() {
return SecurityTokenGeneratorHolder.getInstance();
}
@@ -233,7 +238,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return the singleton instance of the {@link EntityInterceptorHolder}
*/
@Bean
public EntityInterceptorHolder entityInterceptorHolder() {
EntityInterceptorHolder entityInterceptorHolder() {
return EntityInterceptorHolder.getInstance();
}
@@ -243,7 +248,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* {@link AfterTransactionCommitExecutorHolder}
*/
@Bean
public AfterTransactionCommitExecutorHolder afterTransactionCommitExecutorHolder() {
AfterTransactionCommitExecutorHolder afterTransactionCommitExecutorHolder() {
return AfterTransactionCommitExecutorHolder.getInstance();
}
@@ -261,7 +266,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return {@link ExceptionMappingAspectHandler} aspect bean
*/
@Bean
public ExceptionMappingAspectHandler createRepositoryExceptionHandlerAdvice() {
ExceptionMappingAspectHandler createRepositoryExceptionHandlerAdvice() {
return new ExceptionMappingAspectHandler();
}
@@ -305,7 +310,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public SystemManagement systemManagement() {
SystemManagement systemManagement() {
return new JpaSystemManagement();
}
@@ -316,7 +321,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public ReportManagement reportManagement() {
ReportManagement reportManagement() {
return new JpaReportManagement();
}
@@ -327,7 +332,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public DistributionSetManagement distributionSetManagement() {
DistributionSetManagement distributionSetManagement() {
return new JpaDistributionSetManagement();
}
@@ -338,7 +343,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public TenantStatsManagement tenantStatsManagement() {
TenantStatsManagement tenantStatsManagement() {
return new JpaTenantStatsManagement();
}
@@ -349,7 +354,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public TenantConfigurationManagement tenantConfigurationManagement() {
TenantConfigurationManagement tenantConfigurationManagement() {
return new JpaTenantConfigurationManagement();
}
@@ -360,7 +365,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public TargetManagement targetManagement() {
TargetManagement targetManagement() {
return new JpaTargetManagement();
}
@@ -378,7 +383,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public TargetFilterQueryManagement targetFilterQueryManagement(
TargetFilterQueryManagement targetFilterQueryManagement(
final TargetFilterQueryRepository targetFilterQueryRepository,
final VirtualPropertyReplacer virtualPropertyReplacer,
final DistributionSetManagement distributionSetManagement) {
@@ -393,7 +398,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public TagManagement tagManagement() {
TagManagement tagManagement() {
return new JpaTagManagement();
}
@@ -404,19 +409,21 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public SoftwareManagement softwareManagement() {
SoftwareManagement softwareManagement() {
return new JpaSoftwareManagement();
}
/**
* {@link JpaRolloutManagement} bean.
*
* @return a new {@link RolloutManagement}
*/
@Bean
@ConditionalOnMissingBean
public RolloutManagement rolloutManagement() {
return new JpaRolloutManagement();
RolloutManagement rolloutManagement(final TargetManagement targetManagement,
final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
final ApplicationEventPublisher eventPublisher, final VirtualPropertyReplacer virtualPropertyReplacer,
final PlatformTransactionManager txManager, final TenantAware tenantAware,
final LockRegistry lockRegistry) {
return new JpaRolloutManagement(targetManagement, deploymentManagement, rolloutGroupManagement,
distributionSetManagement, context, eventPublisher, virtualPropertyReplacer, txManager, tenantAware,
lockRegistry);
}
/**
@@ -426,7 +433,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public RolloutGroupManagement rolloutGroupManagement() {
RolloutGroupManagement rolloutGroupManagement() {
return new JpaRolloutGroupManagement();
}
@@ -437,7 +444,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public DeploymentManagement deploymentManagement() {
DeploymentManagement deploymentManagement() {
return new JpaDeploymentManagement();
}
@@ -448,7 +455,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public ControllerManagement controllerManagement() {
ControllerManagement controllerManagement() {
return new JpaControllerManagement();
}
@@ -460,7 +467,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Bean
@ConditionalOnMissingBean
public ArtifactManagement artifactManagement() {
ArtifactManagement artifactManagement() {
return new JpaArtifactManagement();
}
@@ -482,7 +489,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public EventEntityManagerHolder eventEntityManagerHolder() {
EventEntityManagerHolder eventEntityManagerHolder() {
return EventEntityManagerHolder.getInstance();
}
@@ -497,7 +504,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public EventEntityManager eventEntityManager(final TenantAware aware, final EntityManager entityManager) {
EventEntityManager eventEntityManager(final TenantAware aware, final EntityManager entityManager) {
return new JpaEventEntityManager(aware, entityManager);
}
@@ -516,7 +523,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public AutoAssignChecker autoAssignChecker(final TargetFilterQueryManagement targetFilterQueryManagement,
AutoAssignChecker autoAssignChecker(final TargetFilterQueryManagement targetFilterQueryManagement,
final TargetManagement targetManagement, final DeploymentManagement deploymentManagement,
final PlatformTransactionManager transactionManager) {
return new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement,
@@ -525,6 +532,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* {@link AutoAssignScheduler} bean.
*
* Note: does not activate in test profile, otherwise it is hard to test the
* auto assign functionality.
*
* @param tenantAware
* to run as specific tenant
@@ -534,14 +544,49 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* to run as system
* @param autoAssignChecker
* to run a check as tenant
* @param lockRegistry
* to lock the tenant for auto assigment
* @return a new {@link AutoAssignChecker}
*/
@Bean
@ConditionalOnMissingBean
// don't active the auto assign scheduler in test, otherwise it is hard to
// test
@Profile("!test")
@ConditionalOnProperty(prefix = "hawkbit.autoassign.scheduler", name = "enabled", matchIfMissing = true)
public AutoAssignScheduler autoAssignScheduler(final TenantAware tenantAware,
final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext,
final AutoAssignChecker autoAssignChecker) {
return new AutoAssignScheduler(tenantAware, systemManagement, systemSecurityContext, autoAssignChecker);
AutoAssignScheduler autoAssignScheduler(final TenantAware tenantAware, final SystemManagement systemManagement,
final SystemSecurityContext systemSecurityContext, final AutoAssignChecker autoAssignChecker,
final LockRegistry lockRegistry) {
return new AutoAssignScheduler(tenantAware, systemManagement, systemSecurityContext, autoAssignChecker,
lockRegistry);
}
/**
* {@link RolloutScheduler} bean.
*
* Note: does not activate in test profile, otherwise it is hard to test the
* rollout handling functionality.
*
* @param tenantAware
* to run as specific tenant
* @param systemManagement
* to find all tenants
* @param rolloutManagement
* to run the rollout handler
* @param systemSecurityContext
* to run as system
* @param threadPoolExecutor
* to execute the handlers in parallel
* @return a new {@link RolloutScheduler} bean.
*/
@Bean
@ConditionalOnMissingBean
@Profile("!test")
@ConditionalOnProperty(prefix = "hawkbit.rollout.scheduler", name = "enabled", matchIfMissing = true)
RolloutScheduler rolloutScheduler(final TenantAware tenantAware, final SystemManagement systemManagement,
final RolloutManagement rolloutManagement, final SystemSecurityContext systemSecurityContext,
@Qualifier("asyncExecutor") final Executor threadPoolExecutor) {
return new RolloutScheduler(tenantAware, systemManagement, rolloutManagement, systemSecurityContext,
threadPoolExecutor);
}
}

View File

@@ -60,8 +60,8 @@ public interface RolloutGroupRepository
* rollout-management to find out rolloutgroups which are in specific
* states.
*
* @param rollout
* the rollout the rolloutgroup belong to
* @param rolloutId
* the ID of the rollout the rolloutgroup belong to
* @param rolloutGroupStatus1
* the status of the rollout groups
* @param rolloutGroupStatus2
@@ -69,22 +69,45 @@ public interface RolloutGroupRepository
* @return the count of rollout groups belonging to a rollout in specific
* status
*/
@Query("SELECT COUNT(r.id) FROM JpaRolloutGroup r WHERE r.rollout = :rollout and (r.status = :status1 or r.status = :status2)")
Long countByRolloutAndStatusOrStatus(@Param("rollout") JpaRollout rollout,
@Query("SELECT COUNT(r.id) FROM JpaRolloutGroup r WHERE r.rollout.id = :rolloutId and (r.status = :status1 or r.status = :status2)")
Long countByRolloutIdAndStatusOrStatus(@Param("rolloutId") long rolloutId,
@Param("status1") RolloutGroupStatus rolloutGroupStatus1,
@Param("status2") RolloutGroupStatus rolloutGroupStatus2);
/**
*
* Counts all rollout-groups refering to a given {@link Rollout} by its ID
* and groups which not having the given status.
*
* @param rolloutId
* the ID of the rollout refering the groups
* @param status1
* the status which the groups should not have
* @param status2
* the status which the groups should not have
* @param status2
* the status which the groups should not have
* @return count of rollout-groups referning a rollout and not in the given
* states
*/
long countByRolloutIdAndStatusNotAndStatusNotAndStatusNot(@Param("rolloutId") long rolloutId,
@Param("status1") JpaRolloutGroup.RolloutGroupStatus status1,
@Param("status2") JpaRolloutGroup.RolloutGroupStatus status2,
@Param("status3") JpaRolloutGroup.RolloutGroupStatus status3);
/**
* Retrieves all {@link RolloutGroup} for a specific parent in a specific
* status. Retrieves the child rolloutgroup for a specific status.
*
* @param rolloutGroup
* the parent rolloutgroup
* @param rolloutGroupId
* the rolloutgroupId to find the parents
* @param status
* the status of the rolloutgroups
* @return The child {@link RolloutGroup}s in a specific status
*/
List<JpaRolloutGroup> findByParentAndStatus(JpaRolloutGroup rolloutGroup, RolloutGroupStatus status);
@Query("SELECT g FROM JpaRolloutGroup g WHERE g.parent.id=:rolloutGroupId and g.status=:status")
List<JpaRolloutGroup> findByParentIdAndStatus(@Param("rolloutGroupId") long rolloutGroupId,
@Param("status") RolloutGroupStatus status);
/**
* Updates all {@link RolloutGroup#getStatus()} of children for given
@@ -124,4 +147,8 @@ public interface RolloutGroupRepository
*/
Page<JpaRolloutGroup> findByRolloutId(final Long rolloutId, Pageable page);
@Modifying
@Query("DELETE FROM JpaRolloutGroup g where g.id in :rolloutGroupIds")
void deleteByIds(@Param("rolloutGroupIds") List<Long> rolloutGroups);
}

View File

@@ -8,6 +8,7 @@
*/
package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@@ -15,11 +16,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
@@ -30,36 +28,15 @@ public interface RolloutRepository
extends BaseEntityRepository<JpaRollout, Long>, JpaSpecificationExecutor<JpaRollout> {
/**
* Updates the {@code lastCheck} field of the {@link Rollout} for rollouts
* in a specific status and only if the {@code lastCheck} is overdue.
* Retrieves all {@link Rollout} for given status.
*
* @param lastCheck
* the time in milliseconds to set to the lastCheck column
* @param delay
* the delay between last checks
* @param status
* the status which the rollout should have to update the last
* check field
* @return the count of the updated rows. Zero if no row has been updated
* the status of the rollouts to find
* @return the list of {@link Rollout} for specific status
*/
@Modifying
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
@Query("UPDATE JpaRollout r SET r.lastCheck = :lastCheck WHERE r.lastCheck < (:lastCheck - :delay) AND r.status=:status")
int updateLastCheck(@Param("lastCheck") final long lastCheck, @Param("delay") final long delay,
@Param("status") final RolloutStatus status);
/**
* Retrieves all {@link Rollout} for a specific {@code lastCheck} time and
* for a specific status.
*
* @param lastCheck
* the lastCheck time to find the specific rollout.
* @param status
* the status of the rollout to find
* @return the list of {@link Rollout} for specific lastCheck time and
* status
*/
List<JpaRollout> findByLastCheckAndStatus(long lastCheck, RolloutStatus status);
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("SELECT sm.id FROM JpaRollout sm WHERE sm.status IN ?1")
List<Long> findByStatusIn(Collection<RolloutStatus> status);
/**
* Retrieves all {@link Rollout} for a specific {@code name}

View File

@@ -47,6 +47,15 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
@EntityGraph(value = "Target.detail", type = EntityGraphType.LOAD)
Optional<Target> findByControllerId(String controllerID);
/**
* Checks if target with given id exists.
*
* @param controllerId to check
* @return <code>true</code> if target with given id exists
*/
@Query("SELECT CASE WHEN COUNT(t)>0 THEN 'true' ELSE 'false' END FROM JpaTarget t WHERE t.controllerId=:controllerId")
boolean existsByControllerId(@Param("controllerId") String controllerId);
/**
* Deletes the {@link Target}s with the given target IDs.
*

View File

@@ -97,6 +97,7 @@ public class AutoAssignChecker {
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void check() {
LOGGER.debug("Auto assigned check call");
final PageRequest pageRequest = new PageRequest(0, PAGE_SIZE);

View File

@@ -9,27 +9,24 @@
package org.eclipse.hawkbit.repository.jpa.autoassign;
import java.util.List;
import java.util.concurrent.locks.Lock;
import org.eclipse.hawkbit.repository.AutoAssignProperties;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Profile;
import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.scheduling.annotation.Scheduled;
/**
* Scheduler to check target filters for auto assignment of distribution sets
*/
// don't active the auto assign scheduler in test, otherwise it is hard to test
@Profile("!test")
@EnableConfigurationProperties(AutoAssignProperties.class)
public class AutoAssignScheduler {
private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignScheduler.class);
private static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.autoassign.scheduler.fixedDelay:2000}";
private final TenantAware tenantAware;
private final SystemManagement systemManagement;
@@ -38,6 +35,8 @@ public class AutoAssignScheduler {
private final AutoAssignChecker autoAssignChecker;
private final LockRegistry lockRegistry;
/**
* Instantiates a new AutoAssignScheduler
*
@@ -49,13 +48,17 @@ public class AutoAssignScheduler {
* to run as system
* @param autoAssignChecker
* to run a check as tenant
* @param lockRegistry
* to acquire a lock per tenant
*/
public AutoAssignScheduler(final TenantAware tenantAware, final SystemManagement systemManagement,
final SystemSecurityContext systemSecurityContext, final AutoAssignChecker autoAssignChecker) {
final SystemSecurityContext systemSecurityContext, final AutoAssignChecker autoAssignChecker,
final LockRegistry lockRegistry) {
this.tenantAware = tenantAware;
this.systemManagement = systemManagement;
this.systemSecurityContext = systemSecurityContext;
this.autoAssignChecker = autoAssignChecker;
this.lockRegistry = lockRegistry;
}
/**
@@ -64,29 +67,38 @@ public class AutoAssignScheduler {
* tenant the auto assignments defined in the target filter queries
* {@link SystemSecurityContext}.
*/
@Scheduled(initialDelayString = AutoAssignProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = AutoAssignProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER)
@Scheduled(initialDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER)
public void autoAssignScheduler() {
LOGGER.debug("auto assign schedule checker has been triggered.");
// run this code in system code privileged to have the necessary
// permission to query and create entities.
systemSecurityContext.runAsSystem(() -> {
// workaround eclipselink that is currently not possible to
// execute a query without multitenancy if MultiTenant
// annotation is used.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
// iterate through all tenants and execute the rollout check for
// each tenant separately.
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking target filter queries for tenants: {}", tenants.size());
for (final String tenant : tenants) {
systemSecurityContext.runAsSystem(() -> executeAutoAssign());
}
private Object executeAutoAssign() {
// workaround eclipselink that is currently not possible to
// execute a query without multitenancy if MultiTenant
// annotation is used.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
// iterate through all tenants and execute the rollout check for
// each tenant separately.
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking target filter queries for tenants: {}", tenants.size());
for (final String tenant : tenants) {
final Lock lock = lockRegistry.obtain(tenant + "-autoassign");
if (!lock.tryLock()) {
return null;
}
try {
tenantAware.runAsTenant(tenant, () -> {
autoAssignChecker.check();
return null;
});
} finally {
lock.unlock();
}
return null;
});
}
return null;
}
}

View File

@@ -43,12 +43,12 @@ public class DistributionSetTypeElement implements Serializable {
@MapsId("dsType")
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "distribution_set_type", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_type_element_dstype"))
@JoinColumn(name = "distribution_set_type", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_type_element_element"))
private JpaDistributionSetType dsType;
@MapsId("smType")
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "software_module_type", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_type_element_smtype"))
@JoinColumn(name = "software_module_type", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_type_element_smtype"))
private JpaSoftwareModuleType smType;
public DistributionSetTypeElement() {

View File

@@ -20,10 +20,10 @@ import javax.persistence.Embeddable;
public class DistributionSetTypeElementCompositeKey implements Serializable {
private static final long serialVersionUID = 1L;
@Column(name = "distribution_set_type", nullable = false)
@Column(name = "distribution_set_type", nullable = false, updatable = false)
private Long dsType;
@Column(name = "software_module_type", nullable = false)
@Column(name = "software_module_type", nullable = false, updatable = false)
private Long smType;
/**

View File

@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa.model;
import java.util.Collections;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
@@ -34,6 +33,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
@@ -58,13 +58,13 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Action, EventAwareEntity {
private static final long serialVersionUID = 1L;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "distribution_set", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_ds"))
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "distribution_set", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_ds"))
@NotNull
private JpaDistributionSet distributionSet;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "target", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_target"))
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "target", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_target"))
@NotNull
private JpaTarget target;
@@ -79,20 +79,20 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Column(name = "forced_time")
private long forcedTime;
@Column(name = "status")
@Column(name = "status", nullable = false)
@NotNull
private Status status;
@CascadeOnDelete
@OneToMany(mappedBy = "action", targetEntity = JpaActionStatus.class, fetch = FetchType.LAZY, cascade = {
CascadeType.REMOVE })
private List<ActionStatus> actionStatus;
@OneToMany(mappedBy = "action", targetEntity = JpaActionStatus.class, fetch = FetchType.LAZY)
private List<JpaActionStatus> actionStatus;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "rolloutgroup", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rolloutgroup"))
@JoinColumn(name = "rolloutgroup", updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rolloutgroup"))
private JpaRolloutGroup rolloutGroup;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "rollout", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rollout"))
@JoinColumn(name = "rollout", updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rollout"))
private JpaRollout rollout;
@Override
@@ -185,13 +185,15 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new ActionCreatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
.publishEvent(new ActionCreatedEvent(this, BaseEntity.getIdOrNull(rollout),
BaseEntity.getIdOrNull(rolloutGroup), EventPublisherHolder.getInstance().getApplicationId()));
}
@Override
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new ActionUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
.publishEvent(new ActionUpdatedEvent(this, BaseEntity.getIdOrNull(rollout),
BaseEntity.getIdOrNull(rolloutGroup), EventPublisherHolder.getInstance().getApplicationId()));
}
@Override

View File

@@ -54,11 +54,11 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
private Long occurredAt;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "action", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_act_stat_action"))
@JoinColumn(name = "action", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_act_stat_action"))
@NotNull
private JpaAction action;
@Column(name = "status")
@Column(name = "status", nullable = false, updatable = false)
@NotNull
private Status status;

View File

@@ -12,6 +12,7 @@ import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
@@ -49,7 +50,7 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art
@NotEmpty
private String filename;
@ManyToOne(optional = false, cascade = { CascadeType.PERSIST })
@ManyToOne(optional = false, cascade = { CascadeType.PERSIST }, fetch = FetchType.LAZY)
@JoinColumn(name = "software_module", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_assigned_sm"))
private JpaSoftwareModule softwareModule;

View File

@@ -16,7 +16,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
@@ -45,9 +44,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent;
@@ -82,40 +79,38 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
@CascadeOnDelete
@ManyToMany(targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY)
@JoinTable(name = "sp_ds_module", joinColumns = {
@JoinColumn(name = "ds_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_ds")) }, inverseJoinColumns = {
@JoinColumn(name = "module_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_module")) })
@JoinColumn(name = "ds_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_ds")) }, inverseJoinColumns = {
@JoinColumn(name = "module_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_module")) })
private Set<SoftwareModule> modules;
@CascadeOnDelete
@ManyToMany(targetEntity = JpaDistributionSetTag.class)
@JoinTable(name = "sp_ds_dstag", joinColumns = {
@JoinColumn(name = "ds", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_ds")) }, inverseJoinColumns = {
@JoinColumn(name = "TAG", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_tag")) })
@JoinColumn(name = "ds", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_ds")) }, inverseJoinColumns = {
@JoinColumn(name = "TAG", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_tag")) })
private Set<DistributionSetTag> tags;
@Column(name = "deleted")
private boolean deleted;
@OneToMany(mappedBy = "assignedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY)
private List<Target> assignedToTargets;
private List<JpaTarget> assignedToTargets;
@OneToMany(mappedBy = "autoAssignDistributionSet", targetEntity = JpaTargetFilterQuery.class, fetch = FetchType.LAZY)
private List<TargetFilterQuery> autoAssignFilters;
@OneToMany(mappedBy = "installedDistributionSet", targetEntity = JpaTargetInfo.class, fetch = FetchType.LAZY)
private List<TargetInfo> installedAtTargets;
private List<JpaTargetInfo> installedAtTargets;
@OneToMany(mappedBy = "distributionSet", targetEntity = JpaAction.class, fetch = FetchType.LAZY)
private List<Action> actions;
private List<JpaAction> actions;
@CascadeOnDelete
@OneToMany(fetch = FetchType.LAZY, targetEntity = JpaDistributionSetMetadata.class, cascade = {
CascadeType.REMOVE })
@JoinColumn(name = "ds_id", insertable = false, updatable = false)
@OneToMany(mappedBy = "distributionSet", fetch = FetchType.LAZY, targetEntity = JpaDistributionSetMetadata.class)
private List<DistributionSetMetadata> metadata;
@ManyToOne(fetch = FetchType.LAZY, targetEntity = JpaDistributionSetType.class)
@JoinColumn(name = "ds_id", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstype_ds"))
@ManyToOne(fetch = FetchType.LAZY, optional = false, targetEntity = JpaDistributionSetType.class)
@JoinColumn(name = "ds_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstype_ds"))
@NotNull
private DistributionSetType type;

View File

@@ -32,8 +32,8 @@ public class JpaDistributionSetMetadata extends JpaMetaData implements Distribut
private static final long serialVersionUID = 1L;
@Id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ds_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_ds"))
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "ds_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_ds"))
private JpaDistributionSet distributionSet;
public JpaDistributionSetMetadata() {

View File

@@ -19,7 +19,6 @@ import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@@ -29,6 +28,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.util.CollectionUtils;
@@ -49,12 +49,12 @@ import org.springframework.util.CollectionUtils;
public class JpaDistributionSetType extends AbstractJpaNamedEntity implements DistributionSetType {
private static final long serialVersionUID = 1L;
@OneToMany(targetEntity = DistributionSetTypeElement.class, cascade = {
CascadeType.ALL }, fetch = FetchType.EAGER, orphanRemoval = true)
@JoinColumn(name = "distribution_set_type", insertable = false, updatable = false)
@CascadeOnDelete
@OneToMany(mappedBy = "dsType", targetEntity = DistributionSetTypeElement.class, cascade = {
CascadeType.PERSIST }, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<DistributionSetTypeElement> elements;
@Column(name = "type_key", nullable = false, length = 64)
@Column(name = "type_key", nullable = false, updatable = false, length = 64)
@Size(max = 64)
@NotEmpty
private String key;

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
@@ -28,6 +29,7 @@ import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -35,7 +37,14 @@ import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.annotations.ConversionValue;
import org.eclipse.persistence.annotations.Convert;
import org.eclipse.persistence.annotations.ObjectTypeConverter;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.queries.UpdateObjectQuery;
import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
import org.hibernate.validator.constraints.NotEmpty;
/**
@@ -49,25 +58,41 @@ import org.hibernate.validator.constraints.NotEmpty;
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
// sub entities
@SuppressWarnings("squid:S2160")
@ObjectTypeConverter(name = "rolloutstatus", objectType = Rollout.RolloutStatus.class, dataType = Integer.class, conversionValues = {
@ConversionValue(objectValue = "CREATING", dataValue = "0"),
@ConversionValue(objectValue = "READY", dataValue = "1"),
@ConversionValue(objectValue = "PAUSED", dataValue = "2"),
@ConversionValue(objectValue = "STARTING", dataValue = "3"),
@ConversionValue(objectValue = "STOPPED", dataValue = "4"),
@ConversionValue(objectValue = "RUNNING", dataValue = "5"),
@ConversionValue(objectValue = "FINISHED", dataValue = "6"),
@ConversionValue(objectValue = "ERROR_CREATING", dataValue = "7"),
@ConversionValue(objectValue = "ERROR_STARTING", dataValue = "8"),
@ConversionValue(objectValue = "DELETING", dataValue = "9"),
@ConversionValue(objectValue = "DELETED", dataValue = "10") })
public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, EventAwareEntity {
private static final long serialVersionUID = 1L;
@OneToMany(targetEntity = JpaRolloutGroup.class)
@JoinColumn(name = "rollout", insertable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollout_rolloutgroup"))
private List<RolloutGroup> rolloutGroups;
private static final String DELETED_PROPERTY = "deleted";
@CascadeOnDelete
@OneToMany(targetEntity = JpaRolloutGroup.class, fetch = FetchType.LAZY, mappedBy = "rollout")
private List<JpaRolloutGroup> rolloutGroups;
@Column(name = "target_filter", length = 1024, nullable = false)
@Size(max = 1024)
@NotEmpty
private String targetFilterQuery;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "distribution_set", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolltout_ds"))
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "distribution_set", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolltout_ds"))
@NotNull
private JpaDistributionSet distributionSet;
@Column(name = "status")
@Column(name = "status", nullable = false)
@Convert("rolloutstatus")
@NotNull
private RolloutStatus status = RolloutStatus.CREATING;
@Column(name = "last_check")
@@ -87,6 +112,9 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
@Column(name = "rollout_groups_created")
private int rolloutGroupsCreated;
@Column(name = "deleted")
private boolean deleted;
@Column(name = "start_at")
private Long startAt;
@@ -142,7 +170,7 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
return startAt;
}
public void setStartAt(Long startAt) {
public void setStartAt(final Long startAt) {
this.startAt = startAt;
}
@@ -196,9 +224,8 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
@Override
public String toString() {
return "Rollout [rolloutGroups=" + rolloutGroups + ", targetFilterQuery=" + targetFilterQuery
+ ", distributionSet=" + distributionSet + ", status=" + status + ", lastCheck=" + lastCheck
+ ", getName()=" + getName() + ", getId()=" + getId() + "]";
return "Rollout [ targetFilterQuery=" + targetFilterQuery + ", distributionSet=" + distributionSet + ", status="
+ status + ", lastCheck=" + lastCheck + ", getName()=" + getName() + ", getId()=" + getId() + "]";
}
@Override
@@ -211,11 +238,35 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new RolloutUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
if (isSoftDeleted(descriptorEvent)) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutDeletedEvent(getTenant(),
getId(), getClass().getName(), EventPublisherHolder.getInstance().getApplicationId()));
}
}
private static boolean isSoftDeleted(final DescriptorEvent event) {
final ObjectChangeSet changeSet = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
final List<DirectToFieldChangeRecord> changes = changeSet.getChanges().stream()
.filter(record -> record instanceof DirectToFieldChangeRecord)
.map(record -> (DirectToFieldChangeRecord) record).collect(Collectors.toList());
return changes.stream().filter(record -> DELETED_PROPERTY.equals(record.getAttribute())
&& Boolean.parseBoolean(record.getNewValue().toString())).count() > 0;
}
@Override
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
// there is no rollout deletion event
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutDeletedEvent(getTenant(),
getId(), getClass().getName(), EventPublisherHolder.getInstance().getApplicationId()));
}
@Override
public boolean isDeleted() {
return deleted;
}
public void setDeleted(final boolean deleted) {
this.deleted = deleted;
}
}

View File

@@ -32,6 +32,7 @@ import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.annotations.ConversionValue;
import org.eclipse.persistence.annotations.Convert;
import org.eclipse.persistence.annotations.ObjectTypeConverter;
@@ -60,18 +61,20 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
private static final long serialVersionUID = 1L;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "rollout", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolloutgroup_rollout"))
@JoinColumn(name = "rollout", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolloutgroup_rollout"))
private JpaRollout rollout;
@Column(name = "status")
@Column(name = "status", nullable = false)
@Convert("rolloutgroupstatus")
private RolloutGroupStatus status = RolloutGroupStatus.CREATING;
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST }, targetEntity = RolloutTargetGroup.class)
@JoinColumn(name = "rolloutGroup_Id", insertable = false, updatable = false)
@CascadeOnDelete
@OneToMany(mappedBy = "rolloutGroup", fetch = FetchType.LAZY, cascade = {
CascadeType.PERSIST }, targetEntity = RolloutTargetGroup.class)
private List<RolloutTargetGroup> rolloutTargetGroup;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolloutgroup_rolloutgroup"))
private JpaRolloutGroup parent;
@Column(name = "success_condition", nullable = false)
@@ -287,8 +290,7 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
@Override
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutGroupUpdatedEvent(this,
EventPublisherHolder.getInstance().getApplicationId()));
this.getRollout().getId(), EventPublisherHolder.getInstance().getApplicationId()));
}
@Override

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.persistence.CascadeType;
import javax.persistence.Column;
@@ -36,11 +37,13 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedE
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.queries.UpdateObjectQuery;
import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
/**
* Base Software Module that is supported by OS level provisioning mechanism on
@@ -60,8 +63,10 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implements SoftwareModule, EventAwareEntity {
private static final long serialVersionUID = 1L;
private static final String DELETED_PROPERTY = "deleted";
@ManyToOne
@JoinColumn(name = "module_type", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_module_type"))
@JoinColumn(name = "module_type", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_module_type"))
@NotNull
private JpaSoftwareModuleType type;
@@ -77,13 +82,12 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
@CascadeOnDelete
@OneToMany(fetch = FetchType.LAZY, mappedBy = "softwareModule", cascade = {
CascadeType.ALL }, targetEntity = JpaArtifact.class)
private List<Artifact> artifacts;
CascadeType.PERSIST }, targetEntity = JpaArtifact.class, orphanRemoval = true)
private List<JpaArtifact> artifacts;
@CascadeOnDelete
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaSoftwareModuleMetadata.class)
@JoinColumn(name = "sw_id", insertable = false, updatable = false)
private List<SoftwareModuleMetadata> metadata;
@OneToMany(mappedBy = "softwareModule", fetch = FetchType.LAZY, targetEntity = JpaSoftwareModuleMetadata.class)
private List<JpaSoftwareModuleMetadata> metadata;
/**
* Default constructor.
@@ -116,12 +120,12 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
public void addArtifact(final Artifact artifact) {
if (null == artifacts) {
artifacts = new ArrayList<>(4);
artifacts.add(artifact);
artifacts.add((JpaArtifact) artifact);
return;
}
if (!artifacts.contains(artifact)) {
artifacts.add(artifact);
artifacts.add((JpaArtifact) artifact);
}
}
@@ -206,6 +210,21 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
new SoftwareModuleUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
if (isSoftDeleted(descriptorEvent)) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new SoftwareModuleDeletedEvent(
getTenant(), getId(), getClass().getName(), EventPublisherHolder.getInstance().getApplicationId()));
}
}
private static boolean isSoftDeleted(final DescriptorEvent event) {
final ObjectChangeSet changeSet = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
final List<DirectToFieldChangeRecord> changes = changeSet.getChanges().stream()
.filter(record -> record instanceof DirectToFieldChangeRecord)
.map(record -> (DirectToFieldChangeRecord) record).collect(Collectors.toList());
return changes.stream().filter(record -> DELETED_PROPERTY.equals(record.getAttribute())
&& Boolean.parseBoolean(record.getNewValue().toString())).count() > 0;
}
@Override

View File

@@ -32,8 +32,8 @@ public class JpaSoftwareModuleMetadata extends JpaMetaData implements SoftwareMo
private static final long serialVersionUID = 1L;
@Id
@ManyToOne(targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY)
@JoinColumn(name = "sw_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_sw"))
@ManyToOne(optional = false, targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY)
@JoinColumn(name = "sw_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_sw"))
private SoftwareModule softwareModule;
public JpaSoftwareModuleMetadata() {

View File

@@ -87,22 +87,21 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
@CascadeOnDelete
@ManyToMany(targetEntity = JpaTargetTag.class)
@JoinTable(name = "sp_target_target_tag", joinColumns = {
@JoinColumn(name = "target", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_target")) }, inverseJoinColumns = {
@JoinColumn(name = "tag", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_tag")) })
@JoinColumn(name = "target", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_target")) }, inverseJoinColumns = {
@JoinColumn(name = "tag", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_tag")) })
private Set<TargetTag> tags;
@CascadeOnDelete
@OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, cascade = {
CascadeType.REMOVE }, targetEntity = JpaAction.class)
@JoinColumn(name = "target", insertable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_act_hist_targ"))
private List<Action> actions;
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, targetEntity = JpaAction.class)
private List<JpaAction> actions;
@ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = JpaDistributionSet.class)
@JoinColumn(name = "assigned_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_assign_ds"))
private JpaDistributionSet assignedDistributionSet;
@CascadeOnDelete
@OneToOne(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY, targetEntity = JpaTargetInfo.class)
@OneToOne(cascade = { CascadeType.PERSIST,
CascadeType.MERGE }, fetch = FetchType.LAZY, targetEntity = JpaTargetInfo.class)
@PrimaryKeyJoinColumn
private JpaTargetInfo targetInfo;
@@ -110,14 +109,13 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
* the security token of the target which allows if enabled to authenticate
* with this security token.
*/
@Column(name = "sec_token", insertable = true, updatable = true, nullable = false, length = 128)
@Size(max = 64)
@Column(name = "sec_token", updatable = true, nullable = false, length = 128)
@Size(max = 128)
@NotEmpty
private String securityToken;
@CascadeOnDelete
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE, CascadeType.PERSIST })
@JoinColumn(name = "target_Id", insertable = false, updatable = false)
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
private List<RolloutTargetGroup> rolloutTargetGroup;
/**
@@ -214,7 +212,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
actions = new ArrayList<>();
}
return actions.add(action);
return actions.add((JpaAction) action);
}
@Override

View File

@@ -79,8 +79,7 @@ public class JpaTargetInfo implements Persistable<Long>, TargetInfo {
private boolean entityNew;
@CascadeOnDelete
@OneToOne(cascade = { CascadeType.MERGE,
CascadeType.REMOVE }, fetch = FetchType.LAZY, targetEntity = JpaTarget.class)
@OneToOne(cascade = { CascadeType.MERGE }, fetch = FetchType.LAZY, targetEntity = JpaTarget.class)
@JoinColumn(name = "target_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_stat_targ"))
@MapsId
private JpaTarget target;
@@ -112,7 +111,7 @@ public class JpaTargetInfo implements Persistable<Long>, TargetInfo {
@Column(name = "attribute_value", length = 128)
@MapKeyColumn(name = "attribute_key", nullable = false, length = 32)
@CollectionTable(name = "sp_target_attributes", joinColumns = {
@JoinColumn(name = "target_id") }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target"))
@JoinColumn(name = "target_id", nullable = false, updatable = false) }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target"))
private final Map<String, String> controllerAttributes = Collections.synchronizedMap(new HashMap<String, String>());
// set default request controller attributes to true, because we want to

View File

@@ -32,7 +32,7 @@ import org.hibernate.validator.constraints.NotEmpty;
public class JpaTenantConfiguration extends AbstractJpaTenantAwareBaseEntity implements TenantConfiguration {
private static final long serialVersionUID = 1L;
@Column(name = "conf_key", length = 128, nullable = false)
@Column(name = "conf_key", length = 128, nullable = false, updatable = false)
@Size(max = 128)
@NotEmpty
private String key;

View File

@@ -43,7 +43,7 @@ import org.eclipse.hawkbit.repository.model.TenantMetaData;
public class JpaTenantMetaData extends AbstractJpaBaseEntity implements TenantMetaData {
private static final long serialVersionUID = 1L;
@Column(name = "tenant", nullable = false, length = 40)
@Column(name = "tenant", nullable = false, updatable = false, length = 40)
@Size(max = 40)
private String tenant;

View File

@@ -24,7 +24,6 @@ import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.persistence.annotations.ExistenceChecking;
@@ -44,19 +43,22 @@ public class RolloutTargetGroup implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@ManyToOne(targetEntity = JpaRolloutGroup.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
@JoinColumn(name = "rolloutGroup_Id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_group"))
private RolloutGroup rolloutGroup;
@ManyToOne(optional = false, targetEntity = JpaRolloutGroup.class, fetch = FetchType.LAZY, cascade = {
CascadeType.PERSIST })
@JoinColumn(name = "rolloutGroup_Id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_group"))
private JpaRolloutGroup rolloutGroup;
@Id
@ManyToOne(targetEntity = JpaTarget.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
@JoinColumn(name = "target_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_target"))
@ManyToOne(optional = false, targetEntity = JpaTarget.class, fetch = FetchType.LAZY, cascade = {
CascadeType.PERSIST })
@JoinColumn(name = "target_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_target"))
private JpaTarget target;
@OneToMany(targetEntity = JpaAction.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
@JoinColumns(value = { @JoinColumn(name = "rolloutgroup", referencedColumnName = "rolloutGroup_Id"),
@JoinColumn(name = "target", referencedColumnName = "target_id") })
private List<Action> actions;
@JoinColumns(value = {
@JoinColumn(name = "rolloutgroup", nullable = false, updatable = false, referencedColumnName = "rolloutGroup_Id"),
@JoinColumn(name = "target", nullable = false, updatable = false, referencedColumnName = "target_id") })
private List<JpaAction> actions;
/**
* default constructor for JPA.
@@ -66,7 +68,7 @@ public class RolloutTargetGroup implements Serializable {
}
public RolloutTargetGroup(final RolloutGroup rolloutGroup, final Target target) {
this.rolloutGroup = rolloutGroup;
this.rolloutGroup = (JpaRolloutGroup) rolloutGroup;
this.target = (JpaTarget) target;
}

View File

@@ -9,63 +9,78 @@
package org.eclipse.hawkbit.repository.jpa.rollout;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorCompletionService;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.RolloutProperties;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.google.common.base.Throwables;
/**
* Scheduler to schedule the
* {@link RolloutManagement#checkRunningRollouts(long)}. The delay between the
* checks be be configured using the properties from {@link RolloutProperties}.
* Scheduler to schedule the {@link RolloutManagement#handleRollouts()}. The
* delay between the checks be be configured using the property from
* {#PROP_SCHEDULER_DELAY_PLACEHOLDER}.
*/
@Component
// don't active the rollout scheduler in test, otherwise it is hard to test
// rolloutmanagement and leads weird side-effects maybe.
@Profile("!test")
public class RolloutScheduler {
private static final Logger LOGGER = LoggerFactory.getLogger(RolloutScheduler.class);
@Autowired
private TenantAware tenantAware;
private static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.rollout.scheduler.fixedDelay:2000}";
@Autowired
private SystemManagement systemManagement;
private final TenantAware tenantAware;
@Autowired
private RolloutManagement rolloutManagement;
private final SystemManagement systemManagement;
@Autowired
private SystemSecurityContext systemSecurityContext;
private final RolloutManagement rolloutManagement;
@Autowired
private RolloutProperties rolloutProperties;
private final SystemSecurityContext systemSecurityContext;
private final ExecutorCompletionService<Void> completionService;
/**
* Constructor.
*
* @param tenantAware
* to run as specific tenant
* @param systemManagement
* to find all tenants
* @param rolloutManagement
* to run the rollout handler
* @param systemSecurityContext
* to run as system
* @param threadPoolExecutor
* to execute the handlers in parallel
*/
public RolloutScheduler(final TenantAware tenantAware, final SystemManagement systemManagement,
final RolloutManagement rolloutManagement, final SystemSecurityContext systemSecurityContext,
final Executor threadPoolExecutor) {
this.tenantAware = tenantAware;
this.systemManagement = systemManagement;
this.rolloutManagement = rolloutManagement;
this.systemSecurityContext = systemSecurityContext;
completionService = new ExecutorCompletionService<>(threadPoolExecutor);
}
/**
* Scheduler method called by the spring-async mechanism. Retrieves all
* tenants from the {@link SystemManagement#findTenants()} and runs for each
* tenant the {@link RolloutManagement#checkRunningRollouts(long)} in the
* tenant the {@link RolloutManagement#handleRollouts()} in the
* {@link SystemSecurityContext}.
*/
@Scheduled(initialDelayString = RolloutProperties.PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = RolloutProperties.PROP_SCHEDULER_DELAY_PLACEHOLDER)
@Scheduled(initialDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER)
public void runningRolloutScheduler() {
if (!rolloutProperties.getScheduler().isEnabled()) {
return;
}
LOGGER.debug("rollout schedule checker has been triggered.");
// run this code in system code privileged to have the necessary
// permission to query and create entities.
systemSecurityContext.runAsSystem(() -> {
final int tasks = systemSecurityContext.runAsSystem(() -> {
// workaround eclipselink that is currently not possible to
// execute a query without multitenancy if MultiTenant
// annotation is used.
@@ -75,119 +90,25 @@ public class RolloutScheduler {
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking rollouts for {} tenants", tenants.size());
for (final String tenant : tenants) {
tenantAware.runAsTenant(tenant, () -> {
final long fixedDelay = rolloutProperties.getScheduler().getFixedDelay();
rolloutManagement.checkRunningRollouts(fixedDelay);
completionService.submit(() -> tenantAware.runAsTenant(tenant, () -> {
rolloutManagement.handleRollouts();
return null;
});
}));
}
return null;
return tenants.size();
});
waitUntilHandlersAreComplete(tasks);
}
/**
* Scheduler method called by the spring-async mechanism. Retrieves all
* tenants from the {@link SystemManagement#findTenants()} and runs for each
* tenant the {@link RolloutManagement#checkStartingRollouts(long)} in the
* {@link SystemSecurityContext}.
*/
@Scheduled(initialDelayString = RolloutProperties.PROP_STARTING_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = RolloutProperties.PROP_STARTING_SCHEDULER_DELAY_PLACEHOLDER)
public void startingRolloutScheduler() {
if (!rolloutProperties.getStartingScheduler().isEnabled()) {
return;
}
LOGGER.debug("rollout starting schedule checker has been triggered.");
// run this code in system code privileged to have the necessary
// permission to query and create entities.
systemSecurityContext.runAsSystem(() -> {
// workaround eclipselink that is currently not possible to
// execute a query without multitenancy if MultiTenant
// annotation is used.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
// iterate through all tenants and execute the rollout check for
// each tenant seperately.
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking starting rollouts for {} tenants", tenants.size());
for (final String tenant : tenants) {
tenantAware.runAsTenant(tenant, () -> {
final long fixedDelay = rolloutProperties.getStartingScheduler().getFixedDelay();
rolloutManagement.checkStartingRollouts(fixedDelay);
return null;
});
private void waitUntilHandlersAreComplete(final int tasks) {
try {
for (int i = 0; i < tasks; i++) {
completionService.take().get();
}
return null;
});
}
/**
* Scheduler method called by the spring-async mechanism. Retrieves all
* tenants from the {@link SystemManagement#findTenants()} and runs for each
* tenant the {@link RolloutManagement#checkCreatingRollouts(long)} in the
* {@link SystemSecurityContext}.
*/
@Scheduled(initialDelayString = RolloutProperties.PROP_CREATING_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = RolloutProperties.PROP_CREATING_SCHEDULER_DELAY_PLACEHOLDER)
public void creatingRolloutScheduler() {
if (!rolloutProperties.getCreatingScheduler().isEnabled()) {
return;
} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt();
throw Throwables.propagate(e);
}
LOGGER.debug("rollout creating schedule checker has been triggered.");
// run this code in system code privileged to have the necessary
// permission to query and create entities.
systemSecurityContext.runAsSystem(() -> {
// workaround eclipselink that is currently not possible to
// execute a query without multitenancy if MultiTenant
// annotation is used.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
// iterate through all tenants and execute the rollout check for
// each tenant seperately.
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking creating rollouts for {} tenants", tenants.size());
for (final String tenant : tenants) {
tenantAware.runAsTenant(tenant, () -> {
final long fixedDelay = rolloutProperties.getCreatingScheduler().getFixedDelay();
rolloutManagement.checkCreatingRollouts(fixedDelay);
return null;
});
}
return null;
});
}
/**
* Scheduler method called by the spring-async mechanism. Retrieves all
* tenants from the {@link SystemManagement#findTenants()} and runs for each
* tenant the {@link RolloutManagement#checkReadyRollouts(long)} in the
* {@link SystemSecurityContext}. Used to auto start Rollouts as soon as
* their startAt time is reached.
*/
@Scheduled(initialDelayString = RolloutProperties.PROP_READY_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = RolloutProperties.PROP_READY_SCHEDULER_DELAY_PLACEHOLDER)
public void readyRolloutScheduler() {
if (!rolloutProperties.getReadyScheduler().isEnabled()) {
return;
}
LOGGER.debug("rollout ready schedule checker has been triggered.");
// run this code in system code privileged to have the necessary
// permission to query and create entities.
systemSecurityContext.runAsSystem(() -> {
// workaround eclipselink that is currently not possible to
// execute a query without multitenancy if MultiTenant
// annotation is used.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
// iterate through all tenants and execute the rollout check for
// each tenant seperately.
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking ready rollouts for {} tenants", tenants.size());
for (final String tenant : tenants) {
tenantAware.runAsTenant(tenant, () -> {
final long fixedDelay = rolloutProperties.getReadyScheduler().getFixedDelay();
rolloutManagement.checkReadyRollouts(fixedDelay);
return null;
});
}
return null;
});
}
}

View File

@@ -71,7 +71,7 @@ public class StartNextGroupRolloutGroupSuccessAction implements RolloutGroupActi
// scheduled. If the group is empty now, we just finish the group if
// there are not actions available for this group.
final List<JpaRolloutGroup> findByRolloutGroupParent = rolloutGroupRepository
.findByParentAndStatus((JpaRolloutGroup) rolloutGroup, RolloutGroupStatus.SCHEDULED);
.findByParentIdAndStatus(rolloutGroup.getId(), RolloutGroupStatus.SCHEDULED);
findByRolloutGroupParent.forEach(nextGroup -> {
logger.debug("Rolloutgroup {} is finished, starting next group", nextGroup);
nextGroup.setStatus(RolloutGroupStatus.FINISHED);

View File

@@ -20,8 +20,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Target;
import org.springframework.data.jpa.domain.Specification;
/**
@@ -37,25 +37,50 @@ public final class ActionSpecifications {
/**
* Specification which joins all necessary tables to retrieve the dependency
* between a target and a local file assignment through the assigen action
* between a target and a local file assignment through the assigned action
* of the target. All actions are included, not only active actions.
*
* @param target
* the target to verfiy if the given artifact is currently
* @param controllerId
* the target to verify if the given artifact is currently
* assigned or had been assigned
* @param sha1Hash
* of the local artifact to check wherever the target had ever
* been assigned
* @return a specification to use with spring JPA
*/
public static Specification<JpaAction> hasTargetAssignedArtifact(final Target target, final String sha1Hash) {
public static Specification<JpaAction> hasTargetAssignedArtifact(final String controllerId, final String sha1Hash) {
return (actionRoot, query, criteriaBuilder) -> {
final Join<JpaAction, JpaDistributionSet> dsJoin = actionRoot.join(JpaAction_.distributionSet);
final SetJoin<JpaDistributionSet, JpaSoftwareModule> modulesJoin = dsJoin.join(JpaDistributionSet_.modules);
final ListJoin<JpaSoftwareModule, JpaArtifact> artifactsJoin = modulesJoin
.join(JpaSoftwareModule_.artifacts);
return criteriaBuilder.and(criteriaBuilder.equal(artifactsJoin.get(JpaArtifact_.sha1Hash), sha1Hash),
criteriaBuilder.equal(actionRoot.get(JpaAction_.target), target));
criteriaBuilder.equal(actionRoot.get(JpaAction_.target).get(JpaTarget_.controllerId),
controllerId));
};
}
/**
* Specification which joins all necessary tables to retrieve the dependency
* between a target and a local file assignment through the assigned action
* of the target. All actions are included, not only active actions.
*
* @param targetId
* the target to verify if the given artifact is currently
* assigned or had been assigned
* @param sha1Hash
* of the local artifact to check wherever the target had ever
* been assigned
* @return a specification to use with spring JPA
*/
public static Specification<JpaAction> hasTargetAssignedArtifact(final Long targetId, final String sha1Hash) {
return (actionRoot, query, criteriaBuilder) -> {
final Join<JpaAction, JpaDistributionSet> dsJoin = actionRoot.join(JpaAction_.distributionSet);
final SetJoin<JpaDistributionSet, JpaSoftwareModule> modulesJoin = dsJoin.join(JpaDistributionSet_.modules);
final ListJoin<JpaSoftwareModule, JpaArtifact> artifactsJoin = modulesJoin
.join(JpaSoftwareModule_.artifacts);
return criteriaBuilder.and(criteriaBuilder.equal(artifactsJoin.get(JpaArtifact_.sha1Hash), sha1Hash),
criteriaBuilder.equal(actionRoot.get(JpaAction_.target).get(JpaTarget_.id), targetId));
};
}
}

View File

@@ -0,0 +1,40 @@
/**
* 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.repository.jpa.specifications;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.springframework.data.jpa.domain.Specification;
/**
* Specifications class for {@link Rollout}s. The class provides Spring Data
* JPQL Specifications.
*
*/
public final class RolloutSpecification {
private RolloutSpecification() {
// utility class
}
/**
* {@link Specification} for retrieving {@link Rollout}s by its DELETED
* attribute.
*
* @param isDeleted
* TRUE/FALSE are compared to the attribute DELETED. If NULL the
* attribute is ignored
* @return the {@link Rollout} {@link Specification}
*/
public static Specification<JpaRollout> isDeleted(final Boolean isDeleted) {
return (root, query, cb) -> cb.equal(root.<Boolean> get(JpaRollout_.deleted), isDeleted);
}
}

View File

@@ -0,0 +1,26 @@
ALTER TABLE sp_rollout ADD COLUMN deleted BOOLEAN;
UPDATE sp_rollout SET deleted = 0;
ALTER TABLE sp_action MODIFY target BIGINT NOT NULL;
ALTER TABLE sp_action MODIFY distribution_set BIGINT NOT NULL;
ALTER TABLE sp_action MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_action_status MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_rollout MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_rollout MODIFY distribution_set BIGINT NOT NULL;
ALTER TABLE sp_rolloutgroup MODIFY rollout BIGINT NOT NULL;
ALTER TABLE sp_rolloutgroup MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_ds_type_element DROP CONSTRAINT fk_ds_type_element_element;
ALTER TABLE sp_ds_type_element
ADD CONSTRAINT fk_ds_type_element_element
FOREIGN KEY (distribution_set_type)
REFERENCES sp_distribution_set_type (id)
ON DELETE CASCADE;
ALTER TABLE sp_ds_type_element DROP CONSTRAINT fk_ds_type_element_smtype;
ALTER TABLE sp_ds_type_element
ADD CONSTRAINT fk_ds_type_element_smtype
FOREIGN KEY (software_module_type)
REFERENCES sp_software_module_type (id)
ON DELETE CASCADE;

View File

@@ -0,0 +1,26 @@
ALTER TABLE sp_rollout ADD COLUMN deleted BOOLEAN;
UPDATE sp_rollout SET deleted = 0;
ALTER TABLE sp_action MODIFY target BIGINT NOT NULL;
ALTER TABLE sp_action MODIFY distribution_set BIGINT NOT NULL;
ALTER TABLE sp_action MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_action_status MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_rollout MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_rollout MODIFY distribution_set BIGINT NOT NULL;
ALTER TABLE sp_rolloutgroup MODIFY rollout BIGINT NOT NULL;
ALTER TABLE sp_rolloutgroup MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_ds_type_element DROP FOREIGN KEY fk_ds_type_element_element;
ALTER TABLE sp_ds_type_element
ADD CONSTRAINT fk_ds_type_element_element
FOREIGN KEY (distribution_set_type)
REFERENCES sp_distribution_set_type (id)
ON DELETE CASCADE;
ALTER TABLE sp_ds_type_element DROP FOREIGN KEY fk_ds_type_element_smtype;
ALTER TABLE sp_ds_type_element
ADD CONSTRAINT fk_ds_type_element_smtype
FOREIGN KEY (software_module_type)
REFERENCES sp_software_module_type (id)
ON DELETE CASCADE;

View File

@@ -65,6 +65,12 @@ public class RemoteIdEventTest extends AbstractRemoteEventTest {
assertAndCreateRemoteEvent(SoftwareModuleDeletedEvent.class);
}
@Test
@Description("Verifies that the rollout id is correct reloaded")
public void testRolloutDeletedEvent() {
assertAndCreateRemoteEvent(RolloutDeletedEvent.class);
}
protected void assertAndCreateRemoteEvent(final Class<? extends RemoteIdEvent> eventType) {
final Constructor<?> constructor = Arrays.stream(eventType.getDeclaredConstructors())

View File

@@ -13,6 +13,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.junit.Test;
@@ -46,6 +47,7 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
final Target target = testdataFactory.createTarget("Test");
generateAction.setTarget(target);
generateAction.setDistributionSet(dsA);
generateAction.setStatus(Status.RUNNING);
final Action action = actionRepository.save(generateAction);
final TargetAssignDistributionSetEvent assignmentEvent = new TargetAssignDistributionSetEvent(action,

View File

@@ -8,9 +8,16 @@
*/
package org.eclipse.hawkbit.repository.event.remote.entity;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import java.lang.reflect.Constructor;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.junit.Test;
@@ -35,7 +42,49 @@ public class ActionEventTest extends AbstractRemoteEntityEventTest<Action> {
@Description("Verifies that the action entity reloading by remote updated works")
public void testActionUpdatedEvent() {
assertAndCreateRemoteEvent(ActionUpdatedEvent.class);
}
@Override
protected RemoteEntityEvent<?> createRemoteEvent(final Action baseEntity,
final Class<? extends RemoteEntityEvent<?>> eventType) {
Constructor<?> constructor = null;
for (final Constructor<?> constructors : eventType.getDeclaredConstructors()) {
if (constructors.getParameterCount() == 4) {
constructor = constructors;
}
}
if (constructor == null) {
throw new IllegalArgumentException("No suitable constructor foundes");
}
try {
return (RemoteEntityEvent<?>) constructor.newInstance(baseEntity, 1L, 2L, "Node");
} catch (final ReflectiveOperationException e) {
fail("Exception should not happen " + e.getMessage());
}
return null;
}
@Override
protected RemoteEntityEvent<?> assertEntity(final Action baseEntity, final RemoteEntityEvent<?> e) {
final AbstractActionEvent event = (AbstractActionEvent) e;
assertThat(event.getEntity()).isSameAs(baseEntity);
assertThat(event.getRolloutId()).isEqualTo(1L);
AbstractActionEvent underTestCreatedEvent = (AbstractActionEvent) createProtoStuffEvent(event);
assertThat(underTestCreatedEvent.getEntity()).isEqualTo(baseEntity);
assertThat(underTestCreatedEvent.getRolloutId()).isEqualTo(1L);
assertThat(underTestCreatedEvent.getRolloutGroupId()).isEqualTo(2L);
underTestCreatedEvent = (AbstractActionEvent) createJacksonEvent(event);
assertThat(underTestCreatedEvent.getEntity()).isEqualTo(baseEntity);
assertThat(underTestCreatedEvent.getRolloutId()).isEqualTo(1L);
assertThat(underTestCreatedEvent.getRolloutGroupId()).isEqualTo(2L);
return underTestCreatedEvent;
}
@Override
@@ -43,8 +92,10 @@ public class ActionEventTest extends AbstractRemoteEntityEventTest<Action> {
final JpaAction generateAction = new JpaAction();
generateAction.setActionType(ActionType.FORCED);
final Target target = testdataFactory.createTarget("Test");
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
generateAction.setTarget(target);
generateAction.setDistributionSet(testdataFactory.createDistributionSet());
generateAction.setDistributionSet(distributionSet);
generateAction.setStatus(Status.RUNNING);
return actionRepository.save(generateAction);
}

View File

@@ -8,8 +8,10 @@
*/
package org.eclipse.hawkbit.repository.event.remote.entity;
import static org.junit.Assert.fail;
import static org.assertj.core.api.Assertions.assertThat;
import java.lang.reflect.Constructor;
import java.util.UUID;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -44,6 +46,47 @@ public class RolloutGroupEventTest extends AbstractRemoteEntityEventTest<Rollout
assertAndCreateRemoteEvent(RolloutGroupUpdatedEvent.class);
}
@Override
protected RemoteEntityEvent<?> createRemoteEvent(final RolloutGroup baseEntity,
final Class<? extends RemoteEntityEvent<?>> eventType) {
Constructor<?> constructor = null;
for (final Constructor<?> constructors : eventType.getDeclaredConstructors()) {
if (constructors.getParameterCount() == 3) {
constructor = constructors;
}
}
if (constructor == null) {
throw new IllegalArgumentException("No suitable constructor foundes");
}
try {
return (RemoteEntityEvent<?>) constructor.newInstance(baseEntity, 1L, "Node");
} catch (final ReflectiveOperationException e) {
fail("Exception should not happen " + e.getMessage());
}
return null;
}
@Override
protected RemoteEntityEvent<?> assertEntity(final RolloutGroup baseEntity, final RemoteEntityEvent<?> e) {
final AbstractRolloutGroupEvent event = (AbstractRolloutGroupEvent) e;
assertThat(event.getEntity()).isSameAs(baseEntity);
assertThat(event.getRolloutId()).isEqualTo(1L);
AbstractRolloutGroupEvent underTestCreatedEvent = (AbstractRolloutGroupEvent) createProtoStuffEvent(event);
assertThat(underTestCreatedEvent.getEntity()).isEqualTo(baseEntity);
assertThat(underTestCreatedEvent.getRolloutId()).isEqualTo(1L);
underTestCreatedEvent = (AbstractRolloutGroupEvent) createJacksonEvent(event);
assertThat(underTestCreatedEvent.getEntity()).isEqualTo(baseEntity);
assertThat(underTestCreatedEvent.getRolloutId()).isEqualTo(1L);
return underTestCreatedEvent;
}
@Override
protected RolloutGroup createEntity() {
testdataFactory.createTarget(UUID.randomUUID().toString());

View File

@@ -31,6 +31,8 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import com.google.common.collect.Lists;
@SpringApplicationConfiguration(classes = {
org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration.class })
public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest {
@@ -80,6 +82,9 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
@Autowired
protected RolloutGroupRepository rolloutGroupRepository;
@Autowired
protected RolloutTargetGroupRepository rolloutTargetGroupRepository;
@Autowired
protected RolloutRepository rolloutRepository;
@@ -91,7 +96,7 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
protected List<Action> findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) {
return actionRepository.findByRolloutIdAndStatus(rollout.getId(), actionStatus);
return Lists.newArrayList(actionRepository.findByRolloutIdAndStatus(pageReq, rollout.getId(), actionStatus));
}
protected TargetTagAssignmentResult toggleTagAssignment(final Collection<Target> targets, final TargetTag tag) {

View File

@@ -9,23 +9,29 @@
package org.eclipse.hawkbit.repository.jpa;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.HashGeneratorUtils;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.Test;
@@ -35,24 +41,47 @@ import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
/**
* Test class for {@link ArtifactManagement} with running MongoDB instance..
*
*
*
*
* Test class for {@link ArtifactManagement}.
*/
@Features("Component Tests - Repository")
@Stories("Artifact Management")
public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
/**
* Test method for
* {@link org.eclipse.hawkbit.repository.ArtifactManagement#createArtifact(java.io.InputStream)}
* .
*
* @throws IOException
* @throws NoSuchAlgorithmException
*/
@Test
@Description("Verifies that management queries react as specfied on calls for non existing entities.")
@ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
public void nonExistingEntityQueries() throws URISyntaxException {
final SoftwareModule module = testdataFactory.createSoftwareModuleOs();
assertThatThrownBy(() -> artifactManagement.createArtifact(IOUtils.toInputStream("test", "UTF-8"), 1234L, "xxx",
null, null, false, null)).isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("SoftwareModule");
assertThatThrownBy(
() -> artifactManagement.createArtifact(IOUtils.toInputStream("test", "UTF-8"), 1234L, "xxx", false))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("SoftwareModule");
assertThatThrownBy(() -> artifactManagement.deleteArtifact(1234L)).isInstanceOf(EntityNotFoundException.class)
.hasMessageContaining("1234").hasMessageContaining("Artifact");
assertThat(artifactManagement.findArtifact(1234L).isPresent()).isFalse();
assertThatThrownBy(() -> artifactManagement.findArtifactBySoftwareModule(pageReq, 1234L))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("SoftwareModule");
assertThat(artifactManagement.findArtifactByFilename("1234").isPresent()).isFalse();
assertThatThrownBy(() -> artifactManagement.findByFilenameAndSoftwareModule("xxx", 1234L))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("SoftwareModule");
assertThat(artifactManagement.findByFilenameAndSoftwareModule("1234", module.getId()).isPresent()).isFalse();
assertThat(artifactManagement.findFirstArtifactBySHA1("1234").isPresent()).isFalse();
assertThat(artifactManagement.loadArtifactBinary("1234").isPresent()).isFalse();
}
@Test
@Description("Test if a local artifact can be created by API including metadata.")
public void createArtifact() throws NoSuchAlgorithmException, IOException {
@@ -205,7 +234,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
final Artifact result = artifactManagement.createArtifact(new ByteArrayInputStream(random),
testdataFactory.createSoftwareModuleOs().getId(), "file1", false);
try (InputStream fileInputStream = artifactManagement.loadArtifactBinary(result.getSha1Hash())
try (InputStream fileInputStream = artifactManagement.loadArtifactBinary(result.getSha1Hash()).get()
.getFileInputStream()) {
assertTrue("The stored binary matches the given binary",
IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream));

View File

@@ -9,9 +9,12 @@
package org.eclipse.hawkbit.repository.jpa;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
@@ -29,10 +32,12 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedE
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
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.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
@@ -55,6 +60,75 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Autowired
private RepositoryProperties repositoryProperties;
@Test
@Description("Verifies that management queries react as specfied on calls for non existing entities.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
public void nonExistingEntityQueries() throws URISyntaxException {
final Target target = testdataFactory.createTarget();
final SoftwareModule module = testdataFactory.createSoftwareModuleOs();
assertThatThrownBy(() -> controllerManagement
.addCancelActionStatus(entityFactory.actionStatus().create(1234L).status(Action.Status.FINISHED)))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Action");
assertThatThrownBy(() -> controllerManagement
.addInformationalActionStatus(entityFactory.actionStatus().create(1234L).status(Action.Status.RUNNING)))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Action");
assertThatThrownBy(() -> controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(1234L).status(Action.Status.FINISHED)))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Action");
assertThat(controllerManagement.findActionWithDetails(1234L).isPresent()).isFalse();
assertThat(controllerManagement.findByControllerId("1234").isPresent()).isFalse();
assertThat(controllerManagement.findByTargetId(1234L).isPresent()).isFalse();
assertThatThrownBy(() -> controllerManagement.findOldestActiveActionByTarget("1234"))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Target");
assertThatThrownBy(() -> controllerManagement
.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), 1234L))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("SoftwareModule");
assertThatThrownBy(
() -> controllerManagement.getActionForDownloadByTargetAndSoftwareModule("1234", module.getId()))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Target");
assertThat(controllerManagement
.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), module.getId()).isPresent())
.isFalse();
assertThatThrownBy(() -> controllerManagement.hasTargetArtifactAssigned(1234L, "XXX"))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Target");
assertThatThrownBy(() -> controllerManagement.hasTargetArtifactAssigned("1234", "XXX"))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Target");
assertThat(controllerManagement.hasTargetArtifactAssigned(target.getControllerId(), "XXX")).isFalse();
assertThat(controllerManagement.hasTargetArtifactAssigned(target.getId(), "XXX")).isFalse();
assertThatThrownBy(() -> controllerManagement.registerRetrieved(1234L, "test message"))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Action");
assertThatThrownBy(() -> controllerManagement.updateControllerAttributes("1234", Maps.newHashMap()))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Target");
assertThatThrownBy(() -> controllerManagement.updateLastTargetQuery("1234", new URI("http://test.com")))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Target");
}
@Test
@Description("Controller confirms successfull update with FINISHED status.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@@ -68,7 +142,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
simulateIntermediateStatusOnUpdate(actionId);
controllerManagament
controllerManagement
.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);
@@ -91,7 +165,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
final Long actionId = createTargetAndAssignDs();
deploymentManagement.cancelAction(actionId);
controllerManagament
controllerManagement
.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);
@@ -111,7 +185,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
final Long actionId = createTargetAndAssignDs();
try {
controllerManagament.addCancelActionStatus(
controllerManagement.addCancelActionStatus(
entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
fail("Expected " + CancelActionNotAllowedException.class.getName());
} catch (final CancelActionNotAllowedException e) {
@@ -144,7 +218,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
simulateIntermediateStatusOnCancellation(actionId);
controllerManagament
controllerManagement
.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);
@@ -171,7 +245,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
simulateIntermediateStatusOnCancellation(actionId);
controllerManagament
controllerManagement
.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);
@@ -199,7 +273,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
simulateIntermediateStatusOnCancellation(actionId);
controllerManagament.addCancelActionStatus(
controllerManagement.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);
@@ -227,7 +301,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
simulateIntermediateStatusOnCancellation(actionId);
controllerManagament
controllerManagement
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.ERROR, true);
@@ -249,22 +323,22 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Step
private void simulateIntermediateStatusOnCancellation(final Long actionId) {
controllerManagament
controllerManagement
.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
controllerManagement
.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
controllerManagement
.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
controllerManagement
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.CANCELING, Action.Status.WARNING, true);
@@ -272,22 +346,22 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Step
private void simulateIntermediateStatusOnUpdate(final Long actionId) {
controllerManagament
controllerManagement
.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
controllerManagement
.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
controllerManagement
.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
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.WARNING, true);
@@ -305,7 +379,10 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
final List<ActionStatus> actionStatusList = deploymentManagement.findActionStatusByAction(pageReq, actionId)
.getContent();
assertThat(actionStatusList.get(actionStatusList.size() - 1).getStatus()).isEqualTo(expectedActionStatus);
if (actionActive) {
assertThat(controllerManagement.findOldestActiveActionByTarget(controllerId).get().getId())
.isEqualTo(actionId);
}
}
@Test
@@ -332,31 +409,31 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(artifact.getSha1Hash()).isEqualTo(artifact2.getSha1Hash());
assertThat(
controllerManagament.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact.getSha1Hash()))
controllerManagement.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact.getSha1Hash()))
.isFalse();
savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getAssignedEntity().iterator()
.next();
assertThat(
controllerManagament.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact.getSha1Hash()))
controllerManagement.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact.getSha1Hash()))
.isTrue();
assertThat(
controllerManagament.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact2.getSha1Hash()))
controllerManagement.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact2.getSha1Hash()))
.isTrue();
}
@Test
@Description("Register a controller which does not exist")
public void findOrRegisterTargetIfItDoesNotexist() {
final Target target = controllerManagament.findOrRegisterTargetIfItDoesNotexist("AA", null);
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist("AA", null);
assertThat(target).as("target should not be null").isNotNull();
final Target sameTarget = controllerManagament.findOrRegisterTargetIfItDoesNotexist("AA", null);
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotexist("AA", null);
assertThat(target).as("Target should be the equals").isEqualTo(sameTarget);
assertThat(targetRepository.count()).as("Only 1 target should be registred").isEqualTo(1L);
// throws exception
try {
controllerManagament.findOrRegisterTargetIfItDoesNotexist("", null);
controllerManagement.findOrRegisterTargetIfItDoesNotexist("", null);
fail("should fail as target does not exist");
} catch (final ConstraintViolationException e) {
@@ -375,19 +452,19 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
final Long actionId = createTargetAndAssignDs();
// test and verify
controllerManagament
controllerManagement
.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
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
Action.Status.ERROR, Action.Status.ERROR, false);
// try with disabled late feedback
repositoryProperties.setRejectActionStatusForClosedAction(true);
controllerManagament
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
// test
@@ -397,7 +474,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
// try with enabled late feedback - should not make a difference as it
// only allows intermediate feedbacks and not multiple close
repositoryProperties.setRejectActionStatusForClosedAction(false);
controllerManagament
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
// test
@@ -422,7 +499,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
// try with disabled late feedback
repositoryProperties.setRejectActionStatusForClosedAction(true);
controllerManagament
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
// test
@@ -432,7 +509,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
// try with enabled late feedback - should not make a difference as it
// only allows intermediate feedbacks and not multiple close
repositoryProperties.setRejectActionStatusForClosedAction(false);
controllerManagament
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
// test
@@ -458,7 +535,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
final Action action = prepareFinishedUpdate();
controllerManagament.addUpdateActionStatus(
controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING));
// nothing changed as "feedback after close" is disabled
@@ -483,7 +560,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
repositoryProperties.setRejectActionStatusForClosedAction(false);
Action action = prepareFinishedUpdate();
action = controllerManagament.addUpdateActionStatus(
action = controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING));
// nothing changed as "feedback after close" is disabled
@@ -512,7 +589,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
private void addAttributeAndVerify(final String controllerId) {
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(1);
testData.put("test1", "testdata1");
controllerManagament.updateControllerAttributes(controllerId, testData);
controllerManagement.updateControllerAttributes(controllerId, testData);
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId).get();
assertThat(target.getTargetInfo().getControllerAttributes()).as("Controller Attributes are wrong")
@@ -523,7 +600,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
private void addSecondAttributeAndVerify(final String controllerId) {
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(2);
testData.put("test2", "testdata20");
controllerManagament.updateControllerAttributes(controllerId, testData);
controllerManagement.updateControllerAttributes(controllerId, testData);
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId).get();
testData.put("test1", "testdata1");
@@ -536,7 +613,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(2);
testData.put("test1", "testdata12");
controllerManagament.updateControllerAttributes(controllerId, testData);
controllerManagement.updateControllerAttributes(controllerId, testData);
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId).get();
testData.put("test2", "testdata20");

View File

@@ -752,7 +752,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
.as("Installed distribution set of action should be null").isNotNull();
final Page<Action> updAct = actionRepository.findByDistributionSetId(pageReq, dsA.getId());
controllerManagament.addUpdateActionStatus(
controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(updAct.getContent().get(0).getId()).status(Status.FINISHED));
targ = targetManagement.findTargetByControllerID(targ.getControllerId()).get();

View File

@@ -139,7 +139,7 @@ public class ReportManagementTest extends AbstractJpaIntegrationTest {
final Target createTarget = testdataFactory.createTarget("t" + month);
final DistributionSetAssignmentResult result = assignDistributionSet(distributionSet,
Lists.newArrayList(createTarget));
controllerManagament.registerRetrieved(result.getActions().get(0),
controllerManagement.registerRetrieved(result.getActions().get(0),
"Controller retrieved update action and should start now the download.");
}
DataReportSeries<LocalDate> feedbackReceivedOverTime = reportManagement
@@ -160,7 +160,7 @@ public class ReportManagementTest extends AbstractJpaIntegrationTest {
final Target createTarget = testdataFactory.createTarget("t2" + month);
final DistributionSetAssignmentResult result = assignDistributionSet(distributionSet,
Lists.newArrayList(createTarget));
controllerManagament.registerRetrieved(result.getActions().get(0),
controllerManagement.registerRetrieved(result.getActions().get(0),
"Controller retrieved update action and should start now the download.");
}
feedbackReceivedOverTime = reportManagement.feedbackReceivedOverTime(DateTypes.perMonth(), from, to);

View File

@@ -23,10 +23,23 @@ import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
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.ActionUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.ConstraintViolationException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.utils.MultipleInvokeHelper;
import org.eclipse.hawkbit.repository.jpa.utils.SuccessCondition;
import org.eclipse.hawkbit.repository.model.Action;
@@ -45,6 +58,8 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -138,12 +153,12 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
// finish one action should be sufficient due the finish condition is at
// 50%
final JpaAction action = (JpaAction) runningActions.get(0);
controllerManagament
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(action.getId()).status(Status.FINISHED));
// check running rollouts again, now the finish condition should be hit
// and should start the next group
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// verify that now the first and the second group are in running state
final List<RolloutGroup> runningRolloutGroups = rolloutGroupManagement.findRolloutGroupsByRolloutId(
@@ -194,7 +209,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
rolloutManagement.startRollout(createdRollout.getId());
// Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
return rolloutManagement.findRolloutById(createdRollout.getId()).get();
}
@@ -202,8 +217,9 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
@Step("Finish three actions of the rollout group and delete two targets")
private void finishActionAndDeleteTargetsOfFirstRunningGroup(final Rollout createdRollout) {
// finish group one by finishing targets and deleting targets
final List<Action> runningActions = actionRepository.findByRolloutIdAndStatus(createdRollout.getId(),
Status.RUNNING);
final Slice<JpaAction> runningActionsSlice = actionRepository.findByRolloutIdAndStatus(pageReq,
createdRollout.getId(), Status.RUNNING);
final List<JpaAction> runningActions = Lists.newArrayList(runningActionsSlice.iterator());
finishAction(runningActions.get(0));
finishAction(runningActions.get(1));
finishAction(runningActions.get(2));
@@ -213,7 +229,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
@Step("Check the status of the rollout groups, second group should be in running status")
private void checkSecondGroupStatusIsRunning(final Rollout createdRollout) {
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
final List<RolloutGroup> runningRolloutGroups = rolloutGroupManagement.findRolloutGroupsByRolloutId(
createdRollout.getId(), new OffsetBasedPageRequest(0, 10, new Sort(Direction.ASC, "id"))).getContent();
assertThat(runningRolloutGroups.get(0).getStatus()).isEqualTo(RolloutGroupStatus.FINISHED);
@@ -223,8 +239,9 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
@Step("Finish one action of the rollout group and delete four targets")
private void finishActionAndDeleteTargetsOfSecondRunningGroup(final Rollout createdRollout) {
final List<Action> runningActions = actionRepository.findByRolloutIdAndStatus(createdRollout.getId(),
Status.RUNNING);
final Slice<JpaAction> runningActionsSlice = actionRepository.findByRolloutIdAndStatus(pageReq,
createdRollout.getId(), Status.RUNNING);
final List<JpaAction> runningActions = Lists.newArrayList(runningActionsSlice.iterator());
finishAction(runningActions.get(0));
targetManagement.deleteTargets(
Lists.newArrayList(runningActions.get(1).getTarget().getId(), runningActions.get(2).getTarget().getId(),
@@ -234,8 +251,9 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
@Step("Delete all targets of the rollout group")
private void deleteAllTargetsFromThirdGroup(final Rollout createdRollout) {
final List<Action> runningActions = actionRepository.findByRolloutIdAndStatus(createdRollout.getId(),
Status.SCHEDULED);
final Slice<JpaAction> runningActionsSlice = actionRepository.findByRolloutIdAndStatus(pageReq,
createdRollout.getId(), Status.SCHEDULED);
final List<JpaAction> runningActions = Lists.newArrayList(runningActionsSlice.iterator());
targetManagement.deleteTargets(Lists.newArrayList(runningActions.get(0).getTarget().getId(),
runningActions.get(1).getTarget().getId(), runningActions.get(2).getTarget().getId(),
runningActions.get(3).getTarget().getId(), runningActions.get(4).getTarget().getId()));
@@ -243,7 +261,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
@Step("Check the status of the rollout groups and the rollout")
private void verifyRolloutAndAllGroupsAreFinished(final Rollout createdRollout) {
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
final List<RolloutGroup> runningRolloutGroups = rolloutGroupManagement.findRolloutGroupsByRolloutId(
createdRollout.getId(), new OffsetBasedPageRequest(0, 10, new Sort(Direction.ASC, "id"))).getContent();
assertThat(runningRolloutGroups.get(0).getStatus()).isEqualTo(RolloutGroupStatus.FINISHED);
@@ -255,7 +273,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
}
private void finishAction(final Action action) {
controllerManagament
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(action.getId()).status(Status.FINISHED));
}
@@ -276,13 +294,13 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
// finish actions with error
for (final Action action : runningActions) {
controllerManagament
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(action.getId()).status(Status.ERROR));
}
// check running rollouts again, now the error condition should be hit
// and should execute the error action
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
final Rollout rollout = rolloutManagement.findRolloutById(createdRollout.getId()).get();
// the rollout itself should be in paused based on the error action
@@ -316,13 +334,13 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
final List<Action> runningActions = findActionsByRolloutAndStatus(createdRollout, Status.RUNNING);
// finish actions with error
for (final Action action : runningActions) {
controllerManagament
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(action.getId()).status(Status.ERROR));
}
// check running rollouts again, now the error condition should be hit
// and should execute the error action
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
final Rollout rollout = rolloutManagement.findRolloutById(createdRollout.getId()).get();
// the rollout itself should be in paused based on the error action
@@ -341,7 +359,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
.isEqualTo(RolloutStatus.RUNNING);
// checking rollouts again
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// next group should be running again after resuming the rollout
final List<RolloutGroup> resumedGroups = rolloutGroupManagement.findRolloutGroupsByRolloutId(
@@ -367,7 +385,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
// calculate the rest of the groups and finish them
for (int groupsLeft = amountGroups - 1; groupsLeft >= 1; groupsLeft--) {
// next check and start next group
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// finish running actions, 2 actions should be finished
assertThat(changeStatusForAllRunningActions(createdRollout, Status.FINISHED)).isEqualTo(2);
assertThat(rolloutManagement.findRolloutById(createdRollout.getId()).get().getStatus())
@@ -376,7 +394,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
}
// check rollout to see that all actions and all groups are finished and
// so can go to FINISHED state of the rollout
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// verify all groups are in finished state
rolloutGroupManagement
@@ -409,7 +427,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
rolloutManagement.startRollout(createdRollout.getId());
// Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
// 6 targets are ready and 2 are running
validationMap = createInitStatusMap();
@@ -418,7 +436,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
validateRolloutActionStatus(createdRollout.getId(), validationMap);
changeStatusForAllRunningActions(createdRollout, Status.FINISHED);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// 4 targets are ready, 2 are finished and 2 are running
validationMap = createInitStatusMap();
validationMap.put(TotalTargetCountStatus.Status.SCHEDULED, 4L);
@@ -427,7 +445,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
validateRolloutActionStatus(createdRollout.getId(), validationMap);
changeStatusForAllRunningActions(createdRollout, Status.FINISHED);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// 2 targets are ready, 4 are finished and 2 are running
validationMap = createInitStatusMap();
validationMap.put(TotalTargetCountStatus.Status.SCHEDULED, 2L);
@@ -436,7 +454,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
validateRolloutActionStatus(createdRollout.getId(), validationMap);
changeStatusForAllRunningActions(createdRollout, Status.FINISHED);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// 0 targets are ready, 6 are finished and 2 are running
validationMap = createInitStatusMap();
validationMap.put(TotalTargetCountStatus.Status.FINISHED, 6L);
@@ -444,7 +462,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
validateRolloutActionStatus(createdRollout.getId(), validationMap);
changeStatusForAllRunningActions(createdRollout, Status.FINISHED);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// 0 targets are ready, 8 are finished and 0 are running
validationMap = createInitStatusMap();
validationMap.put(TotalTargetCountStatus.Status.FINISHED, 8L);
@@ -472,7 +490,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
rolloutManagement.startRollout(createdRollout.getId());
// Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
// 6 targets are ready and 2 are running
validationMap = createInitStatusMap();
@@ -481,7 +499,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
validateRolloutActionStatus(createdRollout.getId(), validationMap);
changeStatusForAllRunningActions(createdRollout, Status.ERROR);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// 6 targets are ready and 2 are error
validationMap = createInitStatusMap();
validationMap.put(TotalTargetCountStatus.Status.SCHEDULED, 6L);
@@ -502,7 +520,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
successCondition, errorCondition);
changeStatusForAllRunningActions(createdRollout, Status.FINISHED);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// round(9/4)=2 targets finished (Group 1)
// round(7/3)=2 targets running (Group 3)
@@ -591,7 +609,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
final Rollout rolloutTwo = createRolloutByVariables("rolloutTwo", "This is the description for rollout two", 1,
"controllerId==rollout-*", dsForRolloutTwo, "50", "80");
changeStatusForAllRunningActions(rolloutOne, Status.FINISHED);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// Verify that 5 targets are finished, 5 are running and 5 are ready.
Map<TotalTargetCountStatus.Status, Long> expectedTargetCountStatus = createInitStatusMap();
expectedTargetCountStatus.put(TotalTargetCountStatus.Status.RUNNING, 5L);
@@ -602,7 +620,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
rolloutManagement.startRollout(rolloutTwo.getId());
// Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
// Verify that 5 targets are finished, 5 are still running and 5 are
// cancelled.
@@ -629,13 +647,13 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
rolloutOne = rolloutManagement.findRolloutById(rolloutOne.getId()).get();
changeStatusForRunningActions(rolloutOne, Status.ERROR, 2);
changeStatusForRunningActions(rolloutOne, Status.FINISHED, 3);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
changeStatusForRunningActions(rolloutOne, Status.ERROR, 2);
changeStatusForRunningActions(rolloutOne, Status.FINISHED, 3);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
changeStatusForRunningActions(rolloutOne, Status.ERROR, 2);
changeStatusForRunningActions(rolloutOne, Status.FINISHED, 3);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// 9 targets are finished and 6 have error
Map<TotalTargetCountStatus.Status, Long> expectedTargetCountStatus = createInitStatusMap();
@@ -653,7 +671,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
rolloutManagement.startRollout(rolloutTwo.getId());
// Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
rolloutTwo = rolloutManagement.findRolloutById(rolloutTwo.getId()).get();
// 6 error targets are know running
@@ -687,7 +705,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
rolloutOne = rolloutManagement.findRolloutById(rolloutOne.getId()).get();
changeStatusForRunningActions(rolloutOne, Status.ERROR, 2);
changeStatusForRunningActions(rolloutOne, Status.FINISHED, 3);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// verify: 40% error but 60% finished -> should move to next group
final List<RolloutGroup> rolloutGruops = rolloutOne.getRolloutGroups();
final Map<TotalTargetCountStatus.Status, Long> expectedTargetCountStatus = createInitStatusMap();
@@ -711,7 +729,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
rolloutOne = rolloutManagement.findRolloutById(rolloutOne.getId()).get();
changeStatusForRunningActions(rolloutOne, Status.ERROR, 2);
changeStatusForRunningActions(rolloutOne, Status.FINISHED, 3);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// verify: 40% error and 60% finished -> should not move to next group
// because successCondition 80%
rolloutOne = rolloutManagement.findRolloutById(rolloutOne.getId()).get();
@@ -736,7 +754,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
rolloutOne = rolloutManagement.findRolloutById(rolloutOne.getId()).get();
changeStatusForRunningActions(rolloutOne, Status.ERROR, 2);
changeStatusForRunningActions(rolloutOne, Status.FINISHED, 3);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
// verify: 40% error -> should pause because errorCondition is 20%
rolloutOne = rolloutManagement.findRolloutById(rolloutOne.getId()).get();
assertThat(RolloutStatus.PAUSED).isEqualTo(rolloutOne.getStatus());
@@ -753,42 +771,42 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
final Rollout rolloutA = createTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, amountGroups,
successCondition, errorCondition, "RolloutA", "RolloutA");
rolloutManagement.startRollout(rolloutA.getId());
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
final int amountTargetsForRollout2 = 10;
final int amountGroups2 = 2;
final Rollout rolloutB = createTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout2, amountGroups2,
successCondition, errorCondition, "RolloutB", "RolloutB");
rolloutManagement.startRollout(rolloutB.getId());
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
changeStatusForAllRunningActions(rolloutB, Status.FINISHED);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
final int amountTargetsForRollout3 = 10;
final int amountGroups3 = 2;
final Rollout rolloutC = createTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout3, amountGroups3,
successCondition, errorCondition, "RolloutC", "RolloutC");
rolloutManagement.startRollout(rolloutC.getId());
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
changeStatusForAllRunningActions(rolloutC, Status.ERROR);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
final int amountTargetsForRollout4 = 15;
final int amountGroups4 = 3;
final Rollout rolloutD = createTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout4, amountGroups4,
successCondition, errorCondition, "RolloutD", "RolloutD");
rolloutManagement.startRollout(rolloutD.getId());
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
changeStatusForRunningActions(rolloutD, Status.ERROR, 1);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
changeStatusForAllRunningActions(rolloutD, Status.FINISHED);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
final Page<Rollout> rolloutPage = rolloutManagement
.findAllRolloutsWithDetailedStatus(new OffsetBasedPageRequest(0, 100, new Sort(Direction.ASC, "name")));
final Page<Rollout> rolloutPage = rolloutManagement.findAllRolloutsWithDetailedStatus(
new OffsetBasedPageRequest(0, 100, new Sort(Direction.ASC, "name")), false);
final List<Rollout> rolloutList = rolloutPage.getContent();
// validate rolloutA -> 6 running and 6 ready
@@ -874,7 +892,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
}
final Slice<Rollout> rollout = rolloutManagement.findRolloutWithDetailedStatusByFilters(
new OffsetBasedPageRequest(0, 100, new Sort(Direction.ASC, "name")), "Rollout%");
new OffsetBasedPageRequest(0, 100, new Sort(Direction.ASC, "name")), "Rollout%", false);
final List<Rollout> rolloutList = rollout.getContent();
assertThat(rolloutList.size()).isEqualTo(5);
int i = 1;
@@ -915,10 +933,10 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
rolloutManagement.startRollout(myRollout.getId());
// Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
changeStatusForRunningActions(myRollout, Status.FINISHED, 2);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
myRollout = rolloutManagement.findRolloutById(myRollout.getId()).get();
float percent = rolloutManagement.getFinishedPercentForRunningGroup(myRollout.getId(),
@@ -926,7 +944,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
assertThat(percent).isEqualTo(40);
changeStatusForRunningActions(myRollout, Status.FINISHED, 3);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
percent = rolloutManagement.getFinishedPercentForRunningGroup(myRollout.getId(),
myRollout.getRolloutGroups().get(0).getId());
@@ -934,7 +952,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
changeStatusForRunningActions(myRollout, Status.FINISHED, 4);
changeStatusForAllRunningActions(myRollout, Status.ERROR);
rolloutManagement.checkRunningRollouts(0);
rolloutManagement.handleRollouts();
percent = rolloutManagement.getFinishedPercentForRunningGroup(myRollout.getId(),
myRollout.getRolloutGroups().get(1).getId());
@@ -961,7 +979,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
rolloutManagement.startRollout(myRollout.getId());
// Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
myRollout = rolloutManagement.findRolloutById(myRollout.getId()).get();
final List<RolloutGroup> rolloutGroups = myRollout.getRolloutGroups();
@@ -1063,7 +1081,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
rolloutManagement.startRollout(myRollout.getId());
// Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
final SuccessConditionRolloutStatus conditionRolloutStatus = new SuccessConditionRolloutStatus(
RolloutStatus.RUNNING);
@@ -1097,7 +1115,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
assertThat(myRollout.getStatus()).isEqualTo(RolloutStatus.READY);
rolloutManagement.checkReadyRollouts(0);
rolloutManagement.handleRollouts();
myRollout = rolloutManagement.findRolloutById(myRollout.getId()).get();
assertThat(myRollout.getStatus()).isEqualTo(RolloutStatus.READY);
@@ -1105,7 +1123,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
rolloutManagement.startRollout(myRollout.getId());
// Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
final SuccessConditionRolloutStatus conditionRolloutTargetCount = new SuccessConditionRolloutStatus(
RolloutStatus.RUNNING);
@@ -1141,7 +1159,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
// schedule rollout auto start into the future
rolloutManagement.updateRollout(
entityFactory.rollout().update(myRollout.getId()).startAt(System.currentTimeMillis() + 60000));
rolloutManagement.checkReadyRollouts(0);
rolloutManagement.handleRollouts();
// rollout should not have been started
myRollout = rolloutManagement.findRolloutById(myRollout.getId()).get();
@@ -1150,13 +1168,13 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
// schedule to now
rolloutManagement
.updateRollout(entityFactory.rollout().update(myRollout.getId()).startAt(System.currentTimeMillis()));
rolloutManagement.checkReadyRollouts(0);
rolloutManagement.handleRollouts();
myRollout = rolloutManagement.findRolloutById(myRollout.getId()).get();
assertThat(myRollout.getStatus()).isEqualTo(RolloutStatus.STARTING);
// Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0);
rolloutManagement.handleRollouts();
final SuccessConditionRolloutStatus conditionRolloutTargetCount = new SuccessConditionRolloutStatus(
RolloutStatus.RUNNING);
@@ -1212,7 +1230,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
TimeUnit.SECONDS.sleep(1);
testdataFactory.createTargets(10, rolloutName + "-notIn-", rolloutName);
rolloutManagement.fillRolloutGroupsWithTargets(myRollout.getId());
rolloutManagement.handleRollouts();
myRollout = rolloutManagement.findRolloutById(myRollout.getId()).get();
assertThat(myRollout.getStatus()).isEqualTo(RolloutStatus.READY);
@@ -1297,6 +1315,50 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
}
@Test
@Description("Verify Exception when a Rollout groups are queried for rollout that does not exist.")
public void findRolloutGroupsForRolloutThatDoesNotExist() throws Exception {
try {
rolloutGroupManagement.findAllRolloutGroupsWithDetailedStatus(1234L, pageReq);
fail("Was able to get Rollout group for rollout that does not exist.");
} catch (final EntityNotFoundException e) {
// OK
}
}
@Test
@Description("Verify Exception when targets are queried for rollout group that does not exist.")
public void findRolloutGroupTargetsForGroupThatDoesNotExist() throws Exception {
try {
rolloutGroupManagement.findRolloutGroupTargets(1234L, pageReq);
fail("Was able to get Rollout group targets for rollout group that does not exist.");
} catch (final EntityNotFoundException e) {
// OK
}
}
@Test
@Description("Verify Exception when targets are queried for rollout group that does not exist.")
public void findRolloutGroupTargetWithActionsForGroupThatDoesNotExist() throws Exception {
try {
rolloutGroupManagement.findAllTargetsWithActionStatus(pageReq, 1234L);
fail("Was able to get Rollout group targets for rollout group that does not exist.");
} catch (final EntityNotFoundException e) {
// OK
}
}
@Test
@Description("Verify Exception when targets are counted for rollout group that does not exist.")
public void countRolloutGroupTargetWithActionsForGroupThatDoesNotExist() throws Exception {
try {
rolloutGroupManagement.countTargetsOfRolloutsGroup(1234L);
fail("Was able to count Rollout group targets for rollout group that does not exist.");
} catch (final EntityNotFoundException e) {
// OK
}
}
@Test
@Description("Verify the start of a Rollout does not work during creation phase.")
public void createAndStartRolloutDuringCreationFails() throws Exception {
@@ -1331,6 +1393,83 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
}
@Test
@ExpectEvents({ @Expect(type = RolloutDeletedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetCreatedEvent.class, count = 25), @Expect(type = RolloutUpdatedEvent.class, count = 2),
@Expect(type = RolloutGroupCreatedEvent.class, count = 5),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = RolloutGroupUpdatedEvent.class, count = 10) })
public void deleteRolloutWhichHasNeverStartedIsHardDeleted() {
final int amountTargetsForRollout = 10;
final int amountOtherTargets = 15;
final int amountGroups = 5;
final String successCondition = "50";
final String errorCondition = "80";
final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout,
amountOtherTargets, amountGroups, successCondition, errorCondition);
// test
rolloutManagement.deleteRollout(createdRollout.getId());
rolloutManagement.handleRollouts();
// verify
final JpaRollout deletedRollout = rolloutRepository.findOne(createdRollout.getId());
assertThat(deletedRollout).isNull();
assertThat(rolloutGroupRepository.count()).isZero();
assertThat(rolloutTargetGroupRepository.count()).isZero();
}
@Test
@ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = RolloutGroupUpdatedEvent.class, count = 16),
@Expect(type = RolloutUpdatedEvent.class, count = 6),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetCreatedEvent.class, count = 25), @Expect(type = TargetUpdatedEvent.class, count = 4),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 2),
@Expect(type = RolloutGroupCreatedEvent.class, count = 5),
@Expect(type = ActionCreatedEvent.class, count = 10), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = RolloutDeletedEvent.class, count = 1) })
public void deleteRolloutWhichHasBeenStartedBeforeIsSoftDeleted() {
final int amountTargetsForRollout = 10;
final int amountOtherTargets = 15;
final int amountGroups = 5;
final String successCondition = "50";
final String errorCondition = "80";
final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout,
amountOtherTargets, amountGroups, successCondition, errorCondition);
// start the rollout, so it has active running actions and a group which
// has been started
rolloutManagement.startRollout(createdRollout.getId());
rolloutManagement.handleRollouts();
// verify we have running actions
assertThat(actionRepository.findByRolloutIdAndStatus(pageReq, createdRollout.getId(), Status.RUNNING)
.getNumberOfElements()).isEqualTo(2);
// test
rolloutManagement.deleteRollout(createdRollout.getId());
rolloutManagement.handleRollouts();
// verify
final JpaRollout deletedRollout = rolloutRepository.findOne(createdRollout.getId());
assertThat(deletedRollout).isNotNull();
assertThat(deletedRollout.getStatus()).isEqualTo(RolloutStatus.DELETED);
assertThat(rolloutManagement.findAll(pageReq, true).getContent()).hasSize(1);
assertThat(rolloutManagement.findAll(pageReq, false).getContent()).hasSize(0);
assertThat(rolloutGroupManagement.findAllRolloutGroupsWithDetailedStatus(createdRollout.getId(), pageReq)
.getContent()).hasSize(amountGroups);
// verify that all scheduled actions are deleted
assertThat(actionRepository.findByRolloutIdAndStatus(pageReq, deletedRollout.getId(), Status.SCHEDULED)
.getNumberOfElements()).isEqualTo(0);
// verify that all running actions keep running
assertThat(actionRepository.findByRolloutIdAndStatus(pageReq, deletedRollout.getId(), Status.RUNNING)
.getNumberOfElements()).isEqualTo(2);
}
private RolloutGroupCreate generateRolloutGroup(final int index, final Integer percentage,
final String targetFilter) {
return entityFactory.rolloutGroup().create().name("Group" + index).description("Group" + index + "desc")
@@ -1397,7 +1536,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
private int changeStatusForAllRunningActions(final Rollout rollout, final Status status) {
final List<Action> runningActions = findActionsByRolloutAndStatus(rollout, Status.RUNNING);
for (final Action action : runningActions) {
controllerManagament
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(action.getId()).status(status));
}
return runningActions.size();
@@ -1408,7 +1547,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
final List<Action> runningActions = findActionsByRolloutAndStatus(rollout, Status.RUNNING);
assertThat(runningActions.size()).isGreaterThanOrEqualTo(amountOfTargetsToGetChanged);
for (int i = 0; i < amountOfTargetsToGetChanged; i++) {
controllerManagament.addUpdateActionStatus(
controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(runningActions.get(i).getId()).status(status));
}
return runningActions.size();

View File

@@ -96,7 +96,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
final Long actionId = assignDistributionSet(installedSet.getId(), assignedC).getActions().get(0);
// set one installed DS also
controllerManagament.addUpdateActionStatus(
controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(actionId).status(Status.FINISHED).message("message"));
assignDistributionSet(setA.getId(), installedC);
@@ -663,7 +663,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
List<Target> installedtargets = testdataFactory.createTargets(10, "assigned", "assigned");
// set on installed and assign another one
assignDistributionSet(installedSet, installedtargets).getActions().forEach(actionId -> controllerManagament
assignDistributionSet(installedSet, installedtargets).getActions().forEach(actionId -> controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.FINISHED)));
assignDistributionSet(assignedSet, installedtargets);

View File

@@ -248,7 +248,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
testData.put("test1", "testdata1");
targetManagement.createTarget(entityFactory.target().create().controllerId(controllerId));
controllerManagament.updateControllerAttributes(controllerId, testData);
controllerManagement.updateControllerAttributes(controllerId, testData);
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId).get();
assertThat(target.getTargetInfo().getControllerAttributes()).as("Controller Attributes are wrong")
@@ -279,11 +279,11 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
Target target = createTargetWithAttributes("4711");
final long current = System.currentTimeMillis();
controllerManagament.updateLastTargetQuery("4711", null);
controllerManagement.updateLastTargetQuery("4711", null);
final DistributionSetAssignmentResult result = assignDistributionSet(set.getId(), "4711");
controllerManagament.addUpdateActionStatus(
controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(result.getActions().get(0)).status(Status.FINISHED));
assignDistributionSet(set2.getId(), "4711");
@@ -694,7 +694,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
@Expect(type = TargetCreatedEvent.class, count = 0)
public void targetCanBeReadWithOnlyReadTargetPermission() throws Exception {
final String knownTargetControllerId = "readTarget";
controllerManagament.findOrRegisterTargetIfItDoesNotexist(knownTargetControllerId, new URI("http://127.0.0.1"));
controllerManagement.findOrRegisterTargetIfItDoesNotexist(knownTargetControllerId, new URI("http://127.0.0.1"));
securityRule.runAs(WithSpringAuthorityRule.withUser("bumlux", "READ_TARGET"), () -> {
final Target findTargetByControllerID = targetManagement.findTargetByControllerID(knownTargetControllerId)

View File

@@ -17,6 +17,7 @@ import java.util.concurrent.TimeUnit;
import org.assertj.core.api.Assertions;
import org.eclipse.hawkbit.repository.event.TenantAwareEvent;
import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
@@ -27,6 +28,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.event.RepositoryEntityEventTest.RepositoryTestConfiguration;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.junit.Before;
@@ -90,6 +92,30 @@ public class RepositoryEntityEventTest extends AbstractJpaIntegrationTest {
assertThat(targetDeletedEvent.getEntityId()).isEqualTo(createdTarget.getId());
}
@Test
@Description("Verifies that the rollout deleted event is published when a rollout has been deleted")
public void rolloutDeletedEventIsPublished() throws InterruptedException {
final int amountTargetsForRollout = 500;
final int amountGroups = 5;
final String successCondition = "50";
final String errorCondition = "80";
final String rolloutName = "rolloutTest";
final String targetPrefixName = rolloutName;
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
testdataFactory.createTargets(amountTargetsForRollout, targetPrefixName + "-", targetPrefixName);
final Rollout createdRollout = createRolloutByVariables(rolloutName, "desc", amountGroups,
"controllerId==" + targetPrefixName + "-*", distributionSet, successCondition, errorCondition);
rolloutManagement.deleteRollout(createdRollout.getId());
rolloutManagement.handleRollouts();
final RolloutDeletedEvent rolloutDeletedEvent = eventListener.waitForEvent(RolloutDeletedEvent.class, 1,
TimeUnit.SECONDS);
assertThat(rolloutDeletedEvent).isNotNull();
assertThat(rolloutDeletedEvent.getEntityId()).isEqualTo(createdRollout.getId());
}
@Test
@Description("Verifies that the distribution set created event is published when a distribution set has been created")
public void distributionSetCreatedEventIsPublished() throws InterruptedException {

View File

@@ -18,6 +18,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.junit.Before;
import org.junit.Test;
@@ -43,14 +44,17 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest {
action = new JpaAction();
action.setActionType(ActionType.SOFT);
action.setDistributionSet(dsA);
target.addAction(action);
action.setTarget(target);
action.setStatus(Status.RUNNING);
target.addAction(action);
actionRepository.save(action);
for (int i = 0; i < 10; i++) {
final JpaAction newAction = new JpaAction();
newAction.setActionType(ActionType.SOFT);
newAction.setDistributionSet(dsA);
newAction.setActive(i % 2 == 0);
newAction.setStatus(Status.RUNNING);
newAction.setTarget(target);
actionRepository.save(newAction);
target.addAction(newAction);

View File

@@ -48,13 +48,13 @@ public class RSQLTargetFieldTest extends AbstractJpaIntegrationTest {
target = targetManagement.createTarget(entityFactory.target().create().controllerId("targetId123")
.name("targetName123").description("targetDesc123"));
attributes.put("revision", "1.1");
target = controllerManagament.updateControllerAttributes(target.getControllerId(), attributes);
target = controllerManagement.updateControllerAttributes(target.getControllerId(), attributes);
target2 = targetManagement
.createTarget(entityFactory.target().create().controllerId("targetId1234").description("targetId1234"));
attributes.put("revision", "1.2");
Thread.sleep(1);
target2 = controllerManagament.updateControllerAttributes(target2.getControllerId(), attributes);
target2 = controllerManagement.updateControllerAttributes(target2.getControllerId(), attributes);
testdataFactory.createTarget("targetId1235");
testdataFactory.createTarget("targetId1236");

View File

@@ -55,6 +55,8 @@ import org.springframework.context.annotation.Profile;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.data.domain.AuditorAware;
import org.springframework.integration.support.locks.DefaultLockRegistry;
import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
@@ -73,6 +75,11 @@ import org.springframework.util.AntPathMatcher;
@EnableAutoConfiguration
public class TestConfiguration implements AsyncConfigurer {
@Bean
public LockRegistry lockRegistry() {
return new DefaultLockRegistry();
}
@Bean
public SecurityTokenGenerator securityTokenGenerator() {
return new SecurityTokenGenerator();

View File

@@ -152,9 +152,6 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware {
@Autowired
protected WebApplicationContext context;
@Autowired
protected ControllerManagement controllerManagament;
@Autowired
protected AuditingHandler auditingHandler;
@@ -269,10 +266,10 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware {
.next();
Action savedAction = deploymentManagement.findActiveActionsByTarget(savedTarget.getControllerId()).get(0);
savedAction = controllerManagament.addUpdateActionStatus(
savedAction = controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(savedAction.getId()).status(Action.Status.RUNNING));
return controllerManagament.addUpdateActionStatus(
return controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(savedAction.getId()).status(Action.Status.FINISHED));
}
@@ -289,7 +286,7 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware {
conditions);
// Run here, because Scheduler is disabled during tests
rolloutManagement.fillRolloutGroupsWithTargets(rollout.getId());
rolloutManagement.handleRollouts();
return rolloutManagement.findRolloutById(rollout.getId()).get();
}