Prioritisation of assignments via mgmt-API (#895)

* Updating the schema for targetfilterquery and rollout
* Updating the weight validation logic and tests
* Make weight optional
* Fix existing multi assignment tests by adding weight, remove weight from TargetFilterQuery
* Add weight validation tests, fix tests
* Add mgmt api tests for assignment and getting action with weight
* Add management layer validation and tests for creating rollouts with weight
* Fix amqp test, add repo level validation to resource tests
* Add weight to rollout mgmt-api and tests
* Add weight to mgmt api target Filter create and update
* Add target filter auto assign weight. disable enforcement of setting a weight in multiassign mode
* Remove ignored tests, fix api doc
* Fix minor findings
* Fix findings
* Remove hardcoded min weight
* Add docu text, fix findings
* Fix api documentation
* Expose weight via DMF
* Expose actions according to weight via ddi
* Fix documentation
* Add method to get actions ordered by weight to deploymentManagement
* Updating the schema for targetfilterquery and rollout
* Updated the indentation
* Updated the helper class, fixed the randomUID in test factory
* Updated the class name with prefix JPA
* Adding the missing License for WeightValidationHelper class
* Adding documentation to the dmf api on weight
* Removed the merger markers
* Updated the class name
* Removed the redundant method
* Addressed final PR comments
* Updated the missing testcase with latest default weight value
* Reverting the default value of weight back to 1000 and updated tests

Signed-off-by: Shruthi Manavalli Ramanna <shruthimanavalli.ramanna@bosch-si.com>
Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>
This commit is contained in:
Stefan Klotz
2019-11-08 10:47:35 +01:00
committed by Stefan Behl
parent 09f2d8a481
commit 9cb5d31396
98 changed files with 2425 additions and 875 deletions

View File

@@ -46,13 +46,13 @@ import org.springframework.security.access.prepost.PreAuthorize;
public interface ControllerManagement {
/**
* Adds an {@link ActionStatus} for a cancel {@link Action} including potential
* state changes for the target and the {@link Action} itself.
* Adds an {@link ActionStatus} for a cancel {@link Action} including
* potential state changes for the target and the {@link Action} itself.
*
* @param create
* to be added
* @return the updated {@link Action}
*
*
* @throws EntityAlreadyExistsException
* if a given entity already exists
*
@@ -93,8 +93,8 @@ public interface ControllerManagement {
@NotNull Collection<Long> moduleId);
/**
* Simple addition of a new {@link ActionStatus} entry to the {@link Action} .
* No state changes.
* Simple addition of a new {@link ActionStatus} entry to the
* {@link Action}. No state changes.
*
* @param create
* to add to the action
@@ -136,36 +136,48 @@ public interface ControllerManagement {
Action addUpdateActionStatus(@NotNull @Valid ActionStatusCreate create);
/**
* Retrieves oldest {@link Action} that is active and assigned to a
* {@link Target}.
*
* Retrieves active {@link Action} with highest priority that is assigned to
* a {@link Target}.
*
* For performance reasons this method does not throw
* {@link EntityNotFoundException} in case target with given controlelrId
* {@link EntityNotFoundException} in case target with given controllerId
* does not exist but will return an {@link Optional#empty()} instead.
*
* @param controllerId
* identifies the target to retrieve the actions from
* @return a list of actions assigned to given target which are active
*
* identifies the target to retrieve the action from
* @return the action
*
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Optional<Action> findOldestActiveActionByTarget(@NotEmpty String controllerId);
Optional<Action> findActiveActionWithHighestWeight(@NotEmpty String controllerId);
/**
* Retrieves all active actions which are assigned to the target with the given
* controller ID.
*
* @param pageable
* pagination parameter
* Retrieves active {@link Action}s with highest weight that are assigned to
* a {@link Target}.
*
* @param controllerId
* of the target
* @return the requested {@link Page} with {@link Action}s
* identifies the target to retrieve the action from
* @param maxActionCount
* max size of returned list
* @return the action
*
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Page<Action> findActiveActionsByTarget(@NotNull Pageable pageable, @NotEmpty String controllerId);
List<Action> findActiveActionsWithHighestWeight(@NotEmpty String controllerId, int maxActionCount);
/**
* Get the {@link Action} entity for given actionId with all lazy attributes.
* Get weight of an Action. Returns the default value if the weight is null
* according to the properties.
*
* @param action
* to extract the weight from
* @return weight of the action
*/
int getWeightConsideringDefault(final Action action);
/**
* Get the {@link Action} entity for given actionId with all lazy
* attributes.
*
* @param actionId
* to be id of the action
@@ -175,7 +187,8 @@ public interface ControllerManagement {
Optional<Action> findActionWithDetails(long actionId);
/**
* Retrieves all the {@link ActionStatus} entries of the given {@link Action}.
* Retrieves all the {@link ActionStatus} entries of the given
* {@link Action}.
*
* @param pageReq
* pagination parameter
@@ -190,10 +203,11 @@ public interface ControllerManagement {
Page<ActionStatus> findActionStatusByAction(@NotNull Pageable pageReq, long actionId);
/**
* Register new target in the repository (plug-and-play) and in case it already
* exists updates {@link Target#getAddress()} and
* Register new target in the repository (plug-and-play) and in case it
* already exists updates {@link Target#getAddress()} and
* {@link Target#getLastTargetQuery()} and switches if
* {@link TargetUpdateStatus#UNKNOWN} to {@link TargetUpdateStatus#REGISTERED}.
* {@link TargetUpdateStatus#UNKNOWN} to
* {@link TargetUpdateStatus#REGISTERED}.
*
* @param controllerId
* reference
@@ -205,10 +219,11 @@ public interface ControllerManagement {
Target findOrRegisterTargetIfItDoesNotExist(@NotEmpty String controllerId, @NotNull URI address);
/**
* Register new target in the repository (plug-and-play) and in case it already
* exists updates {@link Target#getAddress()} and
* {@link Target#getLastTargetQuery()} and {@link Target#getName()} and switches if
* {@link TargetUpdateStatus#UNKNOWN} to {@link TargetUpdateStatus#REGISTERED}.
* Register new target in the repository (plug-and-play) and in case it
* already exists updates {@link Target#getAddress()} and
* {@link Target#getLastTargetQuery()} and {@link Target#getName()} and
* switches if {@link TargetUpdateStatus#UNKNOWN} to
* {@link TargetUpdateStatus#REGISTERED}.
*
* @param controllerId
* reference
@@ -222,14 +237,14 @@ public interface ControllerManagement {
Target findOrRegisterTargetIfItDoesNotExist(@NotEmpty String controllerId, @NotNull URI address, String name);
/**
* Retrieves last {@link Action} for a download of an artifact of given module
* and target if exists and is not canceled.
* Retrieves last {@link Action} for a download of an artifact of given
* module and target if exists and is not canceled.
*
* @param controllerId
* to look for
* @param moduleId
* of the the {@link SoftwareModule} that should be assigned to the
* target
* of the the {@link SoftwareModule} that should be assigned to
* the target
* @return last {@link Action} for given combination
*
* @throws EntityNotFoundException
@@ -267,18 +282,18 @@ public interface ControllerManagement {
int getMaintenanceWindowPollCount();
/**
* Returns polling time based on the maintenance window for an action. Server
* will reduce the polling interval as the start time for maintenance window
* approaches, so that at least these many attempts are made between current
* polling until start of maintenance window. Poll time keeps reducing with
* MinPollingTime as lower limit
* {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}. After the start of
* maintenance window, it resets to default
* Returns polling time based on the maintenance window for an action.
* Server will reduce the polling interval as the start time for maintenance
* window approaches, so that at least these many attempts are made between
* current polling until start of maintenance window. Poll time keeps
* reducing with MinPollingTime as lower limit
* {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}. After the start
* of maintenance window, it resets to default
* {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
*
* @param actionId
* id the {@link Action} for which polling time is calculated based
* on it having maintenance window or not
* id the {@link Action} for which polling time is calculated
* based on it having maintenance window or not
*
* @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
*/
@@ -286,20 +301,20 @@ public interface ControllerManagement {
String getPollingTimeForAction(long actionId);
/**
* Checks if a given target has currently or has even been assigned to the given
* artifact through the action history list. This can e.g. indicate if a target
* is allowed to download a given artifact because it has currently assigned or
* had ever been assigned to the target and so it's visible to a specific target
* e.g. for downloading.
* Checks if a given target has currently or has even been assigned to the
* given artifact through the action history list. This can e.g. indicate if
* a target is allowed to download a given artifact because it has currently
* assigned or had ever been assigned to the target and so it's visible to a
* specific target e.g. for downloading.
*
* @param controllerId
* the ID of the target to check
* @param sha1Hash
* of the artifact to verify if the given target had even been
* assigned to
* @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}
* @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
@@ -308,20 +323,20 @@ public interface ControllerManagement {
boolean hasTargetArtifactAssigned(@NotEmpty String controllerId, @NotEmpty String sha1Hash);
/**
* Checks if a given target has currently or has even been assigned to the given
* artifact through the action history list. This can e.g. indicate if a target
* is allowed to download a given artifact because it has currently assigned or
* had ever been assigned to the target and so it's visible to a specific target
* e.g. for downloading.
* Checks if a given target has currently or has even been assigned to the
* given artifact through the action history list. This can e.g. indicate if
* a target is allowed to download a given artifact because it has currently
* assigned or had ever been assigned to the target and so it's visible to a
* specific target e.g. for downloading.
*
* @param targetId
* the ID of the target to check
* @param sha1Hash
* of the artifact to verify if the given target had even been
* assigned to
* @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}
* @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
@@ -330,8 +345,8 @@ public interface ControllerManagement {
boolean hasTargetArtifactAssigned(long targetId, @NotEmpty String sha1Hash);
/**
* Registers retrieved status for given {@link Target} and {@link Action} if it
* does not exist yet.
* Registers retrieved status for given {@link Target} and {@link Action} if
* it does not exist yet.
*
* @param actionId
* to the handle status for
@@ -385,8 +400,9 @@ public interface ControllerManagement {
Optional<Target> getByControllerId(@NotEmpty String controllerId);
/**
* Finds {@link Target} based on given ID returns found Target without details,
* i.e. NO {@link Target#getTags()} and {@link Target#getActions()} possible.
* Finds {@link Target} based on given ID returns found Target without
* details, i.e. NO {@link Target#getTags()} and {@link Target#getActions()}
* possible.
*
* @param targetId
* to look for.
@@ -398,20 +414,21 @@ public interface ControllerManagement {
Optional<Target> get(long targetId);
/**
* Retrieves the specified number of messages from action history of the given
* {@link Action} based on messageCount. Regardless of the value of
* Retrieves the specified number of messages from action history of the
* given {@link Action} based on messageCount. Regardless of the value of
* messageCount, in order to restrict resource utilisation by controllers,
* maximum number of messages that are retrieved from database is limited by
* {@link RepositoryConstants#MAX_ACTION_HISTORY_MSG_COUNT}. messageCount less
* then zero, retrieves the maximum allowed number of action status messages
* from history; messageCount equal zero, does not retrieve any message; and
* messageCount larger then zero, retrieves the specified number of messages,
* limited by maximum allowed number. A controller sends the feedback for an
* {@link ActionStatus} as a list of messages; while returning the messages,
* even though the messages from multiple {@link ActionStatus} are retrieved in
* descending order by the reported time ({@link ActionStatus#getOccurredAt()}),
* i.e. latest ActionStatus first, the sub-ordering of messages from within
* single {@link ActionStatus} is unspecified.
* {@link RepositoryConstants#MAX_ACTION_HISTORY_MSG_COUNT}. messageCount
* less then zero, retrieves the maximum allowed number of action status
* messages from history; messageCount equal zero, does not retrieve any
* message; and messageCount larger then zero, retrieves the specified
* number of messages, limited by maximum allowed number. A controller sends
* the feedback for an {@link ActionStatus} as a list of messages; while
* returning the messages, even though the messages from multiple
* {@link ActionStatus} are retrieved in descending order by the reported
* time ({@link ActionStatus#getOccurredAt()}), i.e. latest ActionStatus
* first, the sub-ordering of messages from within single
* {@link ActionStatus} is unspecified.
*
* @param actionId
* to be filtered on
@@ -424,10 +441,10 @@ public interface ControllerManagement {
List<String> getActionHistoryMessages(long actionId, int messageCount);
/**
* Cancels given {@link Action} for this {@link Target}. However, it might be
* possible that the controller will continue to work on the cancellation. The
* controller needs to acknowledge or reject the cancellation using
* {@link DdiRootController#postCancelActionFeedback}.
* Cancels given {@link Action} for this {@link Target}. However, it might
* be possible that the controller will continue to work on the
* cancellation. The controller needs to acknowledge or reject the
* cancellation using {@link DdiRootController#postCancelActionFeedback}.
*
* @param actionId
* to be canceled
@@ -454,8 +471,9 @@ public interface ControllerManagement {
void updateActionExternalRef(long actionId, @NotEmpty String externalRef);
/**
* Retrieves list of {@link Action}s which matches the provided externalRefs.
*
* Retrieves list of {@link Action}s which matches the provided
* externalRefs.
*
* @param externalRefs
* for which the actions need to be fetched.
* @return list of {@link Action}s matching the externalRefs.

View File

@@ -14,6 +14,7 @@ import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@@ -76,7 +77,7 @@ public interface DeploymentManagement {
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
List<DistributionSetAssignmentResult> assignDistributionSets(
@NotEmpty List<DeploymentRequest> deploymentRequests);
@Valid @NotEmpty List<DeploymentRequest> deploymentRequests);
/**
* Assigns {@link DistributionSet}s to {@link Target}s according to the
@@ -107,9 +108,9 @@ public interface DeploymentManagement {
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
List<DistributionSetAssignmentResult> assignDistributionSets(
@NotEmpty List<DeploymentRequest> deploymentRequests, String actionMessage);
/**
@Valid @NotEmpty List<DeploymentRequest> deploymentRequests, String actionMessage);
/**
* build a {@link DeploymentRequest} for a target distribution set
* assignment
*
@@ -372,6 +373,30 @@ public interface DeploymentManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<Action> findInActiveActionsByTarget(@NotNull Pageable pageable, @NotEmpty String controllerId);
/**
* Retrieves active {@link Action}s with highest weight that are assigned to
* a {@link Target}.
*
* @param controllerId
* identifies the target to retrieve the action from
* @param maxActionCount
* max size of returned list
* @return the action
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
List<Action> findActiveActionsWithHighestWeight(@NotEmpty String controllerId, int maxActionCount);
/**
* Get weight of an Action. Returns the default value if the weight is null
* according to the properties.
*
* @param action
* to extract the weight from
* @return weight of the action
*/
int getWeightConsideringDefault(final Action action);
/**
* Force cancels given {@link Action} for given {@link Target}. Force
* canceling means that the action is marked as canceled on the SP server

View File

@@ -27,8 +27,8 @@ public class RepositoryProperties {
* especially useful if the action status feedback channel order from the
* device cannot be guaranteed.
*
* Note: if this is enforced you have to make sure that the feedback
* channel from the devices is in order.
* Note: if this is enforced you have to make sure that the feedback channel
* from the devices is in order.
*/
private boolean rejectActionStatusForClosedAction;
@@ -54,6 +54,11 @@ public class RepositoryProperties {
*/
private boolean eagerPollPersistence;
/**
* If an {@link Action} has a weight of null this value is used as weight.
*/
private int actionWeightIfAbsent = 1000;
public boolean isEagerPollPersistence() {
return eagerPollPersistence;
}
@@ -94,4 +99,12 @@ public class RepositoryProperties {
this.publishTargetPollEvent = publishTargetPollEvent;
}
public int getActionWeightIfAbsent() {
return actionWeightIfAbsent;
}
public void setActionWeightIfAbsent(final int actionWeightIfAbsent) {
this.actionWeightIfAbsent = actionWeightIfAbsent;
}
}

View File

@@ -129,7 +129,7 @@ public interface RolloutManagement {
* exceeded.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_CREATE)
Rollout create(@NotNull RolloutCreate create, int amountGroup, @NotNull RolloutGroupConditions conditions);
Rollout create(@NotNull @Valid RolloutCreate create, int amountGroup, @NotNull RolloutGroupConditions conditions);
/**
* Persists a new rollout entity. The filter within the
@@ -166,7 +166,7 @@ public interface RolloutManagement {
* exceeded.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_CREATE)
Rollout create(@NotNull @Valid RolloutCreate rollout, @NotNull @Valid List<RolloutGroupCreate> groups,
Rollout create(@Valid @NotNull RolloutCreate rollout, @NotNull @Valid List<RolloutGroupCreate> groups,
RolloutGroupConditions conditions);
/**
@@ -342,9 +342,9 @@ public interface RolloutManagement {
/**
* Approves or denies a created rollout being in state
* {@link RolloutStatus#WAITING_FOR_APPROVAL}. If the rollout is approved, it
* switches state to {@link RolloutStatus#READY}, otherwise it switches to state
* {@link RolloutStatus#APPROVAL_DENIED}
* {@link RolloutStatus#WAITING_FOR_APPROVAL}. If the rollout is approved,
* it switches state to {@link RolloutStatus#READY}, otherwise it switches
* to state {@link RolloutStatus#APPROVAL_DENIED}
*
* @param rolloutId
* the rollout to be approved or denied.
@@ -365,9 +365,9 @@ public interface RolloutManagement {
/**
* Approves or denies a created rollout being in state
* {@link RolloutStatus#WAITING_FOR_APPROVAL}. If the rollout is approved, it
* switches state to {@link RolloutStatus#READY}, otherwise it switches to state
* {@link RolloutStatus#APPROVAL_DENIED}
* {@link RolloutStatus#WAITING_FOR_APPROVAL}. If the rollout is approved,
* it switches state to {@link RolloutStatus#READY}, otherwise it switches
* to state {@link RolloutStatus#APPROVAL_DENIED}
*
* @param rolloutId
* the rollout to be approved or denied.

View File

@@ -15,6 +15,7 @@ import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -208,7 +209,7 @@ public interface TargetFilterQueryManagement {
* if fields are not filled as specified. Check
* {@link TargetFilterQueryUpdate} for field constraints.
*
* @throws AssignmentQuotaExceededException
* @throws QuotaExceededException
* if the update contains a new query which addresses too many
* targets (auto-assignments only)
*/
@@ -216,46 +217,10 @@ public interface TargetFilterQueryManagement {
TargetFilterQuery update(@NotNull @Valid TargetFilterQueryUpdate update);
/**
* Updates the the auto-assign {@link DistributionSet} and sets default
* (FORCED) {@link ActionType} of the addressed {@link TargetFilterQuery}.
*
* @param queryId
* of the target filter query to be updated
* @param dsId
* to be updated or <code>null</code> in order to remove it
* together with the auto-assign {@link ActionType}
* Updates the auto assign settings of an {@link TargetFilterQuery}.
*
* @return the updated {@link TargetFilterQuery}
*
* @throws EntityNotFoundException
* if either {@link TargetFilterQuery} and/or autoAssignDs are
* provided but not found
*
* @throws AssignmentQuotaExceededException
* if the query that is already associated with this filter
* query addresses too many targets (auto-assignments only)
*
* @throws InvalidAutoAssignDistributionSetException
* if the provided auto-assign {@link DistributionSet} is not
* valid (incomplete or soft deleted)
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
default TargetFilterQuery updateAutoAssignDS(final long queryId, final Long dsId) {
return updateAutoAssignDSWithActionType(queryId, dsId, null);
}
/**
* Updates the the auto-assign {@link DistributionSet} and
* {@link ActionType} of the addressed {@link TargetFilterQuery}.
*
* @param queryId
* of the target filter query to be updated
* @param dsId
* to be updated or <code>null</code> in order to remove it
* together with the auto-assign {@link ActionType}
* @param actionType
* to be updated or <code>null</code> for default (FORCED) if
* distribution set Id is present
* @param autoAssignDistributionSetUpdate
* the new auto assignment
*
* @return the updated {@link TargetFilterQuery}
*
@@ -276,5 +241,6 @@ public interface TargetFilterQueryManagement {
* valid (incomplete or soft deleted)
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
TargetFilterQuery updateAutoAssignDSWithActionType(long queryId, Long dsId, ActionType actionType);
TargetFilterQuery updateAutoAssignDS(
@NotNull @Valid AutoAssignDistributionSetUpdate autoAssignDistributionSetUpdate);
}

View File

@@ -0,0 +1,94 @@
/**
* Copyright (c) 2019 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.builder;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
/**
* Builder to update the auto assign {@link DistributionSet} of a
* {@link TargetFilterQuery} entry. Defines all fields that can be updated.
*/
public class AutoAssignDistributionSetUpdate {
private final long targetFilterId;
private Long dsId;
private ActionType actionType;
@Min(Action.WEIGHT_MIN)
@Max(Action.WEIGHT_MAX)
private Integer weight;
/**
* Constructor
*
* @param targetFilterId
* ID of {@link TargetFilterQuery} to update
*/
public AutoAssignDistributionSetUpdate(final long targetFilterId) {
this.targetFilterId = targetFilterId;
}
/**
* Specify {@link DistributionSet}
*
* @param dsId
* ID of the {@link DistributionSet}
* @return updated builder instance
*/
public AutoAssignDistributionSetUpdate ds(final Long dsId) {
this.dsId = dsId;
return this;
}
/**
* Specify {@link DistributionSet}
*
* @param actionType
* {@link ActionType} used for the auto assignment
* @return updated builder instance
*/
public AutoAssignDistributionSetUpdate actionType(final ActionType actionType) {
this.actionType = actionType;
return this;
}
/**
* Specify weight of resulting {@link Action}
*
* @param weight
* weight used for the auto assignment
* @return updated builder instance
*/
public AutoAssignDistributionSetUpdate weight(final Integer weight) {
this.weight = weight;
return this;
}
public Long getDsId() {
return dsId;
}
public ActionType getActionType() {
return actionType;
}
public Integer getWeight() {
return weight;
}
public long getTargetFilterId() {
return targetFilterId;
}
}

View File

@@ -28,6 +28,8 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
*/
public interface RolloutCreate {
/**
* Set name
*
* @param name
* for {@link Rollout#getName()}
* @return updated builder instance
@@ -35,6 +37,8 @@ public interface RolloutCreate {
RolloutCreate name(@Size(min = 1, max = NamedEntity.NAME_MAX_SIZE) @NotNull String name);
/**
* Set description
*
* @param description
* for {@link Rollout#getDescription()}
* @return updated builder instance
@@ -42,6 +46,8 @@ public interface RolloutCreate {
RolloutCreate description(@Size(max = NamedEntity.DESCRIPTION_MAX_SIZE) String description);
/**
* Set the {@link DistributionSet}
*
* @param set
* for {@link Rollout#getDistributionSet()}
* @return updated builder instance
@@ -51,6 +57,8 @@ public interface RolloutCreate {
}
/**
* Set the id of the {@link DistributionSet}
*
* @param setId
* for {@link Rollout#getDistributionSet()}
* @return updated builder instance
@@ -58,6 +66,8 @@ public interface RolloutCreate {
RolloutCreate set(long setId);
/**
* Set the {@link TargetFilterQuery}
*
* @param targetFilterQuery
* for {@link Rollout#getTargetFilterQuery()}
* @return updated builder instance
@@ -66,6 +76,8 @@ public interface RolloutCreate {
@Size(min = 1, max = TargetFilterQuery.QUERY_MAX_SIZE) @NotNull String targetFilterQuery);
/**
* Set the {@link ActionType}
*
* @param actionType
* for {@link Rollout#getActionType()}
* @return updated builder instance
@@ -73,6 +85,8 @@ public interface RolloutCreate {
RolloutCreate actionType(@NotNull ActionType actionType);
/**
* Set the forcedTime of the resulting {@link Actions}
*
* @param forcedTime
* for {@link Rollout#getForcedTime()}
* @return updated builder instance
@@ -80,6 +94,17 @@ public interface RolloutCreate {
RolloutCreate forcedTime(Long forcedTime);
/**
* Set the weight of the resulting {@link Actions}
*
* @param weight
* for {@link Rollout#getWeight()}
* @return updated builder instance
*/
RolloutCreate weight(Integer weight);
/**
* set start
*
* @param startAt
* for {@link Rollout#getStartAt()}
* @return updated builder instance

View File

@@ -12,6 +12,7 @@ import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.Rollout;
@@ -22,6 +23,8 @@ import org.eclipse.hawkbit.repository.model.Rollout;
*/
public interface RolloutUpdate {
/**
* Set name of the {@link Rollout}
*
* @param name
* for {@link Rollout#getName()}
* @return updated builder instance
@@ -29,6 +32,8 @@ public interface RolloutUpdate {
RolloutUpdate name(@Size(min = 1, max = NamedEntity.NAME_MAX_SIZE) @NotNull String name);
/**
* Set description of the {@link Rollout}
*
* @param description
* for {@link Rollout#getDescription()}
* @return updated builder instance
@@ -36,6 +41,8 @@ public interface RolloutUpdate {
RolloutUpdate description(@Size(max = NamedEntity.DESCRIPTION_MAX_SIZE) String description);
/**
* Set ID of {@link DistributionSet} of the {@link Rollout}
*
* @param setId
* for {@link Rollout#getDistributionSet()}
* @return updated builder instance
@@ -43,6 +50,8 @@ public interface RolloutUpdate {
RolloutUpdate set(long setId);
/**
* Set action type of the {@link Rollout}
*
* @param actionType
* for {@link Rollout#getActionType()}
* @return updated builder instance
@@ -50,6 +59,8 @@ public interface RolloutUpdate {
RolloutUpdate actionType(@NotNull Action.ActionType actionType);
/**
* Set forcedTime of the {@link Rollout}
*
* @param forcedTime
* for {@link Rollout#getForcedTime()}
* @return updated builder instance
@@ -57,10 +68,20 @@ public interface RolloutUpdate {
RolloutUpdate forcedTime(Long forcedTime);
/**
* Set weight of {@link Action}s created by the {@link Rollout}
*
* @param weight
* for {@link Rollout#getWeight()}
* @return updated builder instance
*/
RolloutUpdate weight(Integer weight);
/**
* Set start time of the {@link Rollout}
*
* @param startAt
* for {@link Rollout#getStartAt()}
* @return updated builder instance
*/
RolloutUpdate startAt(Long startAt);
}

View File

@@ -16,12 +16,23 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
*/
public interface TargetFilterQueryBuilder {
/**
* Used to update a {@link TargetFilterQuery}
*
* @param id
* of the updatable entity
* @return builder instance
*/
TargetFilterQueryUpdate update(long id);
/**
* Used to update a the auto assignment of a {@link TargetFilterQuery}
*
* @param id
* of the updatable entity
* @return builder instance
*/
AutoAssignDistributionSetUpdate updateAutoAssign(long id);
/**
* @return builder instance
*/

View File

@@ -13,6 +13,7 @@ import java.util.Optional;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -27,6 +28,8 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
*/
public interface TargetFilterQueryCreate {
/**
* Set filter name
*
* @param name
* of {@link TargetFilterQuery#getName()}
* @return updated builder instance
@@ -34,6 +37,8 @@ public interface TargetFilterQueryCreate {
TargetFilterQueryCreate name(@Size(min = 1, max = NamedEntity.NAME_MAX_SIZE) @NotNull String name);
/**
* Set filter query
*
* @param query
* of {@link TargetFilterQuery#getQuery()}
* @return updated builder instance
@@ -41,6 +46,8 @@ public interface TargetFilterQueryCreate {
TargetFilterQueryCreate query(@Size(min = 1, max = TargetFilterQuery.QUERY_MAX_SIZE) @NotNull String query);
/**
* Set {@link DistributionSet} for auto assignment
*
* @param distributionSet
* for {@link TargetFilterQuery#getAutoAssignDistributionSet()}
* @return updated builder instance
@@ -50,6 +57,8 @@ public interface TargetFilterQueryCreate {
}
/**
* Set ID of {@link DistributionSet} for auto assignment
*
* @param dsId
* for {@link TargetFilterQuery#getAutoAssignDistributionSet()}
* @return updated builder instance
@@ -57,12 +66,23 @@ public interface TargetFilterQueryCreate {
TargetFilterQueryCreate autoAssignDistributionSet(Long dsId);
/**
* Set {@link ActionType} for auto assignment
*
* @param actionType
* for {@link TargetFilterQuery#getAutoAssignActionType()}
* @return updated builder instance
*/
TargetFilterQueryCreate autoAssignActionType(ActionType actionType);
/**
* Set weight of {@link Action} created during auto assignment
*
* @param weight
* weight of {@link Action} generated within auto assignment
* @return updated builder instance
*/
TargetFilterQueryCreate autoAssignWeight(Integer weight);
/**
* @return peek on current state of {@link TargetFilterQuery} in the builder
*/

View File

@@ -32,5 +32,4 @@ public interface TargetFilterQueryUpdate {
* @return updated builder instance
*/
TargetFilterQueryUpdate query(@Size(min = 1, max = TargetFilterQuery.QUERY_MAX_SIZE) String query);
}

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 2019 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.exception;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError;
/**
* This exception is thrown if multi assignments is enabled and an target
* distribution set assignment request does not contain a weight value
*
*/
public class NoWeightProvidedInMultiAssignmentModeException extends AbstractServerRtException {
private static final long serialVersionUID = 1L;
private static final SpServerError THIS_ERROR = SpServerError.SP_NO_WEIGHT_PROVIDED_IN_MULTIASSIGNMENT_MODE;
/**
* Default constructor.
*/
public NoWeightProvidedInMultiAssignmentModeException() {
super(THIS_ERROR);
}
/**
* Parameterized constructor.
*
* @param cause
* of the exception
*/
public NoWeightProvidedInMultiAssignmentModeException(final Throwable cause) {
super(THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
* @param cause
* of the exception
*/
public NoWeightProvidedInMultiAssignmentModeException(final String message, final Throwable cause) {
super(message, THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
*/
public NoWeightProvidedInMultiAssignmentModeException(final String message) {
super(message, THIS_ERROR);
}
}

View File

@@ -38,7 +38,16 @@ public interface Action extends TenantAwareBaseEntity {
* Maximum length of external reference.
*/
int EXTERNAL_REF_MAX_LENGTH = 512;
/**
* Minimum weight to indicate the priority of {@link Action}.
*/
int WEIGHT_MIN = 0;
/**
* Maximum weight to indicate the priority of {@link Action}.
*/
int WEIGHT_MAX = 1000;
/**
* @return the distributionSet
*/
@@ -80,6 +89,11 @@ public interface Action extends TenantAwareBaseEntity {
*/
long getForcedTime();
/**
* @return priority of the {@link Action}.
*/
Optional<Integer> getWeight();
/**
* @return rolloutGroup related to this {@link Action}.
*/
@@ -106,10 +120,11 @@ public interface Action extends TenantAwareBaseEntity {
String getMaintenanceWindowTimeZone();
/**
* @param externalRef associated with this action
* @param externalRef
* associated with this action
*/
void setExternalRef(@NotEmpty String externalRef);
/**
* @return externalRef of the action
*/

View File

@@ -10,6 +10,8 @@ package org.eclipse.hawkbit.repository.model;
import java.util.Objects;
import javax.validation.Valid;
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -19,6 +21,7 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
*/
public class DeploymentRequest {
private final Long distributionSetId;
@Valid
private final TargetWithActionType targetWithActionType;
/**
@@ -34,6 +37,8 @@ public class DeploymentRequest {
* specified for the action.
* @param forceTime
* at what time the type soft turns into forced.
* @param weight
* the priority of an {@link Action}.
* @param maintenanceSchedule
* is the cron expression to be used for scheduling maintenance
* windows. Expression has 6 mandatory fields and 1 last optional
@@ -51,11 +56,10 @@ public class DeploymentRequest {
* if the parameters do not define a valid maintenance schedule.
*/
public DeploymentRequest(final String controllerId, final Long distributionSetId, final ActionType actionType,
final long forceTime, final String maintenanceSchedule, final String maintenanceWindowDuration,
final String maintenanceWindowTimeZone) {
this.targetWithActionType = new TargetWithActionType(controllerId, actionType, forceTime, maintenanceSchedule,
maintenanceWindowDuration,
maintenanceWindowTimeZone);
final long forceTime, final Integer weight, final String maintenanceSchedule,
final String maintenanceWindowDuration, final String maintenanceWindowTimeZone) {
this.targetWithActionType = new TargetWithActionType(controllerId, actionType, forceTime, weight,
maintenanceSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone);
this.distributionSetId = distributionSetId;
}
@@ -71,14 +75,13 @@ public class DeploymentRequest {
return targetWithActionType;
}
@Override
public String toString() {
return String.format(
"DeploymentRequest [controllerId=%s, distributionSetId=%d, actionType=%s, forceTime=%d, maintenanceSchedule=%s, maintenanceWindowDuration=%s, maintenanceWindowTimeZone=%s]",
"DeploymentRequest [controllerId=%s, distributionSetId=%d, actionType=%s, forceTime=%d, weight=%d, maintenanceSchedule=%s, maintenanceWindowDuration=%s, maintenanceWindowTimeZone=%s]",
targetWithActionType.getControllerId(), getDistributionSetId(), targetWithActionType.getActionType(),
targetWithActionType.getForceTime(), targetWithActionType.getMaintenanceSchedule(),
targetWithActionType.getMaintenanceWindowDuration(),
targetWithActionType.getForceTime(), targetWithActionType.getWeight(),
targetWithActionType.getMaintenanceSchedule(), targetWithActionType.getMaintenanceWindowDuration(),
targetWithActionType.getMaintenanceWindowTimeZone());
}
@@ -86,7 +89,7 @@ public class DeploymentRequest {
public int hashCode() {
return Objects.hash(distributionSetId, targetWithActionType);
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {

View File

@@ -18,7 +18,7 @@ public class DeploymentRequestBuilder {
private final String controllerId;
private final Long distributionSetId;
private Integer weight;
private long forceTime = RepositoryModelConstants.NO_FORCE_TIME;
private ActionType actionType = ActionType.FORCED;
private String maintenanceSchedule;
@@ -63,6 +63,18 @@ public class DeploymentRequestBuilder {
return this;
}
/**
* Set the weight of the action.
*
* @param weight
* the priority given to the action.
* @return builder
*/
public DeploymentRequestBuilder setWeight(final Integer weight) {
this.weight = weight;
return this;
}
/**
* Set a maintenanceWindow
*
@@ -94,8 +106,8 @@ public class DeploymentRequestBuilder {
* @return the request object
*/
public DeploymentRequest build() {
return new DeploymentRequest(controllerId, distributionSetId, actionType, forceTime, maintenanceSchedule,
maintenanceWindowDuration, maintenanceWindowTimeZone);
return new DeploymentRequest(controllerId, distributionSetId, actionType, forceTime, weight,
maintenanceSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone);
}
}

View File

@@ -8,6 +8,7 @@
*/
package org.eclipse.hawkbit.repository.model;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -101,6 +102,11 @@ public interface Rollout extends NamedEntity {
*/
String getApprovalRemark();
/**
* @return the priority of {@link Rollout}.
*/
Optional<Integer> getWeight();
/**
*
* State machine for rollout.
@@ -197,5 +203,4 @@ public interface Rollout extends NamedEntity {
*/
DENIED
}
}

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.model;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Optional;
import java.util.Set;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -70,4 +71,10 @@ public interface TargetFilterQuery extends TenantAwareBaseEntity {
*/
ActionType getAutoAssignActionType();
/**
* @return the weight of the {@link Action}s created during an auto
* assignment.
*/
Optional<Integer> getAutoAssignWeight();
}

View File

@@ -10,6 +10,9 @@ package org.eclipse.hawkbit.repository.model;
import java.util.Objects;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -21,6 +24,9 @@ public class TargetWithActionType {
private final String controllerId;
private final ActionType actionType;
private final long forceTime;
@Min(Action.WEIGHT_MIN)
@Max(Action.WEIGHT_MAX)
private final Integer weight;
private String maintenanceSchedule;
private String maintenanceWindowDuration;
private String maintenanceWindowTimeZone;
@@ -32,7 +38,7 @@ public class TargetWithActionType {
* ID if the controller
*/
public TargetWithActionType(final String controllerId) {
this(controllerId, ActionType.FORCED, 0);
this(controllerId, ActionType.FORCED, 0, null);
}
/**
@@ -43,13 +49,17 @@ public class TargetWithActionType {
* @param actionType
* specified for the action.
* @param forceTime
* after that point in time the action is exposed as forcen in
* after that point in time the action is exposed as forced in
* case the type is {@link ActionType#TIMEFORCED}
* @param weight
* the priority of an {@link Action}
*/
public TargetWithActionType(final String controllerId, final ActionType actionType, final long forceTime) {
public TargetWithActionType(final String controllerId, final ActionType actionType, final long forceTime,
final Integer weight) {
this.controllerId = controllerId;
this.actionType = actionType != null ? actionType : ActionType.FORCED;
this.forceTime = forceTime;
this.weight = weight;
}
/**
@@ -61,8 +71,10 @@ public class TargetWithActionType {
* @param actionType
* specified for the action.
* @param forceTime
* after that point in time the action is exposed as forcen in
* after that point in time the action is exposed as forced in
* case the type is {@link ActionType#TIMEFORCED}
* @param weight
* the priority of an {@link Action}
* @param maintenanceSchedule
* is the cron expression to be used for scheduling maintenance
* windows. Expression has 6 mandatory fields and 1 last optional
@@ -80,9 +92,9 @@ public class TargetWithActionType {
* if the parameters do not define a valid maintenance schedule.
*/
public TargetWithActionType(final String controllerId, final ActionType actionType, final long forceTime,
final String maintenanceSchedule, final String maintenanceWindowDuration,
final Integer weight, final String maintenanceSchedule, final String maintenanceWindowDuration,
final String maintenanceWindowTimeZone) {
this(controllerId, actionType, forceTime);
this(controllerId, actionType, forceTime, weight);
this.maintenanceSchedule = maintenanceSchedule;
this.maintenanceWindowDuration = maintenanceWindowDuration;
@@ -104,6 +116,11 @@ public class TargetWithActionType {
return RepositoryModelConstants.NO_FORCE_TIME;
}
public Integer getWeight() {
return weight;
}
public String getControllerId() {
return controllerId;
}
@@ -138,14 +155,14 @@ public class TargetWithActionType {
@Override
public String toString() {
return "TargetWithActionType [controllerId=" + controllerId + ", actionType=" + getActionType() + ", forceTime="
+ getForceTime() + ", maintenanceSchedule=" + getMaintenanceSchedule() + ", maintenanceWindowDuration="
+ getMaintenanceWindowDuration() + ", maintenanceWindowTimeZone=" + getMaintenanceWindowTimeZone()
+ "]";
+ getForceTime() + ", weight=" + getWeight() + ", maintenanceSchedule=" + getMaintenanceSchedule()
+ ", maintenanceWindowDuration=" + getMaintenanceWindowDuration() + ", maintenanceWindowTimeZone="
+ getMaintenanceWindowTimeZone() + "]";
}
@Override
public int hashCode() {
return Objects.hash(actionType, controllerId, forceTime, maintenanceSchedule, maintenanceWindowDuration,
return Objects.hash(actionType, controllerId, forceTime, weight, maintenanceSchedule, maintenanceWindowDuration,
maintenanceWindowTimeZone);
}
@@ -163,10 +180,9 @@ public class TargetWithActionType {
}
final TargetWithActionType other = (TargetWithActionType) obj;
return Objects.equals(actionType, other.actionType) && Objects.equals(controllerId, other.controllerId)
&& Objects.equals(forceTime, other.forceTime)
&& Objects.equals(forceTime, other.forceTime) && Objects.equals(weight, other.weight)
&& Objects.equals(maintenanceSchedule, other.maintenanceSchedule)
&& Objects.equals(maintenanceWindowDuration, other.maintenanceWindowDuration)
&& Objects.equals(maintenanceWindowTimeZone, other.maintenanceWindowTimeZone);
}
}

View File

@@ -20,6 +20,7 @@ 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.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.context.ApplicationContext;
import org.springframework.integration.support.locks.LockRegistry;
@@ -54,12 +55,18 @@ public abstract class AbstractRolloutManagement implements RolloutManagement {
protected final RolloutApprovalStrategy rolloutApprovalStrategy;
protected final TenantConfigurationManagement tenantConfigurationManagement;
protected final SystemSecurityContext systemSecurityContext;
protected AbstractRolloutManagement(final TargetManagement targetManagement,
final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager,
final TenantAware tenantAware, final LockRegistry lockRegistry,
final RolloutApprovalStrategy rolloutApprovalStrategy) {
final RolloutApprovalStrategy rolloutApprovalStrategy,
final TenantConfigurationManagement tenantConfigurationManagement,
final SystemSecurityContext systemSecurityContext) {
this.targetManagement = targetManagement;
this.deploymentManagement = deploymentManagement;
this.rolloutGroupManagement = rolloutGroupManagement;
@@ -70,6 +77,8 @@ public abstract class AbstractRolloutManagement implements RolloutManagement {
this.tenantAware = tenantAware;
this.lockRegistry = lockRegistry;
this.rolloutApprovalStrategy = rolloutApprovalStrategy;
this.tenantConfigurationManagement = tenantConfigurationManagement;
this.systemSecurityContext = systemSecurityContext;
}
protected RolloutGroupsValidation validateTargetsInGroups(final List<RolloutGroup> groups, final String baseFilter,
@@ -156,5 +165,4 @@ public abstract class AbstractRolloutManagement implements RolloutManagement {
return new AsyncResult<>(validateTargetsInGroups(
groups.stream().map(RolloutGroupCreate::build).collect(Collectors.toList()), baseFilter, totalTargets));
}
}

View File

@@ -10,8 +10,13 @@ package org.eclipse.hawkbit.repository.builder;
import java.util.Optional;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import org.eclipse.hawkbit.repository.ValidString;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.springframework.util.StringUtils;
/**
@@ -30,26 +35,77 @@ public abstract class AbstractRolloutUpdateCreate<T> extends AbstractNamedEntity
protected Long forcedTime;
protected Long startAt;
@Min(Action.WEIGHT_MIN)
@Max(Action.WEIGHT_MAX)
protected Integer weight;
/**
* {@link DistributionSet} of rollout
*
* @param set
* ID of the set
* @return this builder
*/
public T set(final long set) {
this.set = set;
return (T) this;
}
/**
* Filter of the rollout
*
* @param targetFilterQuery
* query
* @return this builder
*/
public T targetFilterQuery(final String targetFilterQuery) {
this.targetFilterQuery = StringUtils.trimWhitespace(targetFilterQuery);
return (T) this;
}
/**
* {@link ActionType} used for {@link Action}s
*
* @param actionType
* type
* @return this builder
*/
public T actionType(final ActionType actionType) {
this.actionType = actionType;
return (T) this;
}
/**
* forcedTime used for {@link Action}s
*
* @param forcedTime
* time
* @return this builder
*/
public T forcedTime(final Long forcedTime) {
this.forcedTime = forcedTime;
return (T) this;
}
/**
* weight used for {@link Action}s
*
* @param weight
* weight
* @return this builder
*/
public T weight(final Integer weight) {
this.weight = weight;
return (T) this;
}
/**
* Set start of the Rollout
*
* @param startAt
* start time point
* @return this builder
*/
public T startAt(final Long startAt) {
this.startAt = startAt;
return (T) this;
@@ -67,6 +123,10 @@ public abstract class AbstractRolloutUpdateCreate<T> extends AbstractNamedEntity
return Optional.ofNullable(forcedTime);
}
public Optional<Integer> getWeight() {
return Optional.ofNullable(weight);
}
public Optional<Long> getStartAt() {
return Optional.ofNullable(startAt);
}

View File

@@ -10,8 +10,13 @@ package org.eclipse.hawkbit.repository.builder;
import java.util.Optional;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import org.eclipse.hawkbit.repository.ValidString;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.util.StringUtils;
/**
@@ -31,6 +36,17 @@ public abstract class AbstractTargetFilterQueryUpdateCreate<T> extends AbstractB
protected ActionType actionType;
@Min(Action.WEIGHT_MIN)
@Max(Action.WEIGHT_MAX)
protected Integer weight;
/**
* Set DS ID of the {@link Action} created during auto assignment
*
* @param distributionSetId
* of the {@link TargetFilterQuery}
* @return this builder
*/
public T autoAssignDistributionSet(final Long distributionSetId) {
this.distributionSetId = distributionSetId;
return (T) this;
@@ -40,15 +56,46 @@ public abstract class AbstractTargetFilterQueryUpdateCreate<T> extends AbstractB
return Optional.ofNullable(distributionSetId);
}
/**
* Set {@link ActionType} of the {@link Action} created during auto
* assignment
*
* @param actionType
* of the {@link TargetFilterQuery}
* @return this builder
*/
public T autoAssignActionType(final ActionType actionType) {
this.actionType = actionType;
return (T) this;
}
/**
* Set weight of the {@link Action} created during auto assignment
*
* @param weight
* of the {@link TargetFilterQuery}
* @return this builder
*/
public T autoAssignWeight(final Integer weight) {
this.weight = weight;
return (T) this;
}
public Optional<Integer> getAutoAssignWeight() {
return Optional.ofNullable(weight);
}
public Optional<ActionType> getAutoAssignActionType() {
return Optional.ofNullable(actionType);
}
/**
* Set name of the filter
*
* @param name
* of the {@link TargetFilterQuery}
* @return this builder
*/
public T name(final String name) {
this.name = StringUtils.trimWhitespace(name);
return (T) this;
@@ -58,6 +105,13 @@ public abstract class AbstractTargetFilterQueryUpdateCreate<T> extends AbstractB
return Optional.ofNullable(name);
}
/**
* Set query used by the filter
*
* @param query
* of the {@link TargetFilterQuery}
* @return this builder
*/
public T query(final String query) {
this.query = StringUtils.trimWhitespace(query);
return (T) this;

View File

@@ -16,5 +16,4 @@ public class GenericRolloutUpdate extends AbstractRolloutUpdateCreate<RolloutUpd
public GenericRolloutUpdate(final Long id) {
super.id = id;
}
}

View File

@@ -212,6 +212,7 @@ public abstract class AbstractDsAssignmentStrategy {
final JpaAction actionForTarget = new JpaAction();
actionForTarget.setActionType(targetWithActionType.getActionType());
actionForTarget.setForcedTime(targetWithActionType.getForceTime());
actionForTarget.setWeight(targetWithActionType.getWeight());
actionForTarget.setActive(true);
actionForTarget.setTarget(target);
actionForTarget.setDistributionSet(set);

View File

@@ -28,7 +28,6 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -101,20 +100,33 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
List<Action> findByTargetAndActiveOrderByIdAsc(JpaTarget target, boolean active);
/**
* Retrieves the oldest {@link Action} that is active and referring to the
* given {@link Target}.
*
* @param sort
* order
* Retrieves the active {@link Action}s with the highest weights that refer
* to the given {@link Target}. If {@link Action}s have the same weight they
* are ordered ascending by ID (oldest ones first).
*
* @param pageable
* pageable
* @param controllerId
* the target to find assigned actions
* @param active
* the action active flag
*
* @return the found {@link Action}
* @return the found {@link Action}s
*/
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
Optional<Action> findFirstByTargetControllerIdAndActive(Sort sort, String controllerId, boolean active);
Page<Action> findByTargetControllerIdAndActiveIsTrueAndWeightIsNotNullOrderByWeightDescIdAsc(Pageable pageable,
String controllerId);
/**
* Retrieves the active {@link Action}s with the lowest IDs (the oldest one)
* whose weight is null and that that refers to the given {@link Target}.
*
* @param pageable
* pageable
* @param controllerId
* the target to find assigned actions
* @return the found {@link Action}s
*/
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
Page<Action> findByTargetControllerIdAndActiveIsTrueAndWeightIsNullOrderByIdAsc(Pageable pageable,
String controllerId);
/**
* Checks if an active action exists for given

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2019 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.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.model.Action;
import org.springframework.data.domain.PageRequest;
/**
* Implements utility methods for managing {@link Action}s
*/
public class JpaActionManagement {
protected final ActionRepository actionRepository;
protected final RepositoryProperties repositoryProperties;
protected JpaActionManagement(final ActionRepository actionRepository,
final RepositoryProperties repositoryProperties) {
this.actionRepository = actionRepository;
this.repositoryProperties = repositoryProperties;
}
protected List<Action> findActiveActionsWithHighestWeightConsideringDefault(final String controllerId,
final int maxActionCount) {
if (!actionRepository.activeActionExistsForControllerId(controllerId)) {
return Collections.emptyList();
}
final List<Action> actions = new ArrayList<>();
final PageRequest pageable = PageRequest.of(0, maxActionCount);
actions.addAll(actionRepository
.findByTargetControllerIdAndActiveIsTrueAndWeightIsNotNullOrderByWeightDescIdAsc(pageable, controllerId)
.getContent());
actions.addAll(actionRepository
.findByTargetControllerIdAndActiveIsTrueAndWeightIsNullOrderByIdAsc(pageable, controllerId)
.getContent());
final Comparator<Action> actionImportance = Comparator.comparingInt(this::getWeightConsideringDefault)
.reversed().thenComparing(Action::getId);
return actions.stream().sorted(actionImportance).limit(maxActionCount).collect(Collectors.toList());
}
protected int getWeightConsideringDefault(final Action action) {
return action.getWeight().orElse(repositoryProperties.getActionWeightIfAbsent());
}
}

View File

@@ -110,7 +110,7 @@ import com.google.common.collect.Sets;
*/
@Transactional(readOnly = true)
@Validated
public class JpaControllerManagement implements ControllerManagement {
public class JpaControllerManagement extends JpaActionManagement implements ControllerManagement {
private static final Logger LOG = LoggerFactory.getLogger(JpaControllerManagement.class);
private final BlockingDeque<TargetPoll> queue;
@@ -118,9 +118,6 @@ public class JpaControllerManagement implements ControllerManagement {
@Autowired
private EntityManager entityManager;
@Autowired
private ActionRepository actionRepository;
@Autowired
private TargetRepository targetRepository;
@@ -157,10 +154,9 @@ public class JpaControllerManagement implements ControllerManagement {
@Autowired
private TenantAware tenantAware;
private final RepositoryProperties repositoryProperties;
JpaControllerManagement(final ScheduledExecutorService executorService,
final RepositoryProperties repositoryProperties) {
final RepositoryProperties repositoryProperties, final ActionRepository actionRepository) {
super(actionRepository, repositoryProperties);
if (!repositoryProperties.isEagerPollPersistence()) {
executorService.scheduleWithFixedDelay(this::flushUpdateQueue,
@@ -171,8 +167,6 @@ public class JpaControllerManagement implements ControllerManagement {
} else {
queue = null;
}
this.repositoryProperties = repositoryProperties;
}
@Override
@@ -346,23 +340,19 @@ public class JpaControllerManagement implements ControllerManagement {
}
@Override
public Optional<Action> findOldestActiveActionByTarget(final String controllerId) {
if (!actionRepository.activeActionExistsForControllerId(controllerId)) {
return Optional.empty();
}
// used in favorite to findFirstByTargetAndActiveOrderByIdAsc due to
// DATAJPA-841 issue.
return actionRepository.findFirstByTargetControllerIdAndActive(new Sort(Direction.ASC, "id"), controllerId,
true);
public Optional<Action> findActiveActionWithHighestWeight(final String controllerId) {
return findActiveActionsWithHighestWeight(controllerId, 1).stream().findFirst();
}
@Override
public Page<Action> findActiveActionsByTarget(final Pageable pageable, final String controllerId) {
if (!actionRepository.activeActionExistsForControllerId(controllerId)) {
return Page.empty();
}
return actionRepository.findByActiveAndTarget(pageable, controllerId, true);
public List<Action> findActiveActionsWithHighestWeight(final String controllerId,
final int maxActionCount) {
return findActiveActionsWithHighestWeightConsideringDefault(controllerId, maxActionCount);
}
@Override
public int getWeightConsideringDefault(final Action action) {
return super.getWeightConsideringDefault(action);
}
@Override
@@ -376,7 +366,7 @@ public class JpaControllerManagement implements ControllerManagement {
}
@Override
public void deleteExistingTarget(@NotEmpty String controllerId) {
public void deleteExistingTarget(@NotEmpty final String controllerId) {
final Target target = targetRepository.findByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
targetRepository.deleteById(target.getId());

View File

@@ -8,7 +8,6 @@
*/
package org.eclipse.hawkbit.repository.jpa;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
import java.io.Serializable;
@@ -38,6 +37,7 @@ import org.eclipse.hawkbit.repository.ActionFields;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
@@ -57,6 +57,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.jpa.utils.TenantConfigHelper;
import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -103,7 +105,7 @@ import com.google.common.collect.Lists;
*/
@Transactional(readOnly = true)
@Validated
public class JpaDeploymentManagement implements DeploymentManagement {
public class JpaDeploymentManagement extends JpaActionManagement implements DeploymentManagement {
private static final Logger LOG = LoggerFactory.getLogger(JpaDeploymentManagement.class);
@@ -124,7 +126,6 @@ public class JpaDeploymentManagement implements DeploymentManagement {
}
private final EntityManager entityManager;
private final ActionRepository actionRepository;
private final DistributionSetRepository distributionSetRepository;
private final TargetRepository targetRepository;
private final ActionStatusRepository actionStatusRepository;
@@ -146,9 +147,10 @@ public class JpaDeploymentManagement implements DeploymentManagement {
final EventPublisherHolder eventPublisherHolder, final AfterTransactionCommitExecutor afterCommit,
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager,
final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final Database database) {
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final Database database,
final RepositoryProperties repositoryProperties) {
super(actionRepository, repositoryProperties);
this.entityManager = entityManager;
this.actionRepository = actionRepository;
this.distributionSetRepository = distributionSetRepository;
this.targetRepository = targetRepository;
this.actionStatusRepository = actionStatusRepository;
@@ -194,6 +196,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Transactional(isolation = Isolation.READ_COMMITTED)
public List<DistributionSetAssignmentResult> assignDistributionSets(
final List<DeploymentRequest> deploymentRequests, final String actionMessage) {
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
.validate(deploymentRequests);
return assignDistributionSets(deploymentRequests, actionMessage, onlineDsAssignmentStrategy);
}
@@ -275,8 +279,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
final AbstractDsAssignmentStrategy assignmentStrategy) {
final JpaDistributionSet distributionSetEntity = getAndValidateDsById(dsID);
final List<String> targetIds = targetsWithActionType.stream().map(TargetWithActionType::getControllerId).distinct()
.collect(Collectors.toList());
final List<String> targetIds = targetsWithActionType.stream().map(TargetWithActionType::getControllerId)
.distinct().collect(Collectors.toList());
final List<JpaTarget> targetEntities = assignmentStrategy.findTargetsForAssignment(targetIds,
distributionSetEntity.getId());
@@ -285,8 +289,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
return allTargetsAlreadyAssignedResult(distributionSetEntity, targetsWithActionType.size());
}
final List<JpaAction> assignedActions = doAssignDistributionSetToTargets(targetsWithActionType,
actionMessage, assignmentStrategy, distributionSetEntity, targetEntities);
final List<JpaAction> assignedActions = doAssignDistributionSetToTargets(targetsWithActionType, actionMessage,
assignmentStrategy, distributionSetEntity, targetEntities);
return buildAssignmentResult(distributionSetEntity, assignedActions, targetsWithActionType.size());
}
@@ -659,6 +663,16 @@ public class JpaDeploymentManagement implements DeploymentManagement {
return actionRepository.findByActiveAndTarget(pageable, controllerId, false);
}
@Override
public List<Action> findActiveActionsWithHighestWeight(final String controllerId, final int maxActionCount) {
return findActiveActionsWithHighestWeightConsideringDefault(controllerId, maxActionCount);
}
@Override
public int getWeightConsideringDefault(final Action action) {
return super.getWeightConsideringDefault(action);
}
@Override
public long countActionsByTarget(final String controllerId) {
throwExceptionIfTargetDoesNotExist(controllerId);
@@ -823,7 +837,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
}
private boolean isMultiAssignmentsEnabled() {
return getConfigValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class);
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
.isMultiAssignmentsEnabled();
}
private <T extends Serializable> T getConfigValue(final String key, final Class<T> valueType) {

View File

@@ -34,6 +34,7 @@ import org.eclipse.hawkbit.repository.RolloutHelper;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.RolloutStatusCache;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.builder.GenericRolloutUpdate;
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
@@ -43,6 +44,8 @@ import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEve
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
import org.eclipse.hawkbit.repository.exception.NoWeightProvidedInMultiAssignmentModeException;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
@@ -57,6 +60,8 @@ import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper;
import org.eclipse.hawkbit.repository.jpa.utils.TenantConfigHelper;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -74,6 +79,7 @@ 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.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -165,9 +171,12 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
final EventPublisherHolder eventPublisherHolder, final VirtualPropertyReplacer virtualPropertyReplacer,
final PlatformTransactionManager txManager, final TenantAware tenantAware, final LockRegistry lockRegistry,
final Database database, final RolloutApprovalStrategy rolloutApprovalStrategy) {
final Database database, final RolloutApprovalStrategy rolloutApprovalStrategy,
final TenantConfigurationManagement tenantConfigurationManagement,
final SystemSecurityContext systemSecurityContext) {
super(targetManagement, deploymentManagement, rolloutGroupManagement, distributionSetManagement, context,
virtualPropertyReplacer, txManager, tenantAware, lockRegistry, rolloutApprovalStrategy);
virtualPropertyReplacer, txManager, tenantAware, lockRegistry, rolloutApprovalStrategy,
tenantConfigurationManagement, systemSecurityContext);
this.eventPublisherHolder = eventPublisherHolder;
this.database = database;
}
@@ -226,7 +235,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
}
private JpaRollout createRollout(final JpaRollout rollout) {
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(rollout);
final Long totalTargets = targetManagement.countByRsql(rollout.getTargetFilterQuery());
if (totalTargets == 0) {
throw new ValidationException("Rollout does not match any existing targets");
@@ -618,6 +627,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
action.setStatus(Status.SCHEDULED);
action.setRollout(rollout);
action.setRolloutGroup(rolloutGroup);
rollout.getWeight().ifPresent(action::setWeight);
actionRepository.save(action);
});
}
@@ -1008,6 +1018,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
update.getDescription().ifPresent(rollout::setDescription);
update.getActionType().ifPresent(rollout::setActionType);
update.getForcedTime().ifPresent(rollout::setForcedTime);
update.getWeight().ifPresent(rollout::setWeight);
update.getStartAt().ifPresent(rollout::setStartAt);
update.getSet().ifPresent(setId -> {
final DistributionSet set = distributionSetManagement.get(setId)
@@ -1136,5 +1147,4 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
QuotaHelper.assertAssignmentQuota(target.getId(), requested, quota, Action.class, Target.class,
actionRepository::countByTargetId);
}
}

View File

@@ -18,6 +18,8 @@ import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.builder.GenericTargetFilterQueryUpdate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
@@ -32,11 +34,14 @@ import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper;
import org.eclipse.hawkbit.repository.jpa.utils.TenantConfigHelper;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
@@ -67,19 +72,24 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
private final DistributionSetManagement distributionSetManagement;
private final QuotaManagement quotaManagement;
private final TenantConfigurationManagement tenantConfigurationManagement;
private final SystemSecurityContext systemSecurityContext;
private final Database database;
JpaTargetFilterQueryManagement(final TargetFilterQueryRepository targetFilterQueryRepository,
final TargetRepository targetRepository, final VirtualPropertyReplacer virtualPropertyReplacer,
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
final Database database) {
final Database database, final TenantConfigurationManagement tenantConfigurationManagement,
final SystemSecurityContext systemSecurityContext) {
this.targetFilterQueryRepository = targetFilterQueryRepository;
this.targetRepository = targetRepository;
this.virtualPropertyReplacer = virtualPropertyReplacer;
this.distributionSetManagement = distributionSetManagement;
this.quotaManagement = quotaManagement;
this.database = database;
this.tenantConfigurationManagement = tenantConfigurationManagement;
this.systemSecurityContext = systemSecurityContext;
}
@Override
@@ -92,6 +102,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
// enforce the 'max targets per auto assign' quota right here even if
// the result of the filter query can vary over time
if (create.getAutoAssignDistributionSetId().isPresent()) {
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(create);
create.getQuery().ifPresent(this::assertMaxTargetsQuota);
}
@@ -222,30 +233,26 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Override
@Transactional
public TargetFilterQuery updateAutoAssignDSWithActionType(final long queryId, final Long dsId,
final ActionType actionType) {
final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(queryId);
if (dsId == null) {
public TargetFilterQuery updateAutoAssignDS(final AutoAssignDistributionSetUpdate update) {
final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(
update.getTargetFilterId());
if (update.getDsId() == null) {
targetFilterQuery.setAutoAssignDistributionSet(null);
targetFilterQuery.setAutoAssignActionType(null);
targetFilterQuery.setAutoAssignWeight(null);
} else {
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(update);
// we cannot be sure that the quota was enforced at creation time
// because the Target Filter Query REST API does not allow to
// specify an
// auto-assign distribution set when creating a target filter query
assertMaxTargetsQuota(targetFilterQuery.getQuery());
final JpaDistributionSet distributionSetToAutoAssign = findDistributionSetAndThrowExceptionIfNotFound(dsId);
// must be completed and not soft deleted
verifyDistributionSetAndThrowExceptionIfNotValid(distributionSetToAutoAssign);
targetFilterQuery.setAutoAssignDistributionSet(distributionSetToAutoAssign);
// the action type is set to FORCED per default (when not explicitly
// specified)
targetFilterQuery.setAutoAssignActionType(sanitizeAutoAssignActionType(actionType));
final JpaDistributionSet ds = findDistributionSetAndThrowExceptionIfNotFound(update.getDsId());
verifyDistributionSetAndThrowExceptionIfNotValid(ds);
targetFilterQuery.setAutoAssignDistributionSet(ds);
targetFilterQuery.setAutoAssignActionType(sanitizeAutoAssignActionType(update.getActionType()));
targetFilterQuery.setAutoAssignWeight(update.getWeight());
}
return targetFilterQueryRepository.save(targetFilterQuery);
}
@@ -288,5 +295,4 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
targetRepository.count(RSQLUtility.parse(query, TargetFields.class, virtualPropertyReplacer, database)),
quotaManagement.getMaxTargetsPerAutoAssignment(), Target.class, TargetFilterQuery.class);
}
}

View File

@@ -541,9 +541,11 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final TargetFilterQueryRepository targetFilterQueryRepository, final TargetRepository targetRepository,
final VirtualPropertyReplacer virtualPropertyReplacer,
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
final JpaProperties properties) {
final JpaProperties properties, final TenantConfigurationManagement tenantConfigurationManagement,
final SystemSecurityContext systemSecurityContext) {
return new JpaTargetFilterQueryManagement(targetFilterQueryRepository, targetRepository,
virtualPropertyReplacer, distributionSetManagement, quotaManagement, properties.getDatabase());
virtualPropertyReplacer, distributionSetManagement, quotaManagement, properties.getDatabase(),
tenantConfigurationManagement, systemSecurityContext);
}
/**
@@ -620,10 +622,13 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
final EventPublisherHolder eventPublisherHolder, final VirtualPropertyReplacer virtualPropertyReplacer,
final PlatformTransactionManager txManager, final TenantAware tenantAware, final LockRegistry lockRegistry,
final JpaProperties properties, final RolloutApprovalStrategy rolloutApprovalStrategy) {
final JpaProperties properties, final RolloutApprovalStrategy rolloutApprovalStrategy,
final TenantConfigurationManagement tenantConfigurationManagement,
final SystemSecurityContext systemSecurityContext) {
return new JpaRolloutManagement(targetManagement, deploymentManagement, rolloutGroupManagement,
distributionSetManagement, context, eventPublisherHolder, virtualPropertyReplacer, txManager,
tenantAware, lockRegistry, properties.getDatabase(), rolloutApprovalStrategy);
tenantAware, lockRegistry, properties.getDatabase(), rolloutApprovalStrategy,
tenantConfigurationManagement, systemSecurityContext);
}
/**
@@ -671,11 +676,11 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final PlatformTransactionManager txManager,
final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware,
final JpaProperties properties) {
final JpaProperties properties, final RepositoryProperties repositoryProperties) {
return new JpaDeploymentManagement(entityManager, actionRepository, distributionSetRepository, targetRepository,
actionStatusRepository, auditorProvider, eventPublisherHolder, afterCommit, virtualPropertyReplacer,
txManager, tenantConfigurationManagement, quotaManagement, systemSecurityContext, tenantAware,
properties.getDatabase());
properties.getDatabase(), repositoryProperties);
}
/**
@@ -686,8 +691,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Bean
@ConditionalOnMissingBean
ControllerManagement controllerManagement(final ScheduledExecutorService executorService,
final RepositoryProperties repositoryProperties) {
return new JpaControllerManagement(executorService, repositoryProperties);
final RepositoryProperties repositoryProperties, final ActionRepository actionRepository) {
return new JpaControllerManagement(executorService, repositoryProperties, actionRepository);
}
@Bean

View File

@@ -97,6 +97,8 @@ public class AutoAssignChecker {
final Page<TargetFilterQuery> filterQueries = targetFilterQueryManagement.findWithAutoAssignDS(pageRequest);
// we should ensure that the filter queries are executed
// in the order of weights
for (final TargetFilterQuery filterQuery : filterQueries) {
checkByTargetFilterQueryAndAssignDS(filterQuery);
}
@@ -143,7 +145,8 @@ public class AutoAssignChecker {
return DeploymentHelper.runInNewTransaction(transactionManager, "autoAssignDSToTargets",
Isolation.READ_COMMITTED.value(), status -> {
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(
targetFilterQuery.getQuery(), dsId, targetFilterQuery.getAutoAssignActionType(), PAGE_SIZE);
targetFilterQuery.getQuery(), dsId, targetFilterQuery.getAutoAssignActionType(),
targetFilterQuery.getAutoAssignWeight().orElse(null), PAGE_SIZE);
final int count = deploymentRequests.size();
if (count > 0) {
deploymentManagement.assignDistributionSets(deploymentRequests, actionMessage);
@@ -168,17 +171,15 @@ public class AutoAssignChecker {
* @return list of targets with action type
*/
private List<DeploymentRequest> createAssignmentRequests(final String targetFilterQuery, final Long dsId,
final ActionType type, final int count) {
final ActionType type, final Integer weight, final int count) {
final Page<Target> targets = targetManagement.findByTargetFilterQueryAndNonDS(PageRequest.of(0, count), dsId,
targetFilterQuery);
// the action type is set to FORCED per default (when not explicitly
// specified)
final ActionType autoAssignActionType = type == null ? ActionType.FORCED : type;
return targets.getContent().stream()
.map(t -> DeploymentManagement.deploymentRequest(t.getControllerId(), dsId)
.setActionType(autoAssignActionType).build())
.collect(Collectors.toList());
return targets.getContent().stream().map(t -> DeploymentManagement.deploymentRequest(t.getControllerId(), dsId)
.setActionType(autoAssignActionType).setWeight(weight).build()).collect(Collectors.toList());
}
}

View File

@@ -31,6 +31,7 @@ public class JpaRolloutCreate extends AbstractRolloutUpdateCreate<RolloutCreate>
rollout.setDistributionSet(findDistributionSetAndThrowExceptionIfNotFound(set));
rollout.setTargetFilterQuery(targetFilterQuery);
rollout.setStartAt(startAt);
rollout.setWeight(weight);
if (actionType != null) {
rollout.setActionType(actionType);

View File

@@ -9,6 +9,7 @@
package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.builder.GenericTargetFilterQueryUpdate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
@@ -36,4 +37,9 @@ public class JpaTargetFilterQueryBuilder implements TargetFilterQueryBuilder {
return new JpaTargetFilterQueryCreate(distributionSetManagement);
}
@Override
public AutoAssignDistributionSetUpdate updateAutoAssign(final long id) {
return new AutoAssignDistributionSetUpdate(id);
}
}

View File

@@ -36,7 +36,8 @@ public class JpaTargetFilterQueryCreate extends AbstractTargetFilterQueryUpdateC
return new JpaTargetFilterQuery(name, query,
getAutoAssignDistributionSetId().map(this::findDistributionSetAndThrowExceptionIfNotFound).orElse(null),
getAutoAssignActionType().filter(JpaTargetFilterQueryCreate::isAutoAssignActionTypeValid).orElse(null));
getAutoAssignActionType().filter(JpaTargetFilterQueryCreate::isAutoAssignActionTypeValid).orElse(null),
weight);
}
private DistributionSet findDistributionSetAndThrowExceptionIfNotFound(final Long setId) {

View File

@@ -28,6 +28,8 @@ import javax.persistence.NamedEntityGraphs;
import javax.persistence.NamedSubgraph;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
@@ -89,6 +91,11 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Column(name = "forced_time")
private long forcedTime;
@Column(name = "weight")
@Min(Action.WEIGHT_MIN)
@Max(Action.WEIGHT_MAX)
private Integer weight;
@Column(name = "status", nullable = false)
@ObjectTypeConverter(name = "status", objectType = Action.Status.class, dataType = Integer.class, conversionValues = {
@ConversionValue(objectValue = "FINISHED", dataValue = "0"),
@@ -126,7 +133,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Column(name = "maintenance_time_zone", updatable = false, length = Action.MAINTENANCE_WINDOW_TIMEZONE_LENGTH)
private String maintenanceWindowTimeZone;
@Column(name = "external_ref", length = Action.EXTERNAL_REF_MAX_LENGTH)
private String externalRef;
@@ -192,6 +199,15 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
this.forcedTime = forcedTime;
}
@Override
public Optional<Integer> getWeight() {
return Optional.ofNullable(weight);
}
public void setWeight(final Integer weight) {
this.weight = weight;
}
@Override
public RolloutGroup getRolloutGroup() {
return rolloutGroup;
@@ -213,8 +229,8 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Override
public String toString() {
return "JpaAction [distributionSet=" + distributionSet.getId() + ", version=" + getOptLockRevision() + ", id="
+ getId() + ", actionType=" + getActionType() + ", isActive=" + isActive() + ", createdAt="
+ getCreatedAt() + ", lastModifiedAt=" + getLastModifiedAt() + "]";
+ getId() + ", actionType=" + getActionType() + ", weight=" + getWeight() + ", isActive=" + isActive()
+ ", createdAt=" + getCreatedAt() + ", lastModifiedAt=" + getLastModifiedAt() + "]";
}
@Override
@@ -337,7 +353,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
}
@Override
public void setExternalRef(String externalRef) {
public void setExternalRef(final String externalRef) {
this.externalRef = externalRef;
}

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.persistence.Column;
@@ -23,6 +24,8 @@ import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@@ -90,7 +93,7 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
@ConversionValue(objectValue = "DELETING", dataValue = "9"),
@ConversionValue(objectValue = "DELETED", dataValue = "10"),
@ConversionValue(objectValue = "WAITING_FOR_APPROVAL", dataValue = "11"),
@ConversionValue(objectValue = "APPROVAL_DENIED", dataValue = "12")})
@ConversionValue(objectValue = "APPROVAL_DENIED", dataValue = "12") })
@Convert("rolloutstatus")
@NotNull
private RolloutStatus status = RolloutStatus.CREATING;
@@ -131,6 +134,11 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
@Size(max = Rollout.APPROVAL_REMARK_MAX_SIZE)
private String approvalRemark;
@Column(name = "weight")
@Min(Action.WEIGHT_MIN)
@Max(Action.WEIGHT_MAX)
private Integer weight;
@Transient
private transient TotalTargetCountStatus totalTargetCountStatus;
@@ -204,6 +212,15 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
this.forcedTime = forcedTime;
}
@Override
public Optional<Integer> getWeight() {
return Optional.ofNullable(weight);
}
public void setWeight(final Integer weight) {
this.weight = weight;
}
@Override
public long getTotalTargets() {
return totalTargets;
@@ -299,5 +316,4 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
public void setApprovalRemark(final String approvalRemark) {
this.approvalRemark = approvalRemark;
}
}

View File

@@ -8,6 +8,8 @@
*/
package org.eclipse.hawkbit.repository.jpa.model;
import java.util.Optional;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
@@ -66,11 +68,15 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
@ObjectTypeConverter(name = "autoAssignActionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
// Conversion for 'TIMEFORCED' is disabled because it is not permitted in autoAssignment
@ConversionValue(objectValue = "DOWNLOAD_ONLY", dataValue = "3")})
// Conversion for 'TIMEFORCED' is disabled because it is not
// permitted in autoAssignment
@ConversionValue(objectValue = "DOWNLOAD_ONLY", dataValue = "3") })
@Convert("autoAssignActionType")
private ActionType autoAssignActionType;
@Column(name = "auto_assign_weight", nullable = true)
private Integer autoAssignWeight;
public JpaTargetFilterQuery() {
// Default constructor for JPA.
}
@@ -86,13 +92,16 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
* of the {@link TargetFilterQuery}.
* @param autoAssignActionType
* of the {@link TargetFilterQuery}.
* @param autoAssignWeight
* of the {@link TargetFilterQuery}.
*/
public JpaTargetFilterQuery(final String name, final String query, final DistributionSet autoAssignDistributionSet,
final ActionType autoAssignActionType) {
final ActionType autoAssignActionType, final Integer autoAssignWeight) {
this.name = name;
this.query = query;
this.autoAssignDistributionSet = (JpaDistributionSet) autoAssignDistributionSet;
this.autoAssignActionType = autoAssignActionType;
this.autoAssignWeight = autoAssignWeight;
}
@Override
@@ -131,6 +140,15 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
this.autoAssignActionType = actionType;
}
@Override
public Optional<Integer> getAutoAssignWeight() {
return Optional.ofNullable(autoAssignWeight);
}
public void setAutoAssignWeight(final Integer weight) {
this.autoAssignWeight = weight;
}
@Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2019 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.utils;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.security.SystemSecurityContext;
/**
* A collection of static helper methods for the tenant configuration
*/
public final class TenantConfigHelper {
private final TenantConfigurationManagement tenantConfigurationManagement;
private final SystemSecurityContext systemSecurityContext;
private TenantConfigHelper(final SystemSecurityContext systemSecurityContext,
final TenantConfigurationManagement tenantConfigurationManagement) {
this.systemSecurityContext = systemSecurityContext;
this.tenantConfigurationManagement = tenantConfigurationManagement;
}
/**
* Setting the context of the tenant.
*
* @param systemSecurityContext
* Security context used to get the tenant and for execution
* @param tenantConfigurationManagement
* to get the value from
* @return is active
*/
public static TenantConfigHelper usingContext(final SystemSecurityContext systemSecurityContext,
final TenantConfigurationManagement tenantConfigurationManagement) {
return new TenantConfigHelper(systemSecurityContext, tenantConfigurationManagement);
}
/**
* Is multi-assignments enabled for the current tenant
*
* @return is active
*/
public boolean isMultiAssignmentsEnabled() {
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
.getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue());
}
}

View File

@@ -0,0 +1,132 @@
/**
* Copyright (c) 2019 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.utils;
import java.util.List;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
import org.eclipse.hawkbit.repository.exception.NoWeightProvidedInMultiAssignmentModeException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryCreate;
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.security.SystemSecurityContext;
/**
* Utility class to handle weight validation in Rollout, Auto Assignments, and
* Online Assignment.
*/
public final class WeightValidationHelper {
private final TenantConfigurationManagement tenantConfigurationManagement;
private final SystemSecurityContext systemSecurityContext;
private WeightValidationHelper(final SystemSecurityContext systemSecurityContext,
final TenantConfigurationManagement tenantConfigurationManagement) {
this.systemSecurityContext = systemSecurityContext;
this.tenantConfigurationManagement = tenantConfigurationManagement;
}
/**
* Setting the context of the tenant
*
* @param systemSecurityContext
* security context used to get the tenant and for execution
* @param tenantConfigurationManagement
* to get the value from
*/
public static WeightValidationHelper usingContext(final SystemSecurityContext systemSecurityContext,
final TenantConfigurationManagement tenantConfigurationManagement) {
return new WeightValidationHelper(systemSecurityContext, tenantConfigurationManagement);
}
/**
* Validating weights associated with all the {@link DeploymentRequest}s
*
* @param deploymentRequests
* the {@linkplain List} of {@link DeploymentRequest}s
*/
public void validate(final List<DeploymentRequest> deploymentRequests) {
final long assignmentsWithWeight = deploymentRequests.stream()
.filter(request -> request.getTargetWithActionType().getWeight() != null).count();
final boolean containsAssignmentWithWeight = assignmentsWithWeight > 0;
final boolean containsAssignmentWithoutWeight = assignmentsWithWeight < deploymentRequests.size();
validateWeight(containsAssignmentWithWeight, containsAssignmentWithoutWeight);
}
/**
* Validating weight associated with the {@link Rollout}
*
* @param rollout
* the {@linkplain Rollout}
*/
public void validate(final Rollout rollout) {
validateWeight(rollout.getWeight().orElse(null));
}
/**
* Validating weight associated with the target filter query
*
* @param targetFilterQueryCreate
* the target filter query
*/
public void validate(final JpaTargetFilterQueryCreate targetFilterQueryCreate) {
validateWeight(targetFilterQueryCreate.getAutoAssignWeight().orElse(null));
}
/**
* Validating weight associated with the auto assignment
*
* @param autoAssignDistributionSetUpdate
* the auto assignment distribution set update
*/
public void validate(final AutoAssignDistributionSetUpdate autoAssignDistributionSetUpdate) {
validateWeight(autoAssignDistributionSetUpdate.getWeight());
}
/**
* Checks if the weight is valid
*
* @param weight
* weight tied to the rollout, auto assignment, or online
* assignment.
*/
public void validateWeight(final Integer weight) {
final boolean hasWeight = weight != null;
validateWeight(hasWeight, !hasWeight);
}
/**
* Checks if the weight is valid with the multi-assignments being turned
* off/on.
*
* @param hasWeight
* indicator of the weight if it has numerical value
* @param hasNoWeight
* indicator of the weight if it doesn't have a numerical value
*/
public void validateWeight(final boolean hasWeight, final boolean hasNoWeight) {
// remove bypassing the weight enforcement as soon as weight can be set
// via UI
final boolean bypassWeightEnforcement = true;
final boolean multiAssignmentsEnabled = TenantConfigHelper
.usingContext(systemSecurityContext, tenantConfigurationManagement).isMultiAssignmentsEnabled();
if (!multiAssignmentsEnabled && hasWeight) {
throw new MultiAssignmentIsNotEnabledException();
} else if (bypassWeightEnforcement) {
return;
} else if (multiAssignmentsEnabled && hasNoWeight) {
throw new NoWeightProvidedInMultiAssignmentModeException();
}
}
}

View File

@@ -0,0 +1,3 @@
ALTER TABLE sp_action ADD weight INT;
ALTER TABLE sp_rollout ADD weight INT;
ALTER TABLE sp_target_filter_query ADD auto_assign_weight INT;

View File

@@ -0,0 +1,3 @@
ALTER TABLE sp_action ADD weight INT;
ALTER TABLE sp_rollout ADD weight INT;
ALTER TABLE sp_target_filter_query ADD auto_assign_weight INT;

View File

@@ -0,0 +1,3 @@
ALTER TABLE sp_action ADD weight INT;
ALTER TABLE sp_rollout ADD weight INT;
ALTER TABLE sp_target_filter_query ADD auto_assign_weight INT;

View File

@@ -0,0 +1,3 @@
ALTER TABLE sp_action ADD weight INT;
ALTER TABLE sp_rollout ADD weight INT;
ALTER TABLE sp_target_filter_query ADD auto_assign_weight INT;

View File

@@ -105,5 +105,4 @@ public class RemoteIdEventTest extends AbstractRemoteEventTest {
// gets added because events inherit from of java.util.EventObject
assertThat(underTestCreatedEvent).isEqualToIgnoringGivenFields(event, "source");
}
}

View File

@@ -98,5 +98,4 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
assertThat(actionProperties).isEqualToComparingFieldByField(new ActionProperties(action));
assertThat(underTest.getDistributionSetId()).isEqualTo(action.getDistributionSet().getId());
}
}

View File

@@ -104,7 +104,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(controllerManagement.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(),
module.getId())).isNotPresent();
assertThat(controllerManagement.findOldestActiveActionByTarget(NOT_EXIST_ID)).isNotPresent();
assertThat(controllerManagement.findActiveActionWithHighestWeight(NOT_EXIST_ID)).isNotPresent();
assertThat(controllerManagement.hasTargetArtifactAssigned(target.getControllerId(), "XXX")).isFalse();
assertThat(controllerManagement.hasTargetArtifactAssigned(target.getId(), "XXX")).isFalse();
@@ -457,7 +457,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
.getContent();
assertThat(actionStatusList.get(actionStatusList.size() - 1).getStatus()).isEqualTo(expectedActionStatus);
if (actionActive) {
assertThat(controllerManagement.findOldestActiveActionByTarget(controllerId).get().getId())
assertThat(controllerManagement.findActiveActionWithHighestWeight(controllerId).get().getId())
.isEqualTo(actionId);
}
}
@@ -501,7 +501,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Register a controller which does not exist")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 2)})
@Expect(type = TargetPollEvent.class, count = 2) })
public void findOrRegisterTargetIfItDoesNotExist() {
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST);
assertThat(target).as("target should not be null").isNotNull();
@@ -517,7 +517,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Expect(type = TargetPollEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 1) })
public void findOrRegisterTargetIfItDoesNotExistWithName() {
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST, "TestName");
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
"ChangedTestName");
assertThat(target.getId()).as("Target should be the equals").isEqualTo(sameTarget.getId());
assertThat(target.getName()).as("Taget names should be different").isNotEqualTo(sameTarget.getName());
@@ -560,8 +560,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
.isThrownBy(() -> controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST));
verify(mockTargetRepository, times(TX_RT_MAX)).findOne(any());
}
finally {
} finally {
// revert
((JpaControllerManagement) controllerManagement).setTargetRepository(targetRepository);
}
@@ -597,22 +596,23 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Register a controller which does not exist, then update the controller twice, first time by providing a name property and second time without a new name")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 3) ,
@Expect(type = TargetUpdatedEvent.class, count = 1)})
@Expect(type = TargetPollEvent.class, count = 3), @Expect(type = TargetUpdatedEvent.class, count = 1) })
public void findOrRegisterTargetIfItDoesNotExistDoesUpdateNameOnExistingTargetProperly() {
String controllerId = "12345";
String targetName = "UpdatedName";
final String controllerId = "12345";
final String targetName = "UpdatedName";
final Target newTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, LOCALHOST);
final Target newTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, LOCALHOST);
assertThat(newTarget.getName()).isEqualTo(controllerId);
Target firstTimeUpdatedTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, LOCALHOST, targetName);
final Target firstTimeUpdatedTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId,
LOCALHOST, targetName);
assertThat(firstTimeUpdatedTarget.getName()).isEqualTo(targetName);
//Name should not change to default (name=targetId) if target is updated without new name provided
Target secondTimeUpdatedTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, LOCALHOST);
// Name should not change to default (name=targetId) if target is
// updated without new name provided
final Target secondTimeUpdatedTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId,
LOCALHOST);
assertThat(secondTimeUpdatedTarget.getName()).isEqualTo(targetName);
}
@@ -633,8 +633,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
.isThrownBy(() -> controllerManagement.findOrRegisterTargetIfItDoesNotExist("1234", LOCALHOST));
verify(mockTargetRepository, times(1)).findOne(any());
verify(mockTargetRepository, times(1)).save(any());
}
finally {
} finally {
// revert
((JpaControllerManagement) controllerManagement).setTargetRepository(targetRepository);
}
@@ -651,13 +650,11 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
when(mockTargetRepository.findOne(any())).thenThrow(RuntimeException.class);
try {
assertThatExceptionOfType(RuntimeException.class)
.as("Expected a RuntimeException to be thrown!")
assertThatExceptionOfType(RuntimeException.class).as("Expected a RuntimeException to be thrown!")
.isThrownBy(() -> controllerManagement.findOrRegisterTargetIfItDoesNotExist("aControllerId",
LOCALHOST));
verify(mockTargetRepository, times(1)).findOne(any());
}
finally {
} finally {
// revert
((JpaControllerManagement) controllerManagement).setTargetRepository(targetRepository);
}
@@ -1054,9 +1051,8 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
Collections.singletonMap(keyValid, valueTooLong), null));
assertThatExceptionOfType(InvalidTargetAttributeException.class)
.as("Attribute with key NULL should not be created")
.isThrownBy(() -> controllerManagement.updateControllerAttributes(controllerId,
Collections.singletonMap(keyNull, valueValid), null));
.as("Attribute with key NULL should not be created").isThrownBy(() -> controllerManagement
.updateControllerAttributes(controllerId, Collections.singletonMap(keyNull, valueValid), null));
}
@Test
@@ -1298,9 +1294,9 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(actionId).isNotNull();
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.as("No QuotaExceededException thrown for too many DOWNLOADED updateActionStatus updates")
.isThrownBy(() -> IntStream.range(0, maxMessages).forEach(i -> controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.DOWNLOADED))));
.as("No QuotaExceededException thrown for too many DOWNLOADED updateActionStatus updates").isThrownBy(
() -> IntStream.range(0, maxMessages).forEach(i -> controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(actionId).status(Status.DOWNLOADED))));
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.as("No QuotaExceededException thrown for too many ERROR updateActionStatus updates")
@@ -1326,18 +1322,18 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(actionId).isNotNull();
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.as("No QuotaExceededException thrown for too many DOWNLOADED updateActionStatus updates")
.isThrownBy( ()-> IntStream.range(0, maxMessages).forEach(i -> controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.DOWNLOADED))));
.as("No QuotaExceededException thrown for too many DOWNLOADED updateActionStatus updates").isThrownBy(
() -> IntStream.range(0, maxMessages).forEach(i -> controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(actionId).status(Status.DOWNLOADED))));
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.as("No QuotaExceededException thrown for too many ERROR updateActionStatus updates")
.isThrownBy(()->IntStream.range(0, maxMessages).forEach(i -> controllerManagement
.isThrownBy(() -> IntStream.range(0, maxMessages).forEach(i -> controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.ERROR))));
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.as("No QuotaExceededException thrown for too many FINISHED updateActionStatus updates")
.isThrownBy(()->IntStream.range(0, maxMessages).forEach(i -> controllerManagement
.isThrownBy(() -> IntStream.range(0, maxMessages).forEach(i -> controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.FINISHED))));
}
@@ -1472,6 +1468,39 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertActionStatus(actionId, controllerId, TargetUpdateStatus.IN_SYNC, actionStatus, actionStatus, false);
}
@Test
@Description("Actions are exposed according to thier weight in multi assignment mode.")
public void actionsAreExposedAccordingToTheirWeight() {
final String targetId = testdataFactory.createTarget().getControllerId();
final DistributionSet ds = testdataFactory.createDistributionSet();
final Long actionWeightNull = assignDistributionSet(ds.getId(), targetId).getAssignedEntity().get(0).getId();
enableMultiAssignments();
final Long actionWeight500old = assignDistributionSet(ds.getId(), targetId, 500).getAssignedEntity().get(0)
.getId();
final Long actionWeight500new = assignDistributionSet(ds.getId(), targetId, 500).getAssignedEntity().get(0)
.getId();
final Long actionWeight1000 = assignDistributionSet(ds.getId(), targetId, 1000).getAssignedEntity().get(0)
.getId();
assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId).get().getId())
.isEqualTo(actionWeightNull);
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionWeightNull).status(Status.FINISHED));
assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId).get().getId())
.isEqualTo(actionWeight1000);
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionWeight1000).status(Status.FINISHED));
assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId).get().getId())
.isEqualTo(actionWeight500old);
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionWeight500old).status(Status.FINISHED));
assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId).get().getId())
.isEqualTo(actionWeight500new);
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionWeight500new).status(Status.FINISHED));
assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId)).isEmpty();
}
private void assertAssignedDistributionSetId(final String controllerId, final Long dsId) {
final Optional<Target> target = controllerManagement.getByControllerId(controllerId);
assertThat(target).isPresent();
@@ -1494,12 +1523,10 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(actionRepository.activeActionExistsForControllerId(controllerId)).isEqualTo(false);
}
@Test
@Description("Delete a target on requested target deletion from client side")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1),
@Expect(type = TargetDeletedEvent.class, count = 1)})
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TargetDeletedEvent.class, count = 1) })
public void deleteTargetWithValidThingId() {
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST);
assertThat(target).as("target should not be null").isNotNull();
@@ -1512,7 +1539,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Delete a target with a non existing thingId")
@ExpectEvents({@Expect(type = TargetDeletedEvent.class, count = 0)})
@ExpectEvents({ @Expect(type = TargetDeletedEvent.class, count = 0) })
public void deleteTargetWithInvalidThingId() {
assertThatExceptionOfType(EntityNotFoundException.class)
.as("No EntityNotFoundException thrown when deleting a non-existing target")
@@ -1522,9 +1549,8 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Delete a target after it has been deleted already")
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1),
@Expect(type = TargetDeletedEvent.class, count = 1)})
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TargetDeletedEvent.class, count = 1) })
public void deleteTargetAfterItWasDeleted() {
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST);
assertThat(target).as("target should not be null").isNotNull();

View File

@@ -22,6 +22,8 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import javax.validation.ConstraintViolationException;
import org.assertj.core.api.Assertions;
import org.eclipse.hawkbit.repository.ActionStatusFields;
import org.eclipse.hawkbit.repository.DeploymentManagement;
@@ -171,7 +173,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> assignDistributionSet(ds1, Collections.singletonList(testTarget)));
.isThrownBy(() -> assignDistributionSet(ds1.getId(), testTarget.getControllerId(), 77));
}
@Test
@@ -563,16 +565,17 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
public void previousAssignmentsAreNotCanceledInMultiAssignMode() {
enableMultiAssignments();
final List<Target> targets = testdataFactory.createTargets(10);
final List<String> targetIds = targets.stream().map(Target::getControllerId).collect(Collectors.toList());
// First assignment
final DistributionSet ds1 = testdataFactory.createDistributionSet("Multi-assign-1");
assignDistributionSet(ds1, targets);
assignDistributionSet(ds1.getId(), targetIds, 77);
assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, Status.RUNNING);
// Second assignment
final DistributionSet ds2 = testdataFactory.createDistributionSet("Multi-assign-2");
assignDistributionSet(ds2, targets);
assignDistributionSet(ds2.getId(), targetIds, 45);
assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), STATE_ACTIVE, Status.RUNNING);
assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, Status.RUNNING);
@@ -601,7 +604,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
public void multiassignmentInOneRequest() {
final List<Target> targets = testdataFactory.createTargets(2);
final List<DistributionSet> distributionSets = testdataFactory.createDistributionSets(2);
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(distributionSets, targets);
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(distributionSets, targets, 34);
enableMultiAssignments();
final List<DistributionSetAssignmentResult> results = deploymentManagement
@@ -614,7 +617,14 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
.stream().map(action -> action.getDistributionSet().getId()).collect(Collectors.toList());
assertThat(assignedDsIds).containsExactlyInAnyOrderElementsOf(dsIds);
});
}
protected List<DeploymentRequest> createAssignmentRequests(final Collection<DistributionSet> distributionSets,
final Collection<Target> targets, final int weight) {
final List<DeploymentRequest> deploymentRequests = new ArrayList<>();
distributionSets.forEach(ds -> targets.forEach(target -> deploymentRequests.add(DeploymentManagement
.deploymentRequest(target.getControllerId(), ds.getId()).setWeight(weight).build())));
return deploymentRequests;
}
@Test
@@ -624,9 +634,10 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
final List<DistributionSet> distributionSets = testdataFactory.createDistributionSets(2);
final DeploymentRequest targetToDS0 = DeploymentManagement
.deploymentRequest(target.getControllerId(), distributionSets.get(0).getId()).build();
.deploymentRequest(target.getControllerId(), distributionSets.get(0).getId()).setWeight(78).build();
final DeploymentRequest targetToDS1 = DeploymentManagement
.deploymentRequest(target.getControllerId(), distributionSets.get(1).getId()).build();
.deploymentRequest(target.getControllerId(), distributionSets.get(1).getId()).setWeight(565).build();
Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class)
.isThrownBy(() -> deploymentManagement.assignDistributionSets(Arrays.asList(targetToDS0, targetToDS1)));
@@ -638,17 +649,26 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Duplicate Assignments are removed from a request when multiassignment is disabled, otherwise not")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 3), @Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = MultiActionEvent.class, count = 1) })
public void duplicateAssignmentsInRequestAreOnlyRemovedIfMultiassignmentDisabled() {
final Target target = testdataFactory.createTarget();
final DistributionSet ds = testdataFactory.createDistributionSet();
final String targetId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
final List<DeploymentRequest> twoEqualAssignments = Collections.nCopies(2,
DeploymentManagement.deploymentRequest(target.getControllerId(), ds.getId()).build());
DeploymentManagement.deploymentRequest(targetId, dsId).build());
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignments)))
.isEqualTo(1);
enableMultiAssignments();
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignments)))
final List<DeploymentRequest> twoEqualAssignmentsWithWeight = Collections.nCopies(2,
DeploymentManagement.deploymentRequest(targetId, dsId).setWeight(555).build());
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignmentsWithWeight)))
.isEqualTo(2);
}
@@ -668,7 +688,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
final Long dsId = testdataFactory.createDistributionSet().getId();
final List<DeploymentRequest> deploymentRequests = Collections.nCopies(maxActions + 1,
DeploymentManagement.deploymentRequest(controllerId, dsId).build());
DeploymentManagement.deploymentRequest(controllerId, dsId).setWeight(24).build());
enableMultiAssignments();
Assertions.assertThatExceptionOfType(AssignmentQuotaExceededException.class)
@@ -676,6 +696,67 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
assertThat(actionRepository.countByTargetControllerId(controllerId)).isEqualTo(0);
}
@Test
@Description("An assignment request without a weight is ok when multi assignment in enabled.")
public void weightNotRequiredInMultiAssignmentMode() {
final String targetId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
final DeploymentRequest assignWithoutWeight = DeploymentManagement.deploymentRequest(targetId, dsId).build();
final DeploymentRequest assignWithWeight = DeploymentManagement.deploymentRequest(targetId, dsId).setWeight(567)
.build();
enableMultiAssignments();
deploymentManagement.assignDistributionSets(Arrays.asList(assignWithoutWeight, assignWithWeight));
}
@Test
@Description("An assignment request containing a weight causes an error when multi assignment in disabled.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void weightNotAllowedWhenMultiAssignmentModeNotEnabled() {
final String targetId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
final DeploymentRequest assignWithoutWeight = DeploymentManagement.deploymentRequest(targetId, dsId)
.setWeight(456).build();
Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class).isThrownBy(
() -> deploymentManagement.assignDistributionSets(Collections.singletonList(assignWithoutWeight)));
}
@Test
@Description("Weights are validated and contained in the resulting Action.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = MultiActionEvent.class, count = 2) })
public void weightValidatedAndSaved() {
final String targetId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
final DeploymentRequest valideRequest1 = DeploymentManagement.deploymentRequest(targetId, dsId)
.setWeight(Action.WEIGHT_MAX).build();
final DeploymentRequest valideRequest2 = DeploymentManagement.deploymentRequest(targetId, dsId)
.setWeight(Action.WEIGHT_MIN).build();
final DeploymentRequest weightTooLow = DeploymentManagement.deploymentRequest(targetId, dsId)
.setWeight(Action.WEIGHT_MIN - 1).build();
final DeploymentRequest weightTooHigh = DeploymentManagement.deploymentRequest(targetId, dsId)
.setWeight(Action.WEIGHT_MAX + 1).build();
enableMultiAssignments();
Assertions.assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> deploymentManagement.assignDistributionSets(Collections.singletonList(weightTooLow)));
Assertions.assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
() -> deploymentManagement.assignDistributionSets(Collections.singletonList(weightTooHigh)));
final Long valideActionId1 = getFirstAssignedAction(
deploymentManagement.assignDistributionSets(Collections.singletonList(valideRequest1)).get(0)).getId();
final Long valideActionId2 = getFirstAssignedAction(
deploymentManagement.assignDistributionSets(Collections.singletonList(valideRequest2)).get(0)).getId();
assertThat(actionRepository.getById(valideActionId1).get().getWeight()).get().isEqualTo(Action.WEIGHT_MAX);
assertThat(actionRepository.getById(valideActionId2).get().getWeight()).get().isEqualTo(Action.WEIGHT_MIN);
}
/**
* test a simple deployment by calling the
* {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)}
@@ -1143,13 +1224,15 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Tests the computation of already assigned entities returned as a result of an assignment")
public void testAlreadyAssignedAndAssignedActionsInAssignmentResult(){
// create target1, distributionSet, assign ds to target1 and finish update (close all actions)
public void testAlreadyAssignedAndAssignedActionsInAssignmentResult() {
// create target1, distributionSet, assign ds to target1 and finish
// update (close all actions)
final Action action = prepareFinishedUpdate("target1", "ds", false);
final Target target2 = testdataFactory.createTarget("target2");
final Target target3 = testdataFactory.createTarget("target3");
// assign ds to target2, but don't finish update (actions should be still open)
// assign ds to target2, but don't finish update (actions should be
// still open)
assignDistributionSet(action.getDistributionSet().getId(), target2.getControllerId());
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(
@@ -1283,7 +1366,5 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
public List<Long> getUndeployedTargetIDs() {
return undeployedTargetIDs;
}
}
}

View File

@@ -17,12 +17,15 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.Condition;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
@@ -42,6 +45,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
@@ -126,7 +130,6 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
final Action rolloutCreatedAction = actionsByKnownTarget.stream()
.filter(action -> !action.getId().equals(manuallyAssignedActionId)).findAny().get();
assertThat(rolloutCreatedAction.getStatus()).isEqualTo(Status.FINISHED);
}
@Test
@@ -599,7 +602,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
final String successCondition = "50";
final String errorCondition = "80";
final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout,
amountOtherTargets, amountGroups, successCondition, errorCondition, ActionType.DOWNLOAD_ONLY);
amountOtherTargets, amountGroups, successCondition, errorCondition, ActionType.DOWNLOAD_ONLY, null);
// targets have not started
Map<TotalTargetCountStatus.Status, Long> validationMap = createInitStatusMap();
@@ -619,7 +622,8 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
changeStatusForAllRunningActions(createdRollout, Status.DOWNLOADED);
rolloutManagement.handleRollouts();
// 4 targets are ready, 2 are finished(with DOWNLOADED action status) and 2 are running
// 4 targets are ready, 2 are finished(with DOWNLOADED action status)
// and 2 are running
validationMap = createInitStatusMap();
validationMap.put(TotalTargetCountStatus.Status.SCHEDULED, 4L);
validationMap.put(TotalTargetCountStatus.Status.FINISHED, 2L);
@@ -628,7 +632,8 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
changeStatusForAllRunningActions(createdRollout, Status.DOWNLOADED);
rolloutManagement.handleRollouts();
// 2 targets are ready, 4 are finished(with DOWNLOADED action status) and 2 are running
// 2 targets are ready, 4 are finished(with DOWNLOADED action status)
// and 2 are running
validationMap = createInitStatusMap();
validationMap.put(TotalTargetCountStatus.Status.SCHEDULED, 2L);
validationMap.put(TotalTargetCountStatus.Status.FINISHED, 4L);
@@ -637,7 +642,8 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
changeStatusForAllRunningActions(createdRollout, Status.DOWNLOADED);
rolloutManagement.handleRollouts();
// 0 targets are ready, 6 are finished(with DOWNLOADED action status) and 2 are running
// 0 targets are ready, 6 are finished(with DOWNLOADED action status)
// and 2 are running
validationMap = createInitStatusMap();
validationMap.put(TotalTargetCountStatus.Status.FINISHED, 6L);
validationMap.put(TotalTargetCountStatus.Status.RUNNING, 2L);
@@ -645,7 +651,8 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
changeStatusForAllRunningActions(createdRollout, Status.FINISHED);
rolloutManagement.handleRollouts();
// 0 targets are ready, 6 are finished(with DOWNLOADED action status), 2 are finished and 0 are running
// 0 targets are ready, 6 are finished(with DOWNLOADED action status), 2
// are finished and 0 are running
validationMap = createInitStatusMap();
validationMap.put(TotalTargetCountStatus.Status.FINISHED, 8L);
validateRolloutActionStatus(createdRollout.getId(), validationMap);
@@ -1745,14 +1752,80 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
// verify that all running actions keep running
assertThat(actionRepository.findByRolloutIdAndStatus(PAGE, deletedRollout.getId(), Status.RUNNING)
.getNumberOfElements()).isEqualTo(2);
}
@Test
@Description("Creating a rollout without weight value when multi assignment in enabled.")
public void weightNotRequiredInMultiAssignmentMode() {
enableMultiAssignments();
createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 2, "50", "80", ActionType.FORCED, null);
}
@Test
@Description("Creating a rollout with a weight causes an error when multi assignment in disabled.")
public void weightNotAllowedWhenMultiAssignmentModeNotEnabled() {
Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class)
.isThrownBy(() -> createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 2, "50", "80",
ActionType.FORCED, 66));
}
@Test
@Description("Weight is validated and saved to the Rollout.")
public void weightValidatedAndSaved() {
final String targetPrefix = UUID.randomUUID().toString();
testdataFactory.createTargets(4, targetPrefix);
enableMultiAssignments();
Assertions.assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> createTestRolloutWithTargetsAndDistributionSet(4, 2, "50", "80",
UUID.randomUUID().toString(), UUID.randomUUID().toString(), Action.WEIGHT_MAX + 1));
Assertions.assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> createTestRolloutWithTargetsAndDistributionSet(4, 2, "50", "80",
UUID.randomUUID().toString(), UUID.randomUUID().toString(), Action.WEIGHT_MIN - 1));
final Rollout createdRollout1 = createTestRolloutWithTargetsAndDistributionSet(4, 2, "50", "80",
UUID.randomUUID().toString(), UUID.randomUUID().toString(), Action.WEIGHT_MAX);
final Rollout createdRollout2 = createTestRolloutWithTargetsAndDistributionSet(4, 2, "50", "80",
UUID.randomUUID().toString(), UUID.randomUUID().toString(), Action.WEIGHT_MIN);
assertThat(rolloutRepository.findById(createdRollout1.getId()).get().getWeight()).get()
.isEqualTo(Action.WEIGHT_MAX);
assertThat(rolloutRepository.findById(createdRollout2.getId()).get().getWeight()).get()
.isEqualTo(Action.WEIGHT_MIN);
}
@Test
@Description("A Rollout with weight creats actions with weights")
public void actionsWithWeightAreCreated() {
final int amountOfTargets = 5;
final int weight = 99;
enableMultiAssignments();
final Long rolloutId = createSimpleTestRolloutWithTargetsAndDistributionSet(amountOfTargets, 2, amountOfTargets,
"80", "50", null, weight).getId();
rolloutManagement.start(rolloutId);
rolloutManagement.handleRollouts();
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
assertThat(actions).hasSize(amountOfTargets);
assertThat(actions).allMatch(action -> action.getWeight().get() == weight);
}
@Test
@Description("Rollout can be created without weight in single assignment and be started in multi assignment")
public void createInSingleStartInMultiassigMode() {
final int amountOfTargets = 5;
final Long rolloutId = createSimpleTestRolloutWithTargetsAndDistributionSet(amountOfTargets, 2, amountOfTargets,
"80", "50", null, null).getId();
enableMultiAssignments();
rolloutManagement.start(rolloutId);
rolloutManagement.handleRollouts();
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
assertThat(actions).hasSize(amountOfTargets);
assertThat(actions).allMatch(action -> !action.getWeight().isPresent());
}
private RolloutGroupCreate generateRolloutGroup(final int index, final Integer percentage,
final String targetFilter) {
return entityFactory.rolloutGroup().create().name("Group" + index).description("Group" + index + "desc")
.targetPercentage(Float.valueOf(percentage)).targetFilterQuery(targetFilter);
}
private RolloutCreate generateTargetsAndRollout(final String rolloutName, final int amountTargetsForRollout) {
@@ -1790,27 +1863,35 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
final int amountOtherTargets, final int groupSize, final String successCondition,
final String errorCondition) {
return createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, amountOtherTargets,
groupSize, successCondition, errorCondition, ActionType.FORCED);
groupSize, successCondition, errorCondition, ActionType.FORCED, null);
}
private Rollout createSimpleTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout,
final int amountOtherTargets, final int groupSize, final String successCondition,
final String errorCondition, final ActionType actionType) {
final String errorCondition, final ActionType actionType, final Integer weight) {
final DistributionSet rolloutDS = testdataFactory.createDistributionSet("rolloutDS");
testdataFactory.createTargets(amountTargetsForRollout, "rollout-", "rollout");
testdataFactory.createTargets(amountOtherTargets, "others-", "rollout");
final String filterQuery = "controllerId==rollout-*";
return testdataFactory.createRolloutByVariables("test-rollout-name-1", "test-rollout-description-1", groupSize,
filterQuery, rolloutDS, successCondition, errorCondition, actionType);
filterQuery, rolloutDS, successCondition, errorCondition, actionType, weight);
}
private Rollout createTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout,
final int groupSize, final String successCondition, final String errorCondition, final String rolloutName,
final String targetPrefixName) {
return createTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, groupSize, successCondition,
errorCondition, rolloutName, targetPrefixName, null);
}
private Rollout createTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout,
final int groupSize, final String successCondition, final String errorCondition, final String rolloutName,
final String targetPrefixName, final Integer weight) {
final DistributionSet dsForRolloutTwo = testdataFactory.createDistributionSet("dsFor" + rolloutName);
testdataFactory.createTargets(amountTargetsForRollout, targetPrefixName + "-", targetPrefixName);
return testdataFactory.createRolloutByVariables(rolloutName, rolloutName + "description", groupSize,
"controllerId==" + targetPrefixName + "-*", dsForRolloutTwo, successCondition, errorCondition);
"controllerId==" + targetPrefixName + "-*", dsForRolloutTwo, successCondition, errorCondition,
Action.ActionType.FORCED, weight);
}
private int changeStatusForAllRunningActions(final Rollout rollout, final Status status) {

View File

@@ -20,6 +20,9 @@ import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.List;
import javax.validation.ConstraintViolationException;
import org.assertj.core.api.Assertions;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
@@ -28,8 +31,10 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryCreat
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
@@ -82,13 +87,18 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
verifyThrownExceptionBy(
() -> targetFilterQueryManagement.update(entityFactory.targetFilterQuery().update(NOT_EXIST_IDL)),
"TargetFilterQuery");
verifyThrownExceptionBy(
() -> targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), NOT_EXIST_IDL),
verifyThrownExceptionBy(() -> targetFilterQueryManagement.updateAutoAssignDS(
entityFactory.targetFilterQuery().updateAutoAssign(targetFilterQuery.getId()).ds(NOT_EXIST_IDL)),
"DistributionSet");
verifyThrownExceptionBy(() -> targetFilterQueryManagement.updateAutoAssignDS(NOT_EXIST_IDL, set.getId()),
"TargetFilterQuery");
verifyThrownExceptionBy(
() -> targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), NOT_EXIST_IDL),
() -> targetFilterQueryManagement.updateAutoAssignDS(
entityFactory.targetFilterQuery().updateAutoAssign(NOT_EXIST_IDL).ds(set.getId())),
"TargetFilterQuery");
verifyThrownExceptionBy(() -> targetFilterQueryManagement.updateAutoAssignDS(
entityFactory.targetFilterQuery().updateAutoAssign(targetFilterQuery.getId()).ds(NOT_EXIST_IDL)),
"DistributionSet");
}
@@ -208,23 +218,26 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
@Step
private void verifyAutoAssignmentWithDefaultActionType(final String filterName,
final TargetFilterQuery targetFilterQuery, final DistributionSet distributionSet) {
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), distributionSet.getId());
targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(targetFilterQuery.getId()).ds(distributionSet.getId()));
verifyAutoAssignDsAndActionType(filterName, distributionSet, ActionType.FORCED);
}
@Step
private void verifyAutoAssignmentWithSoftActionType(final String filterName,
final TargetFilterQuery targetFilterQuery, final DistributionSet distributionSet) {
targetFilterQueryManagement.updateAutoAssignDSWithActionType(targetFilterQuery.getId(), distributionSet.getId(),
ActionType.SOFT);
targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(targetFilterQuery.getId()).ds(distributionSet.getId()).actionType(ActionType.SOFT));
verifyAutoAssignDsAndActionType(filterName, distributionSet, ActionType.SOFT);
}
@Step
private void verifyAutoAssignmentWithDownloadOnlyActionType(final String filterName,
final TargetFilterQuery targetFilterQuery, final DistributionSet distributionSet) {
targetFilterQueryManagement.updateAutoAssignDSWithActionType(targetFilterQuery.getId(), distributionSet.getId(),
ActionType.DOWNLOAD_ONLY);
targetFilterQueryManagement
.updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(targetFilterQuery.getId())
.ds(distributionSet.getId()).actionType(ActionType.DOWNLOAD_ONLY));
verifyAutoAssignDsAndActionType(filterName, distributionSet, ActionType.DOWNLOAD_ONLY);
}
@@ -233,9 +246,11 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
final DistributionSet distributionSet) {
// assigning a distribution set with TIMEFORCED action is supposed to
// fail as only FORCED and SOFT action types are allowed
assertThatExceptionOfType(InvalidAutoAssignActionTypeException.class).isThrownBy(
() -> targetFilterQueryManagement.updateAutoAssignDSWithActionType(targetFilterQuery.getId(),
distributionSet.getId(), ActionType.TIMEFORCED));
assertThatExceptionOfType(InvalidAutoAssignActionTypeException.class)
.isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(
entityFactory.targetFilterQuery().updateAutoAssign(targetFilterQuery.getId())
.ds(distributionSet.getId()).actionType(ActionType.TIMEFORCED)));
}
@Step
@@ -245,8 +260,8 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
.type(testdataFactory.findOrCreateDefaultTestDsType()));
assertThatExceptionOfType(InvalidAutoAssignDistributionSetException.class)
.isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(),
incompleteDistributionSet.getId()));
.isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(targetFilterQuery.getId()).ds(incompleteDistributionSet.getId())));
}
@Step
@@ -255,8 +270,9 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
assignDistributionSet(softDeletedDs, testdataFactory.createTarget("forSoftDeletedDs"));
distributionSetManagement.delete(softDeletedDs.getId());
assertThatExceptionOfType(InvalidAutoAssignDistributionSetException.class).isThrownBy(
() -> targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), softDeletedDs.getId()));
assertThatExceptionOfType(InvalidAutoAssignDistributionSetException.class)
.isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(targetFilterQuery.getId()).ds(softDeletedDs.getId())));
}
private void verifyAutoAssignDsAndActionType(final String filterName, final DistributionSet distributionSet,
@@ -282,8 +298,10 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
// assigning a distribution set is supposed to fail as the query
// addresses too many targets
assertThatExceptionOfType(AssignmentQuotaExceededException.class).isThrownBy(() -> targetFilterQueryManagement
.updateAutoAssignDS(targetFilterQuery.getId(), distributionSet.getId()));
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(targetFilterQuery.getId()).ds(distributionSet.getId())));
}
@Test
@@ -313,7 +331,8 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), distributionSet.getId());
targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(targetFilterQuery.getId()).ds(distributionSet.getId()));
// Check if target filter query is there
TargetFilterQuery tfq = targetFilterQueryManagement.getByName(filterName).get();
@@ -340,9 +359,11 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
// later step
assignDistributionSet(distributionSet.getId(), target.getControllerId());
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement
final Long filterId = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name(filterName).query("name==PendingTargets001"))
.getId(), distributionSet.getId());
.getId();
targetFilterQueryManagement.updateAutoAssignDS(
entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(distributionSet.getId()));
// Check if target filter query is there with the distribution set
TargetFilterQuery tfq = targetFilterQueryManagement.getByName(filterName).get();
@@ -372,20 +393,18 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
final DistributionSet distributionSet2 = testdataFactory.createDistributionSet("2");
final TargetFilterQuery tfq = targetFilterQueryManagement.updateAutoAssignDSWithActionType(
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("c").query("name==x")).getId(),
distributionSet.getId(), ActionType.SOFT);
final TargetFilterQuery tfq2 = targetFilterQueryManagement.updateAutoAssignDS(
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name(filterName).query("name==z*")).getId(),
distributionSet2.getId());
final TargetFilterQuery tfq = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("c").query("name==x")
.autoAssignDistributionSet(distributionSet).autoAssignActionType(ActionType.SOFT));
final TargetFilterQuery tfq2 = targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create()
.name(filterName).query("name==z*").autoAssignDistributionSet(distributionSet2));
assertEquals(4L, targetFilterQueryManagement.count());
// check if find works
verifyFindByDistributionSetAndRsql(distributionSet, null, tfq);
targetFilterQueryManagement.updateAutoAssignDS(tfq2.getId(), distributionSet.getId());
targetFilterQueryManagement.updateAutoAssignDS(
entityFactory.targetFilterQuery().updateAutoAssign(tfq2.getId()).ds(distributionSet.getId()));
// check if find works for two
verifyFindByDistributionSetAndRsql(distributionSet, null, tfq, tfq2);
@@ -421,4 +440,73 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
verifyExpectedFilterQueriesInList(tfqList, expectedFilterQueries);
}
@Test
@Description("Creating or updating a target filter query with autoassignment and no-value weight when multi assignment in enabled.")
public void weightNotRequiredInMultiAssignmentMode() {
enableMultiAssignments();
final DistributionSet ds = testdataFactory.createDistributionSet();
final Long filterId = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("a").query("name==*")).getId();
targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("b").query("name==*").autoAssignDistributionSet(ds));
targetFilterQueryManagement
.updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(ds.getId()));
}
@Test
@Description("Creating or updating a target filter query with autoassignment with a weight causes an error when multi assignment in disabled.")
public void weightNotAllowedWhenMultiAssignmentModeNotEnabled() {
final DistributionSet ds = testdataFactory.createDistributionSet();
final Long filterId = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("a").query("name==*")).getId();
Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class)
.isThrownBy(() -> targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create()
.name("b").query("name==*").autoAssignDistributionSet(ds).autoAssignWeight(342)));
Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class)
.isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(
entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(ds.getId()).weight(343)));
}
@Test
@Description("Auto assignment can be removed from filter when multi assignment in enabled.")
public void removeDsFromFilterWhenMultiAssignmentModeNotEnabled() {
enableMultiAssignments();
final DistributionSet ds = testdataFactory.createDistributionSet();
final Long filterId = targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("a")
.query("name==*").autoAssignDistributionSet(ds).autoAssignWeight(23)).getId();
targetFilterQueryManagement
.updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(null).weight(null));
}
@Test
@Description("Weight is validated and saved to the Filter.")
public void weightValidatedAndSaved() {
enableMultiAssignments();
final DistributionSet ds = testdataFactory.createDistributionSet();
Assertions.assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
() -> targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("a")
.query("name==*").autoAssignDistributionSet(ds).autoAssignWeight(Action.WEIGHT_MAX + 1)));
final Long filterId = targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("a")
.query("name==*").autoAssignDistributionSet(ds).autoAssignWeight(Action.WEIGHT_MAX)).getId();
assertThat(targetFilterQueryManagement.get(filterId).get().getAutoAssignWeight().get())
.isEqualTo(Action.WEIGHT_MAX);
Assertions.assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(filterId).ds(ds.getId()).weight(Action.WEIGHT_MAX + 1)));
Assertions.assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(filterId).ds(ds.getId()).weight(Action.WEIGHT_MIN - 1)));
targetFilterQueryManagement.updateAutoAssignDS(
entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(ds.getId()).weight(Action.WEIGHT_MAX));
targetFilterQueryManagement.updateAutoAssignDS(
entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(ds.getId()).weight(Action.WEIGHT_MIN));
assertThat(targetFilterQueryManagement.get(filterId).get().getAutoAssignWeight().get())
.isEqualTo(Action.WEIGHT_MIN);
}
}

View File

@@ -131,7 +131,8 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
// try to find several targets with different filter settings
verifyThat1TargetHasNameAndId("targ-A-special", targSpecialName.getControllerId());
verifyThat1TargetHasAttributeValue("%c-attribute%", targAttribute.getControllerId());
verifyThat1TargetHasAttributeValue("%" + targAttributeId.getControllerId() + "%", targAttributeId.getControllerId());
verifyThat1TargetHasAttributeValue("%" + targAttributeId.getControllerId() + "%",
targAttributeId.getControllerId());
verifyThatRepositoryContains400Targets();
verifyThat200TargetsHaveTagD(targTagW, concat(targBs, targCs));
verifyThat100TargetsContainsGivenTextAndHaveTagAssigned(targTagY, targTagW, targBs);

View File

@@ -63,8 +63,8 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
// target filter query that matches all targets
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*"));
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), secondDistributionSet.getId());
targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(targetFilterQuery.getId()).ds(secondDistributionSet.getId()));
// Run the check
autoAssignChecker.check();
@@ -101,7 +101,8 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
// target filter query that matches all targets
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*"));
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), setA.getId());
targetFilterQueryManagement.updateAutoAssignDS(
entityFactory.targetFilterQuery().updateAutoAssign(targetFilterQuery.getId()).ds(setA.getId()));
final String targetDsAIdPref = "targ";
final List<Target> targets = testdataFactory.createTargets(100, targetDsAIdPref,
@@ -150,18 +151,16 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
// target filter query that matches first bunch of targets, that should
// fail
assertThatExceptionOfType(
InvalidAutoAssignDistributionSetException.class)
.isThrownBy(
() -> targetFilterQueryManagement.updateAutoAssignDS(
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create()
.name("filterA").query("id==" + targetDsFIdPref + "*")).getId(),
setF.getId()));
assertThatExceptionOfType(InvalidAutoAssignDistributionSetException.class).isThrownBy(() -> {
final Long filterId = targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("filterA").query("id==" + targetDsFIdPref + "*"))
.getId();
targetFilterQueryManagement
.updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(setF.getId()));
});
// target filter query that matches failed bunch of targets
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("filterB").query("id==" + targetDsAIdPref + "*"))
.getId(), setA.getId());
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filterB")
.query("id==" + targetDsAIdPref + "*").autoAssignDistributionSet(setA.getId()));
final List<Target> targetsF = testdataFactory.createTargets(10, targetDsFIdPref,
targetDsFIdPref.concat(" description"));
@@ -243,12 +242,10 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
final List<Target> targets = testdataFactory.createTargets(targetCount, "target" + prefix,
prefix.concat(" description"));
targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("filter" + prefix).query("id==target" + prefix + "*")
.autoAssignDistributionSet(distributionSet).autoAssignActionType(actionType));
targetFilterQueryManagement
.updateAutoAssignDSWithActionType(
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create()
.name("filter" + prefix).query("id==target" + prefix + "*")).getId(),
distributionSet.getId(), actionType);
return targets;
}
@@ -262,4 +259,38 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
assertThat(actions).allMatch(action -> action.getActionType().equals(actionType));
}
@Test
@Description("An auto assignment target filter with weight creats actions with weights")
public void actionsWithWeightAreCreated() throws Exception {
final int amountOfTargets = 5;
final DistributionSet ds = testdataFactory.createDistributionSet();
final int weight = 32;
enableMultiAssignments();
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("a").query("name==*")
.autoAssignDistributionSet(ds).autoAssignWeight(weight));
testdataFactory.createTargets(amountOfTargets);
autoAssignChecker.check();
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
assertThat(actions).hasSize(amountOfTargets);
assertThat(actions).allMatch(action -> action.getWeight().get() == weight);
}
@Test
@Description("An auto assignment target filter without weight still works after multi assignment is enabled")
public void filterWithoutWeightWorksInMultiAssignmentMode() throws Exception {
final int amountOfTargets = 5;
final DistributionSet ds = testdataFactory.createDistributionSet();
targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("a").query("name==*").autoAssignDistributionSet(ds));
enableMultiAssignments();
testdataFactory.createTargets(amountOfTargets);
autoAssignChecker.check();
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
assertThat(actions).hasSize(amountOfTargets);
assertThat(actions).allMatch(action -> !action.getWeight().isPresent());
}
}

View File

@@ -46,6 +46,7 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest {
action.setDistributionSet(dsA);
action.setTarget(target);
action.setStatus(Status.RUNNING);
action.setWeight(45);
target.addAction(action);
actionRepository.save(action);
@@ -56,6 +57,7 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest {
newAction.setActive(i % 2 == 0);
newAction.setStatus(Status.RUNNING);
newAction.setTarget(target);
newAction.setWeight(45);
actionRepository.save(newAction);
target.addAction(newAction);
}

View File

@@ -41,14 +41,10 @@ public class RSQLTargetFilterQueryFieldsTest extends AbstractJpaIntegrationTest
final DistributionSet ds1 = testdataFactory.createDistributionSet("AutoAssignedDs_1");
final DistributionSet ds2 = testdataFactory.createDistributionSet("AutoAssignedDs_2");
filter1 = targetFilterQueryManagement.updateAutoAssignDSWithActionType(
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name(filterName1).query("name==*")).getId(),
ds1.getId(), ActionType.SOFT);
filter2 = targetFilterQueryManagement.updateAutoAssignDS(
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name(filterName2).query("name==*")).getId(),
ds2.getId());
filter1 = targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name(filterName1)
.query("name==*").autoAssignDistributionSet(ds1).autoAssignActionType(ActionType.SOFT));
filter2 = targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name(filterName2)
.query("name==*").autoAssignDistributionSet(ds2));
targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name(filterName3).query("name==*"));

View File

@@ -16,9 +16,7 @@ import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -118,6 +116,8 @@ public abstract class AbstractIntegrationTest {
protected static final URI LOCALHOST = URI.create("http://127.0.0.1");
protected static final int DEFAULT_TEST_WEIGHT = 500;
/**
* Number of {@link DistributionSetType}s that exist in every test case. One
* generated by using
@@ -250,9 +250,14 @@ public abstract class AbstractIntegrationTest {
protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final List<String> controllerIds,
final ActionType actionType, final long forcedTime) {
return assignDistributionSet(dsID, controllerIds, actionType, forcedTime, null);
}
protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final List<String> controllerIds,
final ActionType actionType, final long forcedTime, final Integer weight) {
final List<DeploymentRequest> deploymentRequests = controllerIds.stream()
.map(id -> DeploymentManagement.deploymentRequest(id, dsID).setActionType(actionType)
.setForceTime(forcedTime).build())
.setForceTime(forcedTime).setWeight(weight).build())
.collect(Collectors.toList());
final List<DistributionSetAssignmentResult> results = deploymentManagement
.assignDistributionSets(deploymentRequests);
@@ -266,7 +271,13 @@ public abstract class AbstractIntegrationTest {
return assignDistributionSet(ds.getId(), targetIds, ActionType.FORCED);
}
private DistributionSetAssignmentResult makeAssignment(final DeploymentRequest request) {
protected DistributionSetAssignmentResult assignDistributionSet(final Long dsId, final List<String> targetIds,
final int weight) {
return assignDistributionSet(dsId, targetIds, ActionType.FORCED, RepositoryModelConstants.NO_FORCE_TIME,
weight);
}
protected DistributionSetAssignmentResult makeAssignment(final DeploymentRequest request) {
final List<DistributionSetAssignmentResult> results = deploymentManagement
.assignDistributionSets(Collections.singletonList(request));
assertThat(results).hasSize(1);
@@ -302,35 +313,21 @@ public abstract class AbstractIntegrationTest {
protected DistributionSetAssignmentResult assignDistributionSetWithMaintenanceWindow(final long dsID,
final String controllerId, final String maintenanceWindowSchedule, final String maintenanceWindowDuration,
final String maintenanceWindowTimeZone) {
return makeAssignment(DeploymentManagement.deploymentRequest(controllerId, dsID)
.setMaintenance(maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone)
.build());
}
protected DistributionSetAssignmentResult assignDistributionSetWithMaintenanceWindow(final long dsID,
final String controllerId, final ActionType type, final String maintenanceWindowSchedule,
final String maintenanceWindowDuration,
final String maintenanceWindowTimeZone) {
return makeAssignment(DeploymentManagement.deploymentRequest(controllerId, dsID).setActionType(type)
.setMaintenance(maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone)
.build());
}
protected List<DeploymentRequest> createAssignmentRequests(final Collection<DistributionSet> distributionSets,
final Collection<Target> targets) {
final List<DeploymentRequest> deploymentRequests = new ArrayList<>();
distributionSets.forEach(ds -> targets.forEach(
target -> deploymentRequests
.add(DeploymentManagement.deploymentRequest(target.getControllerId(), ds.getId()).build()))
);
return deploymentRequests;
}
protected DistributionSetAssignmentResult assignDistributionSet(final DistributionSet pset, final Target target) {
return assignDistributionSet(pset, Arrays.asList(target));
}
protected DistributionSetAssignmentResult assignDistributionSet(final long dsId, final String targetId,
final int weight) {
return assignDistributionSet(dsId, Collections.singletonList(targetId), weight);
}
protected void enableMultiAssignments() {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, true);
}
@@ -477,12 +474,14 @@ public abstract class AbstractIntegrationTest {
return randomStringBuilder.toString();
}
protected static Action getFirstAssignedAction(final DistributionSetAssignmentResult distributionSetAssignmentResult) {
protected static Action getFirstAssignedAction(
final DistributionSetAssignmentResult distributionSetAssignmentResult) {
return distributionSetAssignmentResult.getAssignedEntity().stream().findFirst()
.orElseThrow(() -> new IllegalStateException("expected one assigned action, found none"));
}
protected static Long getFirstAssignedActionId(final DistributionSetAssignmentResult distributionSetAssignmentResult) {
protected static Long getFirstAssignedActionId(
final DistributionSetAssignmentResult distributionSetAssignmentResult) {
return getFirstAssignedAction(distributionSetAssignmentResult).getId();
}

View File

@@ -1,6 +1,6 @@
/**
* 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
@@ -19,6 +19,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -117,8 +118,8 @@ public class TestdataFactory {
public static final String SM_TYPE_RT = "runtime";
/**
* Key of test "application" {@link SoftwareModuleType} : optional software in
* {@link #DS_TYPE_DEFAULT}.
* Key of test "application" {@link SoftwareModuleType} : optional software
* in {@link #DS_TYPE_DEFAULT}.
*/
public static final String SM_TYPE_APP = "application";
@@ -160,13 +161,14 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>.
*
*
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
*
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final String prefix) {
@@ -175,24 +177,25 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>.
*
*
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet() {
return createDistributionSet("", DEFAULT_VERSION, false);
return createDistributionSet(UUID.randomUUID().toString(), DEFAULT_VERSION, false);
}
/**
* Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>.
*
*
* @param modules
* of {@link DistributionSet#getModules()}
*
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final Collection<SoftwareModule> modules) {
@@ -201,15 +204,16 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>.
*
*
* @param modules
* of {@link DistributionSet#getModules()}
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
*
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final Collection<SoftwareModule> modules, final String prefix) {
@@ -218,14 +222,15 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION}.
*
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION}.
*
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
* @param isRequiredMigrationStep
* for {@link DistributionSet#isRequiredMigrationStep()}
*
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final String prefix, final boolean isRequiredMigrationStep) {
@@ -234,15 +239,16 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and
* {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>.
*
*
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
* @param tags
* {@link DistributionSet#getTags()}
*
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final String prefix, final Collection<DistributionSetTag> tags) {
@@ -251,17 +257,19 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* {@link #SM_TYPE_APP}.
*
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
* , {@link #SM_TYPE_APP}.
*
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
* @param version
* {@link DistributionSet#getVersion()} and
* {@link SoftwareModule#getVersion()} extended by a random number.
* {@link SoftwareModule#getVersion()} extended by a random
* number.
* @param isRequiredMigrationStep
* for {@link DistributionSet#isRequiredMigrationStep()}
*
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final String prefix, final String version,
@@ -295,7 +303,7 @@ public class TestdataFactory {
* {@link SoftwareModuleMetadata#isTargetVisible()} and
* {@link #INVISIBLE_SM_MD_KEY}, {@link #INVISIBLE_SM_MD_VALUE} without
* {@link SoftwareModuleMetadata#isTargetVisible()}
*
*
* @param set
* to add metadata to
*/
@@ -313,17 +321,19 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository.
*
*
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
* @param version
* {@link DistributionSet#getVersion()} and
* {@link SoftwareModule#getVersion()} extended by a random number.
* {@link SoftwareModule#getVersion()} extended by a random
* number.
* @param isRequiredMigrationStep
* for {@link DistributionSet#isRequiredMigrationStep()}
* @param modules
* for {@link DistributionSet#getModules()}
*
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final String prefix, final String version,
@@ -338,9 +348,9 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet} in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* {@link #SM_TYPE_APP}.
*
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
* , {@link #SM_TYPE_APP}.
*
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
@@ -350,6 +360,7 @@ public class TestdataFactory {
* number.updat
* @param tags
* {@link DistributionSet#getTags()}
*
* @return {@link DistributionSet} entity.
*/
public DistributionSet createDistributionSet(final String prefix, final String version,
@@ -365,13 +376,14 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet}s in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an iterative
* number and {@link DistributionSet#isRequiredMigrationStep()}
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an
* iterative number and {@link DistributionSet#isRequiredMigrationStep()}
* <code>false</code>.
*
*
* @param number
* of {@link DistributionSet}s to create
*
* @return {@link List} of {@link DistributionSet} entities
*/
public List<DistributionSet> createDistributionSets(final int number) {
@@ -380,8 +392,9 @@ public class TestdataFactory {
}
/**
* Create a list of {@link DistributionSet}s without modules, i.e. incomplete.
*
* Create a list of {@link DistributionSet}s without modules, i.e.
* incomplete.
*
* @param number
* of {@link DistributionSet}s to create
* @return {@link List} of {@link DistributionSet} entities
@@ -400,16 +413,17 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet}s in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an iterative
* number and {@link DistributionSet#isRequiredMigrationStep()}
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an
* iterative number and {@link DistributionSet#isRequiredMigrationStep()}
* <code>false</code>.
*
*
* @param prefix
* for {@link SoftwareModule}s and {@link DistributionSet}s name,
* vendor and description.
* @param number
* of {@link DistributionSet}s to create
*
* @return {@link List} of {@link DistributionSet} entities
*/
public List<DistributionSet> createDistributionSets(final String prefix, final int number) {
@@ -426,11 +440,12 @@ public class TestdataFactory {
* Creates {@link DistributionSet}s in repository with
* {@link #DEFAULT_DESCRIPTION} and
* {@link DistributionSet#isRequiredMigrationStep()} <code>false</code>.
*
*
* @param name
* {@link DistributionSet#getName()}
* @param version
* {@link DistributionSet#getVersion()}
*
* @return {@link DistributionSet} entity
*/
public DistributionSet createDistributionSetWithNoSoftwareModules(final String name, final String version) {
@@ -440,11 +455,12 @@ public class TestdataFactory {
}
/**
* Creates {@link Artifact}s for given {@link SoftwareModule} with a small text
* payload.
*
* Creates {@link Artifact}s for given {@link SoftwareModule} with a small
* text payload.
*
* @param moduleId
* the {@link Artifact}s belong to.
*
* @return {@link Artifact} entity.
*/
public List<Artifact> createArtifacts(final Long moduleId) {
@@ -458,15 +474,18 @@ public class TestdataFactory {
}
/**
* Create an {@link Artifact} for given {@link SoftwareModule} with a small text
* payload.
*
* Create an {@link Artifact} for given {@link SoftwareModule} with a small
* text payload.
*
* @param artifactData
* the {@link Artifact} Inputstream
*
* @param moduleId
* the {@link Artifact} belongs to
*
* @param filename
* that was provided during upload.
*
* @return {@link Artifact} entity.
*/
public Artifact createArtifact(final String artifactData, final Long moduleId, final String filename) {
@@ -476,17 +495,21 @@ public class TestdataFactory {
}
/**
* Create an {@link Artifact} for given {@link SoftwareModule} with a small text
* payload.
* Create an {@link Artifact} for given {@link SoftwareModule} with a small
* text payload.
*
* @param artifactData
* the {@link Artifact} Inputstream
*
* @param moduleId
* the {@link Artifact} belongs to
*
* @param filename
* that was provided during upload.
*
* @param fileSize
* the file size
*
* @return {@link Artifact} entity.
*/
public Artifact createArtifact(final byte[] artifactData, final Long moduleId, final String filename,
@@ -497,11 +520,12 @@ public class TestdataFactory {
/**
* Creates {@link SoftwareModule} with {@link #DEFAULT_VENDOR} and
* {@link #DEFAULT_VERSION} and random generated {@link Target#getDescription()}
* in the repository.
*
* {@link #DEFAULT_VERSION} and random generated
* {@link Target#getDescription()} in the repository.
*
* @param typeKey
* of the {@link SoftwareModuleType}
*
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModule(final String typeKey) {
@@ -509,10 +533,12 @@ public class TestdataFactory {
}
/**
* Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_APP_KEY}
* with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random
* generated {@link Target#getDescription()} in the repository.
*
* Creates {@link SoftwareModule} of type
* {@value Constants#SMT_DEFAULT_APP_KEY} with {@link #DEFAULT_VENDOR} and
* {@link #DEFAULT_VERSION} and random generated
* {@link Target#getDescription()} in the repository.
*
*
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModuleApp() {
@@ -520,12 +546,15 @@ public class TestdataFactory {
}
/**
* Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_APP_KEY}
* with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random
* generated {@link Target#getDescription()} in the repository.
*
* Creates {@link SoftwareModule} of type
* {@value Constants#SMT_DEFAULT_APP_KEY} with {@link #DEFAULT_VENDOR} and
* {@link #DEFAULT_VERSION} and random generated
* {@link Target#getDescription()} in the repository.
*
* @param prefix
* added to name and version
*
*
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModuleApp(final String prefix) {
@@ -533,10 +562,12 @@ public class TestdataFactory {
}
/**
* Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_OS_KEY}
* with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random
* generated {@link Target#getDescription()} in the repository.
*
* Creates {@link SoftwareModule} of type
* {@value Constants#SMT_DEFAULT_OS_KEY} with {@link #DEFAULT_VENDOR} and
* {@link #DEFAULT_VERSION} and random generated
* {@link Target#getDescription()} in the repository.
*
*
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModuleOs() {
@@ -544,12 +575,15 @@ public class TestdataFactory {
}
/**
* Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_OS_KEY}
* with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random
* generated {@link Target#getDescription()} in the repository.
*
* Creates {@link SoftwareModule} of type
* {@value Constants#SMT_DEFAULT_OS_KEY} with {@link #DEFAULT_VENDOR} and
* {@link #DEFAULT_VERSION} and random generated
* {@link Target#getDescription()} in the repository.
*
* @param prefix
* added to name and version
*
*
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModuleOs(final String prefix) {
@@ -558,13 +592,14 @@ public class TestdataFactory {
/**
* Creates {@link SoftwareModule} with {@link #DEFAULT_VENDOR} and
* {@link #DEFAULT_VERSION} and random generated {@link Target#getDescription()}
* in the repository.
*
* {@link #DEFAULT_VERSION} and random generated
* {@link Target#getDescription()} in the repository.
*
* @param typeKey
* of the {@link SoftwareModuleType}
* @param prefix
* added to name and version
*
* @return persisted {@link SoftwareModule}.
*/
public SoftwareModule createSoftwareModule(final String typeKey, final String prefix) {
@@ -603,7 +638,7 @@ public class TestdataFactory {
return target;
}
private void assertTargetProperlyCreated(Target target) {
private void assertTargetProperlyCreated(final Target target) {
assertThat(target.getCreatedBy()).isNotNull();
assertThat(target.getCreatedAt()).isNotNull();
assertThat(target.getLastModifiedBy()).isNotNull();
@@ -614,15 +649,16 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSet}s in repository including three
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} ,
* {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an iterative
* number and {@link DistributionSet#isRequiredMigrationStep()}
* {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
* , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an
* iterative number and {@link DistributionSet#isRequiredMigrationStep()}
* <code>false</code>.
*
* In addition it updates the created {@link DistributionSet}s and
* {@link SoftwareModule}s to ensure that {@link BaseEntity#getLastModifiedAt()}
* and {@link BaseEntity#getLastModifiedBy()} is filled.
*
* {@link SoftwareModule}s to ensure that
* {@link BaseEntity#getLastModifiedAt()} and
* {@link BaseEntity#getLastModifiedBy()} is filled.
*
* @return persisted {@link DistributionSet}.
*/
public DistributionSet createUpdatedDistributionSet() {
@@ -639,8 +675,8 @@ public class TestdataFactory {
/**
* @return {@link DistributionSetType} with key {@link #DS_TYPE_DEFAULT} and
* {@link SoftwareModuleType}s {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT}
* , {@link #SM_TYPE_APP}.
* {@link SoftwareModuleType}s {@link #SM_TYPE_OS},
* {@link #SM_TYPE_RT} , {@link #SM_TYPE_APP}.
*/
public DistributionSetType findOrCreateDefaultTestDsType() {
final List<SoftwareModuleType> mand = new ArrayList<>();
@@ -656,11 +692,12 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSetType} in repository.
*
*
* @param dsTypeKey
* {@link DistributionSetType#getKey()}
* @param dsTypeName
* {@link DistributionSetType#getName()}
*
* @return persisted {@link DistributionSetType}
*/
public DistributionSetType findOrCreateDistributionSetType(final String dsTypeKey, final String dsTypeName) {
@@ -672,7 +709,7 @@ public class TestdataFactory {
/**
* Finds {@link DistributionSetType} in repository with given
* {@link DistributionSetType#getKey()} or creates if it does not exist yet.
*
*
* @param dsTypeKey
* {@link DistributionSetType#getKey()}
* @param dsTypeName
@@ -681,6 +718,7 @@ public class TestdataFactory {
* {@link DistributionSetType#getMandatoryModuleTypes()}
* @param optional
* {@link DistributionSetType#getOptionalModuleTypes()}
*
* @return persisted {@link DistributionSetType}
*/
public DistributionSetType findOrCreateDistributionSetType(final String dsTypeKey, final String dsTypeName,
@@ -694,11 +732,12 @@ public class TestdataFactory {
/**
* Finds {@link SoftwareModuleType} in repository with given
* {@link SoftwareModuleType#getKey()} or creates if it does not exist yet with
* {@link SoftwareModuleType#getMaxAssignments()} = 1.
*
* {@link SoftwareModuleType#getKey()} or creates if it does not exist yet
* with {@link SoftwareModuleType#getMaxAssignments()} = 1.
*
* @param key
* {@link SoftwareModuleType#getKey()}
*
* @return persisted {@link SoftwareModuleType}
*/
public SoftwareModuleType findOrCreateSoftwareModuleType(final String key) {
@@ -708,11 +747,12 @@ public class TestdataFactory {
/**
* Finds {@link SoftwareModuleType} in repository with given
* {@link SoftwareModuleType#getKey()} or creates if it does not exist yet.
*
*
* @param key
* {@link SoftwareModuleType#getKey()}
* @param maxAssignments
* {@link SoftwareModuleType#getMaxAssignments()}
*
* @return persisted {@link SoftwareModuleType}
*/
public SoftwareModuleType findOrCreateSoftwareModuleType(final String key, final int maxAssignments) {
@@ -732,6 +772,7 @@ public class TestdataFactory {
* {@link DistributionSet#getType()}
* @param modules
* {@link DistributionSet#getModules()}
*
* @return the created {@link DistributionSet}
*/
public DistributionSet createDistributionSet(final String name, final String version,
@@ -754,6 +795,7 @@ public class TestdataFactory {
* {@link DistributionSet#getModules()}
* @param requiredMigrationStep
* {@link DistributionSet#isRequiredMigrationStep()}
*
* @return the created {@link DistributionSet}
*/
public DistributionSet generateDistributionSet(final String name, final String version,
@@ -775,6 +817,7 @@ public class TestdataFactory {
* {@link DistributionSet#getType()}
* @param modules
* {@link DistributionSet#getModules()}
*
* @return the created {@link DistributionSet}
*/
public DistributionSet generateDistributionSet(final String name, final String version,
@@ -787,6 +830,7 @@ public class TestdataFactory {
*
* @param name
* {@link DistributionSet#getName()}
*
* @return the generated {@link DistributionSet}
*/
public DistributionSet generateDistributionSet(final String name) {
@@ -795,11 +839,13 @@ public class TestdataFactory {
}
/**
* Creates {@link Target}s in repository and with {@link #DEFAULT_CONTROLLER_ID}
* as prefix for {@link Target#getControllerId()}.
*
* Creates {@link Target}s in repository and with
* {@link #DEFAULT_CONTROLLER_ID} as prefix for
* {@link Target#getControllerId()}.
*
* @param number
* of {@link Target}s to create
*
* @return {@link List} of {@link Target} entities
*/
public List<Target> createTargets(final int number) {
@@ -815,7 +861,7 @@ public class TestdataFactory {
/**
* Builds {@link Target} objects with given prefix for
* {@link Target#getControllerId()} followed by a number suffix.
*
*
* @param start
* value for the controllerId suffix
* @param numberOfTargets
@@ -835,8 +881,9 @@ public class TestdataFactory {
/**
* Builds {@link Target} objects with given prefix for
* {@link Target#getControllerId()} followed by a number suffix starting with 0.
*
* {@link Target#getControllerId()} followed by a number suffix starting
* with 0.
*
* @param numberOfTargets
* of {@link Target}s to generate
* @param controllerIdPrefix
@@ -926,9 +973,10 @@ public class TestdataFactory {
/**
* Creates {@link DistributionSetTag}s in repository.
*
*
* @param number
* of {@link DistributionSetTag}s
*
* @return the persisted {@link DistributionSetTag}s
*/
public List<DistributionSetTag> createDistributionSetTags(final int number) {
@@ -950,14 +998,16 @@ public class TestdataFactory {
}
/**
* Append {@link ActionStatus} to all {@link Action}s of given {@link Target}s.
*
* Append {@link ActionStatus} to all {@link Action}s of given
* {@link Target}s.
*
* @param targets
* to add {@link ActionStatus}
* @param status
* to add
* @param message
* to add
*
* @return updated {@link Action}.
*/
public List<Action> sendUpdateActionStatusToTargets(final Collection<Target> targets, final Status status,
@@ -966,14 +1016,16 @@ public class TestdataFactory {
}
/**
* Append {@link ActionStatus} to all {@link Action}s of given {@link Target}s.
*
* Append {@link ActionStatus} to all {@link Action}s of given
* {@link Target}s.
*
* @param targets
* to add {@link ActionStatus}
* @param status
* to add
* @param msgs
* to add
*
* @return updated {@link Action}.
*/
public List<Action> sendUpdateActionStatusToTargets(final Collection<Target> targets, final Status status,
@@ -991,7 +1043,7 @@ public class TestdataFactory {
/**
* Creates rollout based on given parameters.
*
*
* @param rolloutName
* of the {@link Rollout}
* @param rolloutDescription
@@ -1012,7 +1064,7 @@ public class TestdataFactory {
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
final String successCondition, final String errorCondition) {
return createRolloutByVariables(rolloutName, rolloutDescription, groupSize, filterQuery, distributionSet,
successCondition, errorCondition, Action.ActionType.FORCED);
successCondition, errorCondition, Action.ActionType.FORCED, null);
}
/**
@@ -1034,11 +1086,14 @@ public class TestdataFactory {
* to switch to next group
* @param actionType
* the type of the Rollout
* @param weight
* weight of the Rollout
* @return created {@link Rollout}
*/
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
final String successCondition, final String errorCondition, final Action.ActionType actionType) {
final String successCondition, final String errorCondition, final Action.ActionType actionType,
final Integer weight) {
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, successCondition)
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition)
@@ -1046,7 +1101,7 @@ public class TestdataFactory {
final Rollout rollout = rolloutManagement.create(
entityFactory.rollout().create().name(rolloutName).description(rolloutDescription)
.targetFilterQuery(filterQuery).set(distributionSet).actionType(actionType),
.targetFilterQuery(filterQuery).set(distributionSet).actionType(actionType).weight(weight),
groupSize, conditions);
// Run here, because Scheduler is disabled during tests
@@ -1058,10 +1113,10 @@ public class TestdataFactory {
/**
* Create {@link Rollout} with a new {@link DistributionSet} and
* {@link Target}s.
*
*
* @param prefix
* for rollouts name, description, {@link Target#getControllerId()}
* filter
* for rollouts name, description,
* {@link Target#getControllerId()} filter
* @return created {@link Rollout}
*/
public Rollout createRollout(final String prefix) {
@@ -1071,12 +1126,12 @@ public class TestdataFactory {
}
/**
* Create the soft deleted {@link Rollout} with a new {@link DistributionSet}
* and {@link Target}s.
*
* Create the soft deleted {@link Rollout} with a new
* {@link DistributionSet} and {@link Target}s.
*
* @param prefix
* for rollouts name, description, {@link Target#getControllerId()}
* filter
* for rollouts name, description,
* {@link Target#getControllerId()} filter
* @return created {@link Rollout}
*/
public Rollout createSoftDeletedRollout(final String prefix) {