Semi automatic Rollouts with fine groups definition (#337)

* Rollout Mgmt API accepts now extended Group definition. Filling Reollout Groups with Targets is now a scheduled task.

Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com>

* Fire RolloutGroupCreated event and fix db migration.

Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com>

* Fill groups now excludes targets in own group

Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com>

* Starting of Rollouts as scheduled task

Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com>

* Finished implementation of new Rollout starting proccess

Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com>

* Reset last check on status change and fixed unused imports

Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com>

* Code quality improvements

Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com>

* Reworked start of scheduled Actions. Improved code quality.

Signed-off-by: Dominik Herbst <dominik.herbst@bosch-si.com>
This commit is contained in:
Dominik Herbst
2016-11-16 09:26:50 +01:00
committed by Michael Hirsch
parent 66b6983406
commit b6834e9ee2
37 changed files with 2009 additions and 457 deletions

View File

@@ -276,25 +276,6 @@ public interface DeploymentManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
List<Action> findActionsByRolloutAndStatus(@NotNull Rollout rollout, @NotNull Action.Status actionStatus);
/**
* Retrieving all actions referring to a given rollout with a specific
* action as parent reference and a specific status.
*
* Finding all actions of a specific rolloutgroup parent relation.
*
* @param rollout
* the rollout the actions belong to
* @param rolloutGroupParent
* the parent rollout group the actions should reference
* @param actionStatus
* the status the actions have
* @return the actions referring a specific rollout and a specific parent
* rollout group in a specific status
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
List<Long> findActionsByRolloutGroupParentAndStatus(@NotNull Rollout rollout,
@NotNull RolloutGroup rolloutGroupParent, @NotNull Action.Status actionStatus);
/**
* Retrieves all {@link Action}s of a specific target.
*
@@ -513,6 +494,19 @@ public interface DeploymentManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Action startScheduledAction(@NotNull Long actionId);
/**
* Starts all scheduled actions of an RolloutGroup parent.
*
* @param rollout
* the rollout the actions belong to
* @param rolloutGroupParent
* the parent rollout group the actions should reference. null
* references the first group
* @return the amount of started actions
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
long startScheduledActionsByRolloutGroupParent(@NotNull Rollout rollout, RolloutGroup rolloutGroupParent);
/**
* All {@link ActionStatus} entries in the repository.
*

View File

@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEvent;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
@@ -26,6 +25,8 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.security.access.prepost.PreAuthorize;
import java.util.List;
/**
* RolloutManagement to control rollouts e.g. like creating, starting, resuming
* and pausing rollouts. This service secures all the functionality based on the
@@ -64,6 +65,30 @@ public interface RolloutManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void checkRunningRollouts(long delayBetweenChecks);
/**
* Checking Rollouts that are currently being created with asynchronous
* assignment of targets to the Rollout Groups.
*
* @param delayBetweenChecks
* the time in milliseconds of the delay between the further and
* this check. This check is only applied if the last check is
* less than (lastcheck-delay).
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void checkCreatingRollouts(long delayBetweenChecks);
/**
* Checking Rollouts that are currently being started with asynchronous
* creation of actions to the targets of a group.
*
* @param delayBetweenChecks
* the time in milliseconds of the delay between the further and
* this check. This check is only applied if the last check is
* less than (lastcheck-delay).
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void checkStartingRollouts(long delayBetweenChecks);
/**
* Counts all {@link Rollout}s in the repository.
*
@@ -85,14 +110,17 @@ public interface RolloutManagement {
/**
* Persists a new rollout entity. The filter within the
* {@link Rollout#getTargetFilterQuery()} is used to retrieve the targets
* which are effected by this rollout to create. The targets will then be
* split up into groups. The size of the groups can be defined in the
* {@code groupSize} parameter.
* which are effected by this rollout to create. The amount of groups will
* be defined as equally sized.
*
* The rollout is not started. Only the preparation of the rollout is done,
* persisting and creating all the necessary groups. The Rollout and the
* groups are persisted in {@link RolloutStatus#READY} and
* {@link RolloutGroupStatus#READY} so they can be started
* creating and persisting all the necessary groups. The Rollout and the
* groups are persisted in {@link RolloutStatus#CREATING} and
* {@link RolloutGroupStatus#CREATING}.
*
* The RolloutScheduler will start to assign targets to the groups. Once all
* targets have been assigned to the groups, the rollout status is changed
* to {@link RolloutStatus#READY} so it can be started with
* {@link #startRollout(Rollout)}.
*
* @param rollout
@@ -112,40 +140,52 @@ public interface RolloutManagement {
/**
* Persists a new rollout entity. The filter within the
* {@link Rollout#getTargetFilterQuery()} is used to retrieve the targets
* which are effected by this rollout to create. The creation of the rollout
* will be done synchronously and will be returned. The targets will then be
* split up into groups. The size of the groups can be defined in the
* {@code groupSize} parameter.
*
* The creation of the rollout groups is executed asynchronously due it
* might take some time to split up the targets into groups. The creation of
* the {@link RolloutGroup} is published as event
* {@link RolloutGroupCreatedEvent}.
*
* The rollout is in status {@link RolloutStatus#CREATING} until all rollout
* groups has been created and the targets are split up, then the rollout
* will change the status to {@link RolloutStatus#READY}.
* {@link Rollout#getTargetFilterQuery()} is used to filter the targets
* which are affected by this rollout. The given groups will be used to
* create the groups.
*
* The rollout is not started. Only the preparation of the rollout is done,
* persisting and creating all the necessary groups. The Rollout and the
* groups are persisted in {@link RolloutStatus#READY} and
* {@link RolloutGroupStatus#READY} so they can be started
* creating and persisting all the necessary groups. The Rollout and the
* groups are persisted in {@link RolloutStatus#CREATING} and
* {@link RolloutGroupStatus#CREATING}.
*
* The RolloutScheduler will start to assign targets to the groups. Once all
* targets have been assigned to the groups, the rollout status is changed
* to {@link RolloutStatus#READY} so it can be started with
* {@link #startRollout(Rollout)}.
*
* @param rollout
* the rollout to be created
* @param amountGroup
* the number of groups should be created for the rollout and
* split up the targets
* the rollout entity to create
* @param groups
* optional definition of groups
* @param conditions
* the rolloutgroup conditions and actions which should be
* applied for each {@link RolloutGroup}
* @return the created rollout entity in state
* {@link RolloutStatus#CREATING}
* the rollout group conditions and actions which should be
* applied for each {@link RolloutGroup} if not defined by the
* RolloutGroup itself
* @return the persisted rollout.
*
* @throws IllegalArgumentException
* in case the given groupSize is zero or lower.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
Rollout createRolloutAsync(@NotNull Rollout rollout, int amountGroup, @NotNull RolloutGroupConditions conditions);
Rollout createRollout(@NotNull Rollout rollout, @NotNull List<RolloutGroup> groups, RolloutGroupConditions conditions);
/**
* Can be called on a Rollout in {@link RolloutStatus#CREATING} to
* automatically fill it with targets.
*
* Works through all Rollout groups in {@link RolloutGroupStatus#CREATING}
* and fills them with remaining targets until the supposed amount of
* targets for the group is reached. Targets are added to a group when they
* match the overall {@link Rollout#getTargetFilterQuery()} and the
* {@link RolloutGroup#getTargetFilterQuery()} and not more than
* {@link RolloutGroup#getTargetPercentage()} are assigned to the group.
*
* @param rollout
* the rollout
*/
void fillRolloutGroupsWithTargets(final Rollout rollout);
/**
* Retrieves all rollouts.
@@ -284,12 +324,10 @@ public interface RolloutManagement {
/**
* Starts a rollout which has been created. The rollout must be in
* {@link RolloutStatus#READY} state. The according actions will be created
* for each affected target in the rollout. The actions of the first group
* will be started immediately {@link RolloutGroupStatus#RUNNING} as the
* other groups will be {@link RolloutGroupStatus#SCHEDULED} state.
*
* The rollout itself will be then also in {@link RolloutStatus#RUNNING}.
* {@link RolloutStatus#READY} state. The Rollout will be set into the
* {@link RolloutStatus#STARTING} state. The RolloutScheduler will ensure
* all actions are created and the first group is started. The rollout
* itself will be then also in {@link RolloutStatus#RUNNING}.
*
* @param rollout
* the rollout to be started
@@ -303,28 +341,6 @@ public interface RolloutManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
Rollout startRollout(@NotNull Rollout rollout);
/**
* Starts a rollout asynchronously which has been created. The rollout must
* be in {@link RolloutStatus#READY} state. The according actions will be
* created asynchronously for each affected target in the rollout. The
* actions of the first group will be started immediately
* {@link RolloutGroupStatus#RUNNING} as the other groups will be
* {@link RolloutGroupStatus#SCHEDULED} state.
*
* The rollout itself will be then also in {@link RolloutStatus#RUNNING}.
*
* @param rollout
* the rollout to be started
*
* @return the started rollout
*
* @throws RolloutIllegalStateException
* if given rollout is not in {@link RolloutStatus#READY}. Only
* ready rollouts can be started.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
Rollout startRolloutAsync(@NotNull Rollout rollout);
/**
* Update rollout details.
*

View File

@@ -19,6 +19,7 @@ import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
@@ -268,7 +269,7 @@ public interface TargetManagement {
* id of the {@link DistributionSet}
* @param targetFilterQuery
* {@link TargetFilterQuery}
* @return the found {@link TargetIdName}s
* @return a page of the found {@link Target}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<Target> findAllTargetsByTargetFilterQueryAndNonDS(@NotNull Pageable pageRequest, Long distributionSetId,
@@ -283,11 +284,54 @@ public interface TargetManagement {
* id of the {@link DistributionSet}
* @param targetFilterQuery
* {@link TargetFilterQuery}
* @return the found {@link TargetIdName}s
* @return the count of found {@link Target}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Long countTargetsByTargetFilterQueryAndNonDS(Long distributionSetId, @NotNull TargetFilterQuery targetFilterQuery);
/**
* Finds all targets for all the given parameter {@link TargetFilterQuery}
* and that are not assigned to one of the {@link RolloutGroup}s
*
* @param pageRequest
* the pageRequest to enhance the query for paging and sorting
* @param groups
* the list of {@link RolloutGroup}s
* @param targetFilterQuery
* RSQL filter
* @return a page of the found {@link Target}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<Target> findAllTargetsByTargetFilterQueryAndNotInRolloutGroups(@NotNull Pageable pageRequest,
List<RolloutGroup> groups, @NotNull String targetFilterQuery);
/**
* Counts all targets for all the given parameter {@link TargetFilterQuery}
* and that are not assigned to one of the {@link RolloutGroup}s
*
* @param groups
* the list of {@link RolloutGroup}s
* @param targetFilterQuery
* RSQL filter
* @return count of the found {@link Target}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Long countAllTargetsByTargetFilterQueryAndNotInRolloutGroups(List<RolloutGroup> groups,
@NotNull String targetFilterQuery);
/**
* Finds all targets of the provided {@link RolloutGroup} that have no
* Action for the RolloutGroup.
*
* @param pageRequest
* the pageRequest to enhance the query for paging and sorting
* @param group
* the {@link RolloutGroup}
* @return the found {@link Target}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Page<Target> findAllTargetsInRolloutGroupWithoutAction(@NotNull Pageable pageRequest, @NotNull RolloutGroup group);
/**
* retrieves {@link Target}s by the assigned {@link DistributionSet} without
* details, i.e. NO {@link Target#getTags()} and {@link Target#getActions()}

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.exception;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError;
/**
* the {@link RolloutVerificationException} is thrown when a rollout or
* its groups get created or modified with a configuration that is
* not valid or can't be verified
*
*/
public class RolloutVerificationException extends AbstractServerRtException {
private static final long serialVersionUID = 1L;
private static final SpServerError THIS_ERROR = SpServerError.SP_ROLLOUT_VERIFICATION_FAILED;
/**
* Default constructor.
*/
public RolloutVerificationException() {
super(THIS_ERROR);
}
/**
* Parameterized constructor.
*
* @param cause
* of the exception
*/
public RolloutVerificationException(final Throwable cause) {
super(THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
* @param cause
* of the exception
*/
public RolloutVerificationException(final String message, final Throwable cause) {
super(message, THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
*/
public RolloutVerificationException(final String message) {
super(message, THIS_ERROR);
}
}

View File

@@ -146,13 +146,17 @@ public interface Rollout extends NamedEntity {
/**
* Rollout could not be created due to errors, might be a database
* problem during asynchronous creating.
* @deprecated legacy status is not used anymore
*/
@Deprecated
ERROR_CREATING,
/**
* Rollout could not be started due to errors, might be database problem
* during asynchronous starting.
* @deprecated legacy status is not used anymore
*/
@Deprecated
ERROR_STARTING;
}

View File

@@ -152,6 +152,23 @@ public interface RolloutGroup extends NamedEntity {
*/
String getSuccessActionExp();
/**
* @param successAction
* which is executed if the {@link RolloutGroupSuccessCondition}
* is met
*/
void setSuccessAction(RolloutGroupSuccessAction successAction);
/**
*
* @param successActionExp
* a String representation of the expression to be evaluated by
* the {@link RolloutGroupSuccessAction} might be {@code null} if
* no expression must be set for the
* {@link RolloutGroupSuccessAction}
*/
void setSuccessActionExp(String successActionExp);
/**
* @return the total amount of targets containing in this group
*/
@@ -169,10 +186,41 @@ public interface RolloutGroup extends NamedEntity {
void setTotalTargetCountStatus(TotalTargetCountStatus totalTargetCountStatus);
/**
* Rollout goup state machine.
* @return the target filter query, that is used to assign Targets to this
* Group
*/
String getTargetFilterQuery();
/**
* @param targetFilterQuery
* the target filter query, that is used to assign Targets to
* this Group. Can be null, if no restrictions should apply.
*/
void setTargetFilterQuery(String targetFilterQuery);
/**
* @return the percentage of matching Targets that should be assigned to
* this Group
*/
float getTargetPercentage();
/**
* @param targetPercentage
* the percentage of matching Targets that should be assigned to
* this Group
*/
void setTargetPercentage(float targetPercentage);
/**
* Rollout group state machine.
*
*/
public enum RolloutGroupStatus {
enum RolloutGroupStatus {
/**
* Group has been defined, but not all targets have been assigned yet.
*/
CREATING,
/**
* Ready to start the group.
@@ -198,18 +246,18 @@ public interface RolloutGroup extends NamedEntity {
/**
* Group is running.
*/
RUNNING;
RUNNING
}
/**
* The condition to evaluate if an group is success state.
*/
public enum RolloutGroupSuccessCondition {
enum RolloutGroupSuccessCondition {
THRESHOLD("thresholdRolloutGroupSuccessCondition");
private final String beanName;
private RolloutGroupSuccessCondition(final String beanName) {
RolloutGroupSuccessCondition(final String beanName) {
this.beanName = beanName;
}
@@ -224,12 +272,12 @@ public interface RolloutGroup extends NamedEntity {
/**
* The condition to evaluate if an group is in error state.
*/
public enum RolloutGroupErrorCondition {
enum RolloutGroupErrorCondition {
THRESHOLD("thresholdRolloutGroupErrorCondition");
private final String beanName;
private RolloutGroupErrorCondition(final String beanName) {
RolloutGroupErrorCondition(final String beanName) {
this.beanName = beanName;
}
@@ -242,14 +290,14 @@ public interface RolloutGroup extends NamedEntity {
}
/**
* The actions executed when the {@link RolloutGroup#errorCondition} is hit.
* The actions executed when the {@link RolloutGroup#getErrorCondition()} is hit.
*/
public enum RolloutGroupErrorAction {
enum RolloutGroupErrorAction {
PAUSE("pauseRolloutGroupAction");
private final String beanName;
private RolloutGroupErrorAction(final String beanName) {
RolloutGroupErrorAction(final String beanName) {
this.beanName = beanName;
}
@@ -262,15 +310,15 @@ public interface RolloutGroup extends NamedEntity {
}
/**
* The actions executed when the {@link RolloutGroup#successCondition} is
* The actions executed when the {@link RolloutGroup#getSuccessCondition()} is
* hit.
*/
public enum RolloutGroupSuccessAction {
enum RolloutGroupSuccessAction {
NEXTGROUP("startNextRolloutGroupAction");
private final String beanName;
private RolloutGroupSuccessAction(final String beanName) {
RolloutGroupSuccessAction(final String beanName) {
this.beanName = beanName;
}

View File

@@ -18,14 +18,14 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCond
* easily built.
*/
public class RolloutGroupConditions {
private RolloutGroupSuccessCondition successCondition;
private String successConditionExp;
private RolloutGroupSuccessAction successAction;
private String successActionExp;
private RolloutGroupErrorCondition errorCondition;
private String errorConditionExp;
private RolloutGroupErrorAction errorAction;
private String errorActionExp;
private RolloutGroupSuccessCondition successCondition = RolloutGroupSuccessCondition.THRESHOLD;
private String successConditionExp = "50";
private RolloutGroupSuccessAction successAction = RolloutGroupSuccessAction.NEXTGROUP;
private String successActionExp = "";
private RolloutGroupErrorCondition errorCondition = RolloutGroupErrorCondition.THRESHOLD;
private String errorConditionExp = "50";
private RolloutGroupErrorAction errorAction = RolloutGroupErrorAction.PAUSE;
private String errorActionExp = "";
public RolloutGroupSuccessCondition getSuccessCondition() {
return successCondition;