Add support for dynamic rollout group template (#1752)
1. Add support in REST and Mgmt API for dynamic group template 2. If present - groups follows the pattern of this template, otherwise - the last static group 3. This allows to create pure dynamic rollout with 0 static groups - auto assignment equivalent with groups Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -18,6 +18,7 @@ import jakarta.validation.constraints.NotEmpty;
|
|||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||||
|
import org.eclipse.hawkbit.repository.builder.DynamicRolloutGroupTemplate;
|
||||||
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
|
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
|
||||||
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
||||||
import org.eclipse.hawkbit.repository.builder.RolloutUpdate;
|
import org.eclipse.hawkbit.repository.builder.RolloutUpdate;
|
||||||
@@ -78,6 +79,45 @@ public interface RolloutManagement {
|
|||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||||
long countByDistributionSetIdAndRolloutIsStoppable(long setId);
|
long countByDistributionSetIdAndRolloutIsStoppable(long setId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 amount of groups will be defined
|
||||||
|
* as equally sized.
|
||||||
|
*
|
||||||
|
* The rollout is not started. Only the preparation of the rollout is done,
|
||||||
|
* 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 .
|
||||||
|
*
|
||||||
|
* @param create
|
||||||
|
* the rollout entity to create
|
||||||
|
* @param amountGroup
|
||||||
|
* the amount of groups to split the rollout into
|
||||||
|
* @param confirmationRequired
|
||||||
|
* if a confirmation is required by the device group(s) of the rollout
|
||||||
|
* @param conditions
|
||||||
|
* the rolloutgroup conditions and actions which should be applied
|
||||||
|
* for each {@link RolloutGroup}
|
||||||
|
* @param dynamicRolloutGroupTemplate the template for dynamic rollout groups
|
||||||
|
* @return the persisted rollout.
|
||||||
|
*
|
||||||
|
* @throws EntityNotFoundException
|
||||||
|
* if given {@link DistributionSet} does not exist
|
||||||
|
* @throws ConstraintViolationException
|
||||||
|
* if rollout or group parameters are invalid.
|
||||||
|
* @throws AssignmentQuotaExceededException
|
||||||
|
* if the maximum number of allowed targets per rollout group is
|
||||||
|
* exceeded.
|
||||||
|
*/
|
||||||
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_CREATE)
|
||||||
|
Rollout create(@NotNull @Valid RolloutCreate create, int amountGroup, boolean confirmationRequired,
|
||||||
|
@NotNull RolloutGroupConditions conditions, DynamicRolloutGroupTemplate dynamicRolloutGroupTemplate);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persists a new rollout entity. The filter within the
|
* Persists a new rollout entity. The filter within the
|
||||||
* {@link Rollout#getTargetFilterQuery()} is used to retrieve the targets which
|
* {@link Rollout#getTargetFilterQuery()} is used to retrieve the targets which
|
||||||
@@ -130,7 +170,7 @@ public interface RolloutManagement {
|
|||||||
* The RolloutScheduler will start to assign targets to the groups. Once all
|
* 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
|
* targets have been assigned to the groups, the rollout status is changed to
|
||||||
* {@link RolloutStatus#READY} so it can be started with
|
* {@link RolloutStatus#READY} so it can be started with
|
||||||
* {@link #start(Rollout)}.
|
* {@link #start(long)}.
|
||||||
*
|
*
|
||||||
* @param rollout
|
* @param rollout
|
||||||
* the rollout entity to create
|
* the rollout entity to create
|
||||||
@@ -274,8 +314,6 @@ public interface RolloutManagement {
|
|||||||
*
|
*
|
||||||
* @param rolloutId
|
* @param rolloutId
|
||||||
* rollout id
|
* rollout id
|
||||||
* @param deleted
|
|
||||||
* flag if deleted rollouts should be included
|
|
||||||
* @return rollout details of targets count for different statuses
|
* @return rollout details of targets count for different statuses
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 Contributors to the Eclipse Foundation
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made
|
||||||
|
* available under the terms of the Eclipse Public License 2.0
|
||||||
|
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.repository.builder;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder to create a new dynamic rollout group secret
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class DynamicRolloutGroupTemplate {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name suffix, by default "" is used.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private String nameSuffix = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The count of matching Targets that should be assigned to this Group
|
||||||
|
*/
|
||||||
|
private long targetCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The group conditions
|
||||||
|
*/
|
||||||
|
private RolloutGroupConditions conditions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If confirmation is required for this rollout group (considered with confirmation flow active)
|
||||||
|
*/
|
||||||
|
private boolean confirmationRequired;
|
||||||
|
}
|
||||||
@@ -13,7 +13,6 @@ import org.eclipse.hawkbit.repository.model.Rollout;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder for {@link Rollout}.
|
* Builder for {@link Rollout}.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface RolloutGroupBuilder {
|
public interface RolloutGroupBuilder {
|
||||||
@@ -22,5 +21,4 @@ public interface RolloutGroupBuilder {
|
|||||||
* @return builder instance
|
* @return builder instance
|
||||||
*/
|
*/
|
||||||
RolloutGroupCreate create();
|
RolloutGroupCreate create();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -24,49 +24,41 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
|||||||
* Builder to create a new {@link RolloutGroup} entry. Defines all fields that
|
* Builder to create a new {@link RolloutGroup} entry. Defines all fields that
|
||||||
* can be set at creation time. Other fields are set by the repository
|
* can be set at creation time. Other fields are set by the repository
|
||||||
* automatically, e.g. {@link BaseEntity#getCreatedAt()}.
|
* automatically, e.g. {@link BaseEntity#getCreatedAt()}.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public interface RolloutGroupCreate {
|
public interface RolloutGroupCreate {
|
||||||
/**
|
/**
|
||||||
* @param name
|
* @param name for {@link Rollout#getName()}
|
||||||
* for {@link Rollout#getName()}
|
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
RolloutGroupCreate name(@Size(min = 1, max = NamedEntity.NAME_MAX_SIZE) @NotNull String name);
|
RolloutGroupCreate name(@Size(min = 1, max = NamedEntity.NAME_MAX_SIZE) @NotNull String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param description
|
* @param description for {@link Rollout#getDescription()}
|
||||||
* for {@link Rollout#getDescription()}
|
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
RolloutGroupCreate description(@Size(max = NamedEntity.DESCRIPTION_MAX_SIZE) String description);
|
RolloutGroupCreate description(@Size(max = NamedEntity.DESCRIPTION_MAX_SIZE) String description);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param targetFilterQuery
|
* @param targetFilterQuery for {@link Rollout#getTargetFilterQuery()}
|
||||||
* for {@link Rollout#getTargetFilterQuery()}
|
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
RolloutGroupCreate targetFilterQuery(
|
RolloutGroupCreate targetFilterQuery(
|
||||||
@Size(min = 1, max = TargetFilterQuery.QUERY_MAX_SIZE) @NotNull String targetFilterQuery);
|
@Size(min = 1, max = TargetFilterQuery.QUERY_MAX_SIZE) @NotNull String targetFilterQuery);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param targetPercentage
|
* @param targetPercentage the percentage of matching Targets that should be assigned to this Group
|
||||||
* the percentage of matching Targets that should be assigned to
|
|
||||||
* this Group
|
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
RolloutGroupCreate targetPercentage(Float targetPercentage);
|
RolloutGroupCreate targetPercentage(Float targetPercentage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param conditions
|
* @param conditions as created by {@link RolloutGroupConditionBuilder}.
|
||||||
* as created by {@link RolloutGroupConditionBuilder}.
|
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
RolloutGroupCreate conditions(RolloutGroupConditions conditions);
|
RolloutGroupCreate conditions(RolloutGroupConditions conditions);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param confirmationRequired
|
* @param confirmationRequired if confirmation is required for this rollout group (considered
|
||||||
* if confirmation is required for this rollout group (considered
|
|
||||||
* with confirmation flow active)
|
* with confirmation flow active)
|
||||||
* @return updated builder instance
|
* @return updated builder instance
|
||||||
*/
|
*/
|
||||||
@@ -76,5 +68,4 @@ public interface RolloutGroupCreate {
|
|||||||
* @return peek on current state of {@link RolloutGroup} in the builder
|
* @return peek on current state of {@link RolloutGroup} in the builder
|
||||||
*/
|
*/
|
||||||
RolloutGroup build();
|
RolloutGroup build();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -12,15 +12,12 @@ package org.eclipse.hawkbit.repository.builder;
|
|||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import jakarta.validation.constraints.Size;
|
import jakarta.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.NamedEntity;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder to update an existing {@link Rollout} entry. Defines all fields that
|
* Builder to update an existing {@link Rollout} entry. Defines all fields that
|
||||||
* can be updated.
|
* can be updated.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public interface RolloutUpdate {
|
public interface RolloutUpdate {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public final class RolloutHelper {
|
|||||||
*/
|
*/
|
||||||
public static void verifyRolloutGroupParameter(final int amountGroup, final QuotaManagement quotaManagement) {
|
public static void verifyRolloutGroupParameter(final int amountGroup, final QuotaManagement quotaManagement) {
|
||||||
if (amountGroup <= 0) {
|
if (amountGroup <= 0) {
|
||||||
throw new ValidationException("The amount of groups cannot be lower than zero");
|
throw new ValidationException("The amount of groups cannot be lower than or equal to zero for static rollouts");
|
||||||
} else if (amountGroup > quotaManagement.getMaxRolloutGroupsPerRollout()) {
|
} else if (amountGroup > quotaManagement.getMaxRolloutGroupsPerRollout()) {
|
||||||
throw new AssignmentQuotaExceededException(
|
throw new AssignmentQuotaExceededException(
|
||||||
"The amount of groups cannot be greater than " + quotaManagement.getMaxRolloutGroupsPerRollout());
|
"The amount of groups cannot be greater than " + quotaManagement.getMaxRolloutGroupsPerRollout());
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.eclipse.hawkbit.repository.TargetManagement;
|
|||||||
import org.eclipse.hawkbit.repository.event.remote.RolloutGroupDeletedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.RolloutGroupDeletedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.RolloutStoppedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.RolloutStoppedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||||
import org.eclipse.hawkbit.repository.jpa.management.JpaRolloutManagement;
|
import org.eclipse.hawkbit.repository.jpa.management.JpaRolloutManagement;
|
||||||
@@ -221,7 +222,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
|||||||
// When all groups are ready the rollout status can be changed to be
|
// When all groups are ready the rollout status can be changed to be
|
||||||
// ready, too.
|
// ready, too.
|
||||||
if (readyGroups == rolloutGroups.size()) {
|
if (readyGroups == rolloutGroups.size()) {
|
||||||
if (rollout.isDynamic()) {
|
if (rollout.isDynamic() && !rolloutGroups.get(rolloutGroups.size() - 1).isDynamic()) {
|
||||||
// add first dynamic group one by using the last as a parent and as a pattern
|
// add first dynamic group one by using the last as a parent and as a pattern
|
||||||
createDynamicGroup(rollout, rolloutGroups.get(rolloutGroups.size() - 1), rolloutGroups.size(), RolloutGroupStatus.READY);
|
createDynamicGroup(rollout, rolloutGroups.get(rolloutGroups.size() - 1), rolloutGroups.size(), RolloutGroupStatus.READY);
|
||||||
}
|
}
|
||||||
@@ -395,9 +396,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendRolloutGroupDeletedEvents(final JpaRollout rollout) {
|
private void sendRolloutGroupDeletedEvents(final JpaRollout rollout) {
|
||||||
final List<Long> groupIds = rollout.getRolloutGroups().stream().map(RolloutGroup::getId)
|
final List<Long> groupIds = rollout.getRolloutGroups().stream().map(RolloutGroup::getId).toList();
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
afterCommit.afterCommit(() -> groupIds.forEach(rolloutGroupId -> eventPublisherHolder.getEventPublisher()
|
afterCommit.afterCommit(() -> groupIds.forEach(rolloutGroupId -> eventPublisherHolder.getEventPublisher()
|
||||||
.publishEvent(new RolloutGroupDeletedEvent(tenantAware.getCurrentTenant(), rolloutGroupId,
|
.publishEvent(new RolloutGroupDeletedEvent(tenantAware.getCurrentTenant(), rolloutGroupId,
|
||||||
JpaRolloutGroup.class, eventPublisherHolder.getApplicationId()))));
|
JpaRolloutGroup.class, eventPublisherHolder.getApplicationId()))));
|
||||||
@@ -425,24 +424,11 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
|||||||
executeRolloutGroupSuccessAction(rollout, latestRolloutGroup.get(0));
|
executeRolloutGroupSuccessAction(rollout, latestRolloutGroup.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int DEFAULT_DYNAMIC_GROUP_EXPECTED = 100;
|
|
||||||
private static int expectedDynamicGroupSize(final List<? extends RolloutGroup> rolloutGroups) {
|
|
||||||
int expected = 0;
|
|
||||||
// gets the size of last static group (it is a pattern for dynamic groups)
|
|
||||||
for (final RolloutGroup rolloutGroup : rolloutGroups) {
|
|
||||||
if (rolloutGroup.isDynamic()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
expected = rolloutGroup.getTotalTargets();
|
|
||||||
}
|
|
||||||
return expected <= 0 ? DEFAULT_DYNAMIC_GROUP_EXPECTED /* default if the last static group has been empty */ : expected;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fakes getTotalTargets count to match expected for the last dynamic group
|
// fakes getTotalTargets count to match expected for the last dynamic group
|
||||||
// so the evaluation to use total targets to properly
|
// so the evaluation to use total targets to properly
|
||||||
private RolloutGroup evalProxy(final RolloutGroup group, final List<JpaRolloutGroup> rolloutGroups) {
|
private RolloutGroup evalProxy(final RolloutGroup group, final List<JpaRolloutGroup> rolloutGroups) {
|
||||||
if (group.isDynamic()) {
|
if (group.isDynamic()) {
|
||||||
final int expected = expectedDynamicGroupSize(rolloutGroups);
|
final int expected = Math.max((int)group.getTargetPercentage(), 1);
|
||||||
return (RolloutGroup) Proxy.newProxyInstance(
|
return (RolloutGroup) Proxy.newProxyInstance(
|
||||||
RolloutGroup.class.getClassLoader(),
|
RolloutGroup.class.getClassLoader(),
|
||||||
new Class<?>[] {RolloutGroup.class},
|
new Class<?>[] {RolloutGroup.class},
|
||||||
@@ -702,8 +688,8 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// expected as last full group - last static or previously filled in dynamic group
|
// expected as last full group
|
||||||
final long expectedInGroup = expectedDynamicGroupSize(rolloutGroups);
|
final long expectedInGroup = Math.max((int)group.getTargetPercentage(), 1);
|
||||||
|
|
||||||
final long currentlyInGroup = group.getTotalTargets();
|
final long currentlyInGroup = group.getTotalTargets();
|
||||||
if (currentlyInGroup >= expectedInGroup) {
|
if (currentlyInGroup >= expectedInGroup) {
|
||||||
@@ -739,7 +725,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
|||||||
updateTotalTargetCount(group, group.getTotalTargets() + newActions);
|
updateTotalTargetCount(group, group.getTotalTargets() + newActions);
|
||||||
|
|
||||||
if (targetsLeftToAdd == 0) {
|
if (targetsLeftToAdd == 0) {
|
||||||
// this is filled create a new one in sheduled state
|
// this is filled create a new one in scheduled state
|
||||||
createDynamicGroup(rollout, group, rolloutGroups.size(), RolloutGroupStatus.SCHEDULED);
|
createDynamicGroup(rollout, group, rolloutGroups.size(), RolloutGroupStatus.SCHEDULED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -755,19 +741,32 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void createDynamicGroup(final JpaRollout rollout, final RolloutGroup lastGroup, final int groupCount, final RolloutGroupStatus status) {
|
private void createDynamicGroup(final JpaRollout rollout, final RolloutGroup lastGroup, final int groupCount, final RolloutGroupStatus status) {
|
||||||
|
try {
|
||||||
|
RolloutHelper.verifyRolloutGroupParameter(groupCount + 1, quotaManagement);
|
||||||
|
} catch (final AssignmentQuotaExceededException e) {
|
||||||
|
log.warn("Quota exceeded for dynamic rollout group creation: {}. Stop it", e.getMessage());
|
||||||
|
if (isRolloutComplete(rollout)) {
|
||||||
|
rollout.setStatus(RolloutStatus.STOPPED);
|
||||||
|
rolloutRepository.save(rollout);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
final JpaRolloutGroup group = new JpaRolloutGroup();
|
final JpaRolloutGroup group = new JpaRolloutGroup();
|
||||||
final String nameAndDesc = "group-" + (groupCount + 1) + "-dynamic";
|
final String lastGroupWithoutSuffix = "group-" + groupCount;
|
||||||
group.setDynamic(true);
|
final String suffix = lastGroup.getName().startsWith(lastGroupWithoutSuffix) ? lastGroup.getName().substring(lastGroupWithoutSuffix.length()) : "";
|
||||||
|
final String nameAndDesc = "group-" + (groupCount + 1) + suffix;
|
||||||
group.setName(nameAndDesc);
|
group.setName(nameAndDesc);
|
||||||
group.setDescription(nameAndDesc);
|
group.setDescription(nameAndDesc);
|
||||||
group.setRollout(rollout);
|
group.setRollout(rollout);
|
||||||
group.setParent(lastGroup);
|
group.setParent(lastGroup);
|
||||||
|
group.setDynamic(true);
|
||||||
// no need to be filled with targets, directly in ready (if first on create - it will be scheduled on start)
|
// no need to be filled with targets, directly in ready (if first on create - it will be scheduled on start)
|
||||||
// or scheduled state (for next dynamic groups)
|
// or scheduled state (for next dynamic groups)
|
||||||
group.setStatus(status);
|
group.setStatus(status);
|
||||||
group.setConfirmationRequired(lastGroup.isConfirmationRequired());
|
group.setConfirmationRequired(lastGroup.isConfirmationRequired());
|
||||||
|
|
||||||
group.setTargetPercentage(lastGroup.getTargetPercentage());
|
// for dynamic groups the target count is kept in target percentage
|
||||||
|
group.setTargetPercentage(lastGroup.isDynamic() ? lastGroup.getTargetPercentage() : lastGroup.getTotalTargets());
|
||||||
group.setTargetFilterQuery(lastGroup.getTargetFilterQuery());
|
group.setTargetFilterQuery(lastGroup.getTargetFilterQuery());
|
||||||
|
|
||||||
addSuccessAndErrorConditionsAndActions(group, lastGroup.getSuccessCondition(),
|
addSuccessAndErrorConditionsAndActions(group, lastGroup.getSuccessCondition(),
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ import java.util.function.Function;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import jakarta.validation.ConstraintDeclarationException;
|
import jakarta.validation.ConstraintDeclarationException;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
import jakarta.validation.ValidationException;
|
import jakarta.validation.ValidationException;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
@@ -36,6 +38,7 @@ import org.eclipse.hawkbit.repository.RolloutStatusCache;
|
|||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.ContextAware;
|
import org.eclipse.hawkbit.ContextAware;
|
||||||
|
import org.eclipse.hawkbit.repository.builder.DynamicRolloutGroupTemplate;
|
||||||
import org.eclipse.hawkbit.repository.builder.GenericRolloutUpdate;
|
import org.eclipse.hawkbit.repository.builder.GenericRolloutUpdate;
|
||||||
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
|
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
|
||||||
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
||||||
@@ -181,15 +184,37 @@ public class JpaRolloutManagement implements RolloutManagement {
|
|||||||
return rolloutRepository.findById(rolloutId).map(Rollout.class::cast);
|
return rolloutRepository.findById(rolloutId).map(Rollout.class::cast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
@Retryable(include = {
|
||||||
|
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
|
public Rollout create(@NotNull @Valid RolloutCreate create, int amountGroup, boolean confirmationRequired,
|
||||||
|
@NotNull RolloutGroupConditions conditions) {
|
||||||
|
return create(create, amountGroup, confirmationRequired, conditions, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@Retryable(include = {
|
@Retryable(include = {
|
||||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
public Rollout create(final RolloutCreate rollout, final int amountGroup, final boolean confirmationRequired,
|
public Rollout create(final RolloutCreate rollout, final int amountGroup, final boolean confirmationRequired,
|
||||||
final RolloutGroupConditions conditions) {
|
final RolloutGroupConditions conditions, final DynamicRolloutGroupTemplate dynamicRolloutGroupTemplate) {
|
||||||
|
if (amountGroup == 0) {
|
||||||
|
if (dynamicRolloutGroupTemplate == null) {
|
||||||
|
throw new ValidationException(
|
||||||
|
"When amount of groups is 0, the rollouts shall be dynamic and a dynamic group template must be provided");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
RolloutHelper.verifyRolloutGroupParameter(amountGroup, quotaManagement);
|
RolloutHelper.verifyRolloutGroupParameter(amountGroup, quotaManagement);
|
||||||
final JpaRollout savedRollout = createRollout((JpaRollout) rollout.build());
|
}
|
||||||
return createRolloutGroups(amountGroup, conditions, savedRollout, confirmationRequired);
|
|
||||||
|
final JpaRollout rolloutRequest = (JpaRollout) rollout.build();
|
||||||
|
if (dynamicRolloutGroupTemplate != null && !rolloutRequest.isDynamic()) {
|
||||||
|
throw new ValidationException("Dynamic group template is only allowed for dynamic rollouts");
|
||||||
|
}
|
||||||
|
|
||||||
|
final JpaRollout savedRollout = createRollout(rolloutRequest, amountGroup == 0);
|
||||||
|
return createRolloutGroups(amountGroup, conditions, savedRollout, confirmationRequired, dynamicRolloutGroupTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -199,13 +224,18 @@ public class JpaRolloutManagement implements RolloutManagement {
|
|||||||
public Rollout create(final RolloutCreate rollout, final List<RolloutGroupCreate> groups,
|
public Rollout create(final RolloutCreate rollout, final List<RolloutGroupCreate> groups,
|
||||||
final RolloutGroupConditions conditions) {
|
final RolloutGroupConditions conditions) {
|
||||||
RolloutHelper.verifyRolloutGroupParameter(groups.size(), quotaManagement);
|
RolloutHelper.verifyRolloutGroupParameter(groups.size(), quotaManagement);
|
||||||
final JpaRollout savedRollout = createRollout((JpaRollout) rollout.build());
|
final JpaRollout rolloutRequest = (JpaRollout) rollout.build();
|
||||||
|
final JpaRollout savedRollout = createRollout(rolloutRequest, false);
|
||||||
return createRolloutGroups(groups, conditions, savedRollout);
|
return createRolloutGroups(groups, conditions, savedRollout);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JpaRollout createRollout(final JpaRollout rollout) {
|
private JpaRollout createRollout(final JpaRollout rollout, final boolean pureDynamic) {
|
||||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(rollout);
|
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(rollout);
|
||||||
final JpaDistributionSet distributionSet = (JpaDistributionSet) rollout.getDistributionSet();
|
final JpaDistributionSet distributionSet = (JpaDistributionSet) rollout.getDistributionSet();
|
||||||
|
|
||||||
|
if (pureDynamic) {
|
||||||
|
rollout.setTotalTargets(0);
|
||||||
|
} else {
|
||||||
final long totalTargets;
|
final long totalTargets;
|
||||||
final String errMsg;
|
final String errMsg;
|
||||||
if (RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) {
|
if (RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) {
|
||||||
@@ -222,6 +252,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
|||||||
throw new ValidationException(errMsg);
|
throw new ValidationException(errMsg);
|
||||||
}
|
}
|
||||||
rollout.setTotalTargets(totalTargets);
|
rollout.setTotalTargets(totalTargets);
|
||||||
|
}
|
||||||
|
|
||||||
if (((JpaDistributionSetManagement)distributionSetManagement).isImplicitLockApplicable(distributionSet)) {
|
if (((JpaDistributionSetManagement)distributionSetManagement).isImplicitLockApplicable(distributionSet)) {
|
||||||
distributionSetManagement.lock(distributionSet.getId());
|
distributionSetManagement.lock(distributionSet.getId());
|
||||||
@@ -235,23 +266,27 @@ public class JpaRolloutManagement implements RolloutManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Rollout createRolloutGroups(final int amountOfGroups, final RolloutGroupConditions conditions,
|
private Rollout createRolloutGroups(final int amountOfGroups, final RolloutGroupConditions conditions,
|
||||||
final JpaRollout rollout, final boolean isConfirmationRequired) {
|
final JpaRollout rollout, final boolean isConfirmationRequired, final DynamicRolloutGroupTemplate dynamicRolloutGroupTemplate) {
|
||||||
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.CREATING);
|
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.CREATING);
|
||||||
RolloutHelper.verifyRolloutGroupConditions(conditions);
|
RolloutHelper.verifyRolloutGroupConditions(conditions);
|
||||||
|
|
||||||
final JpaRollout savedRollout = rollout;
|
RolloutGroup lastSavedGroup = null;
|
||||||
|
if (amountOfGroups == 0) {
|
||||||
|
if (dynamicRolloutGroupTemplate == null) {
|
||||||
|
throw new ConstraintDeclarationException(
|
||||||
|
"At least one static rollout group must be defined for a static rollout");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// we can enforce the 'max targets per group' quota right here because
|
// we can enforce the 'max targets per group' quota right here because
|
||||||
// we want to distribute the targets equally to the different groups
|
// we want to distribute the targets equally to the different groups
|
||||||
assertTargetsPerRolloutGroupQuota(rollout.getTotalTargets() / amountOfGroups);
|
assertTargetsPerRolloutGroupQuota(rollout.getTotalTargets() / amountOfGroups);
|
||||||
|
|
||||||
RolloutGroup lastSavedGroup = null;
|
|
||||||
for (int i = 0; i < amountOfGroups; i++) {
|
for (int i = 0; i < amountOfGroups; i++) {
|
||||||
final String nameAndDesc = "group-" + (i + 1);
|
final String nameAndDesc = "group-" + (i + 1);
|
||||||
final JpaRolloutGroup group = new JpaRolloutGroup();
|
final JpaRolloutGroup group = new JpaRolloutGroup();
|
||||||
group.setName(nameAndDesc);
|
group.setName(nameAndDesc);
|
||||||
group.setDescription(nameAndDesc);
|
group.setDescription(nameAndDesc);
|
||||||
group.setRollout(savedRollout);
|
group.setRollout(rollout);
|
||||||
group.setParent(lastSavedGroup);
|
group.setParent(lastSavedGroup);
|
||||||
group.setStatus(RolloutGroupStatus.CREATING);
|
group.setStatus(RolloutGroupStatus.CREATING);
|
||||||
group.setConfirmationRequired(isConfirmationRequired);
|
group.setConfirmationRequired(isConfirmationRequired);
|
||||||
@@ -271,9 +306,31 @@ public class JpaRolloutManagement implements RolloutManagement {
|
|||||||
lastSavedGroup = rolloutGroupRepository.save(group);
|
lastSavedGroup = rolloutGroupRepository.save(group);
|
||||||
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup, rollout);
|
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup, rollout);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
savedRollout.setRolloutGroupsCreated(amountOfGroups);
|
if (dynamicRolloutGroupTemplate != null && rollout.isDynamic()) { // if not null then it is a dynamic rollout (already validated), but for sure
|
||||||
return rolloutRepository.save(savedRollout);
|
// create first template rollout group
|
||||||
|
final String nameAndDesc = "group-" + (amountOfGroups + 1) + dynamicRolloutGroupTemplate.getNameSuffix();
|
||||||
|
final JpaRolloutGroup group = new JpaRolloutGroup();
|
||||||
|
group.setName(nameAndDesc);
|
||||||
|
group.setDescription(nameAndDesc);
|
||||||
|
group.setRollout(rollout);
|
||||||
|
group.setParent(lastSavedGroup);
|
||||||
|
group.setDynamic(true);
|
||||||
|
group.setStatus(RolloutGroupStatus.READY);
|
||||||
|
group.setConfirmationRequired(isConfirmationRequired);
|
||||||
|
|
||||||
|
addSuccessAndErrorConditionsAndActions(group, conditions);
|
||||||
|
|
||||||
|
// for dynamic groups the target count is kept in target percentage
|
||||||
|
group.setTargetPercentage(dynamicRolloutGroupTemplate.getTargetCount());
|
||||||
|
|
||||||
|
lastSavedGroup = rolloutGroupRepository.save(group);
|
||||||
|
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup, rollout);
|
||||||
|
}
|
||||||
|
|
||||||
|
rollout.setRolloutGroupsCreated(lastSavedGroup.isDynamic() ? amountOfGroups + 1 : amountOfGroups);
|
||||||
|
return rolloutRepository.save(rollout);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Rollout createRolloutGroups(final List<RolloutGroupCreate> groupList,
|
private Rollout createRolloutGroups(final List<RolloutGroupCreate> groupList,
|
||||||
|
|||||||
@@ -174,28 +174,28 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
|
|||||||
|
|
||||||
protected void assertRollout(final Rollout rollout, final boolean dynamic, final Rollout.RolloutStatus status, final int groupCreated, final long totalTargets) {
|
protected void assertRollout(final Rollout rollout, final boolean dynamic, final Rollout.RolloutStatus status, final int groupCreated, final long totalTargets) {
|
||||||
final Rollout refreshed = refresh(rollout);
|
final Rollout refreshed = refresh(rollout);
|
||||||
assertThat(refreshed.isDynamic()).isEqualTo(dynamic);
|
assertThat(refreshed.isDynamic()).as("Is dynamic").isEqualTo(dynamic);
|
||||||
assertThat(refreshed.getStatus()).isEqualTo(status);
|
assertThat(refreshed.getStatus()).as("Status").isEqualTo(status);
|
||||||
assertThat(refreshed.getRolloutGroupsCreated()).isEqualTo(groupCreated);
|
assertThat(refreshed.getRolloutGroupsCreated()).as("Groups created").isEqualTo(groupCreated);
|
||||||
assertThat(refreshed.getTotalTargets()).isEqualTo(totalTargets);
|
assertThat(refreshed.getTotalTargets()).as("Total targets").isEqualTo(totalTargets);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertGroup(final RolloutGroup group, final boolean dynamic, final RolloutGroup.RolloutGroupStatus status, final long totalTargets) {
|
protected void assertGroup(final RolloutGroup group, final boolean dynamic, final RolloutGroup.RolloutGroupStatus status, final long totalTargets) {
|
||||||
final RolloutGroup refreshed = refresh(group);
|
final RolloutGroup refreshed = refresh(group);
|
||||||
assertThat(refreshed.isDynamic()).isEqualTo(dynamic);
|
assertThat(refreshed.isDynamic()).as("Is dynamic").isEqualTo(dynamic);
|
||||||
assertThat(refreshed.getStatus()).isEqualTo(status);
|
assertThat(refreshed.getStatus()).as("Status").isEqualTo(status);
|
||||||
assertThat(refreshed.getTotalTargets()).isEqualTo(totalTargets);
|
assertThat(refreshed.getTotalTargets()).as("Total targets").isEqualTo(totalTargets);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Page<JpaAction> assertAndGetRunning(final Rollout rollout, final int count) {
|
protected Page<JpaAction> assertAndGetRunning(final Rollout rollout, final int count) {
|
||||||
final Page<JpaAction> running = actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), Action.Status.RUNNING);
|
final Page<JpaAction> running = actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), Action.Status.RUNNING);
|
||||||
assertThat(running.getTotalElements()).isEqualTo(count);
|
assertThat(running.getTotalElements()).as("Action count").isEqualTo(count);
|
||||||
return running;
|
return running;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertScheduled(final Rollout rollout, final int count) {
|
protected void assertScheduled(final Rollout rollout, final int count) {
|
||||||
final Page<JpaAction> running = actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), Action.Status.SCHEDULED);
|
final Page<JpaAction> running = actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), Action.Status.SCHEDULED);
|
||||||
assertThat(running.getTotalElements()).isEqualTo(count);
|
assertThat(running.getTotalElements()).as("Action count").isEqualTo(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void finishAction(final Action action) {
|
protected void finishAction(final Action action) {
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import io.qameta.allure.Description;
|
|||||||
import io.qameta.allure.Feature;
|
import io.qameta.allure.Feature;
|
||||||
import io.qameta.allure.Story;
|
import io.qameta.allure.Story;
|
||||||
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
||||||
|
import org.eclipse.hawkbit.repository.builder.DynamicRolloutGroupTemplate;
|
||||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||||
@@ -85,7 +87,7 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
@Description("Verifies a simple dynamic rollout flow")
|
@Description("Verifies a simple dynamic rollout flow")
|
||||||
void dynamicRolloutFlow() {
|
void dynamicRolloutFlow() {
|
||||||
final String rolloutName = "dynamic-rollout-std";
|
final String rolloutName = "dynamic-rollout-std";
|
||||||
final int amountGroups = 5; // static only
|
final int amountGroups = 3; // static only
|
||||||
final String targetPrefix = "controller-dynamic-rollout-std-";
|
final String targetPrefix = "controller-dynamic-rollout-std-";
|
||||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
|
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
|
||||||
|
|
||||||
@@ -177,6 +179,193 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 2); // assign the target created when paused
|
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 2); // assign the target created when paused
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies a simple dynamic rollout flow with a dynamic group template")
|
||||||
|
void dynamicRolloutTemplateFlow() {
|
||||||
|
final String rolloutName = "dynamic-template-rollout-std";
|
||||||
|
final int amountGroups = 3; // static only
|
||||||
|
final String targetPrefix = "controller-template-dynamic-rollout-std-";
|
||||||
|
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
|
||||||
|
|
||||||
|
// create rollout with amountGroups static groups * 3 targets and dynamic group template with 6 targets
|
||||||
|
testdataFactory.createTargets(targetPrefix, 0, amountGroups * 3);
|
||||||
|
final Rollout rollout = testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||||
|
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30",
|
||||||
|
Action.ActionType.FORCED, 1000, false, true,
|
||||||
|
DynamicRolloutGroupTemplate.builder().nameSuffix("-dyn").targetCount(6).build());
|
||||||
|
|
||||||
|
// rollout is READY, amountGroups + 1 (dynamic) rollout groups and amountGroups * 3 targets in static groups
|
||||||
|
assertRollout(rollout, true, RolloutStatus.READY, amountGroups + 1, amountGroups * 3);
|
||||||
|
List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(
|
||||||
|
new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id")),
|
||||||
|
rollout.getId()).getContent();
|
||||||
|
final RolloutGroup dynamic1 = groups.get(amountGroups);
|
||||||
|
assertRollout(rollout, true, RolloutStatus.READY, amountGroups + 1, amountGroups * 3); // + dynamic
|
||||||
|
for (int i = 0; i < amountGroups; i++) {
|
||||||
|
assertGroup(groups.get(i), false, RolloutGroupStatus.READY, 3);
|
||||||
|
}
|
||||||
|
assertGroup(dynamic1, true, RolloutGroupStatus.READY, 0);
|
||||||
|
|
||||||
|
// add 4 targets for the first dynamic group, fill partially
|
||||||
|
testdataFactory.createTargets(targetPrefix, amountGroups * 3, 4);
|
||||||
|
// start rollout
|
||||||
|
rolloutManagement.start(rollout.getId());
|
||||||
|
|
||||||
|
// handleStartingRollout (no handleRunning called yet)
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 1, amountGroups * 3);
|
||||||
|
for (int i = 0; i < amountGroups; i++) {
|
||||||
|
assertGroup(groups.get(i), false, i == 0 ? RolloutGroupStatus.RUNNING : RolloutGroupStatus.SCHEDULED, 3);
|
||||||
|
}
|
||||||
|
assertGroup(dynamic1, true, RolloutGroupStatus.SCHEDULED, 0);
|
||||||
|
|
||||||
|
executeStaticWithoutOneTargetFromTheLastGroup(groups, rollout, amountGroups);
|
||||||
|
|
||||||
|
// partially fill the first dynamic (it is running and now create actions for 4 targets)
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 1, amountGroups * 3 + 4);
|
||||||
|
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 4);
|
||||||
|
|
||||||
|
// fill first (2) and create fill partially the second (+2 new)
|
||||||
|
testdataFactory.createTargets(targetPrefix, amountGroups * 3 + 4, 4);
|
||||||
|
rolloutHandler.handleAll(); // fill first dynamic group and create a new dynamic2
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 6);
|
||||||
|
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 6);
|
||||||
|
groups = rolloutGroupManagement.findByRollout(
|
||||||
|
new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id")),
|
||||||
|
rollout.getId()).getContent();
|
||||||
|
final RolloutGroup dynamic2 = groups.get(amountGroups + 1);
|
||||||
|
assertGroup(dynamic2, true, RolloutGroupStatus.SCHEDULED, 0);
|
||||||
|
|
||||||
|
// create scheduled actions for the dynamic2
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 6);
|
||||||
|
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 6);
|
||||||
|
assertGroup(dynamic2, true, RolloutGroupStatus.SCHEDULED, 0);
|
||||||
|
assertAndGetRunning(rollout, 7); // one from the last static group and 6 from the first dynamic
|
||||||
|
assertScheduled(rollout, 0);
|
||||||
|
|
||||||
|
// executes last from static and dynamic1 without 1 target
|
||||||
|
assertAndGetRunning(rollout, 7)// one from the last static and 6 from the first dynamic
|
||||||
|
.stream()
|
||||||
|
// remove the last assigned to dynamic1 - it could be amountGroups * 3 + 2 or bigger by id
|
||||||
|
.filter(action -> Integer.parseInt(action.getTarget().getControllerId().substring(targetPrefix.length())) < amountGroups * 3 + 5)
|
||||||
|
.forEach(this::finishAction);
|
||||||
|
assertAndGetRunning(rollout, 1); // remains on in the first dynamic
|
||||||
|
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 6);
|
||||||
|
assertGroup(groups.get(amountGroups - 1), false, RolloutGroupStatus.FINISHED, 3);
|
||||||
|
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 6);
|
||||||
|
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 0);
|
||||||
|
|
||||||
|
rolloutHandler.handleAll(); // add 2 action to now running second dynamic
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 8);
|
||||||
|
assertAndGetRunning(rollout, 3);
|
||||||
|
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 2);
|
||||||
|
|
||||||
|
testdataFactory.createTargets(targetPrefix, amountGroups * 3 + 8, 2);
|
||||||
|
rolloutManagement.pauseRollout(rollout.getId());
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
assertRollout(rollout, true, RolloutStatus.PAUSED, amountGroups + 2, amountGroups * 3 + 8);
|
||||||
|
assertAndGetRunning(rollout, 3);
|
||||||
|
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 2); // no new assignment
|
||||||
|
|
||||||
|
rolloutManagement.resumeRollout(rollout.getId());
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 10);
|
||||||
|
assertAndGetRunning(rollout, 5);
|
||||||
|
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 4); // assign the target created when paused
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies a simple pure (no static groups) dynamic rollout flow with a dynamic group template")
|
||||||
|
void dynamicRolloutPureFlow() {
|
||||||
|
final String rolloutName = "pure-dynamic-rollout-std";
|
||||||
|
final String targetPrefix = "controller-pure-dynamic-rollout-std-";
|
||||||
|
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
|
||||||
|
|
||||||
|
final Rollout rollout = testdataFactory.createRolloutByVariables(rolloutName, rolloutName, 0,
|
||||||
|
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30",
|
||||||
|
Action.ActionType.FORCED, 1000, false, true,
|
||||||
|
DynamicRolloutGroupTemplate.builder().nameSuffix("-dyn").targetCount(6).build());
|
||||||
|
|
||||||
|
// rollout is READY, amountGroups + 1 (dynamic) rollout groups and amountGroups * 3 targets in static groups
|
||||||
|
assertRollout(rollout, true, RolloutStatus.READY, 1, 0);
|
||||||
|
List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(
|
||||||
|
new OffsetBasedPageRequest(0, 10, Sort.by(Direction.ASC, "id")),
|
||||||
|
rollout.getId()).getContent();
|
||||||
|
final RolloutGroup dynamic1 = groups.get(0);
|
||||||
|
assertRollout(rollout, true, RolloutStatus.READY, 1, 0); // + dynamic
|
||||||
|
assertGroup(dynamic1, true, RolloutGroupStatus.READY, 0);
|
||||||
|
|
||||||
|
// add 4 targets for the first dynamic group, fill partially
|
||||||
|
testdataFactory.createTargets(targetPrefix, 0, 4);
|
||||||
|
// start rollout
|
||||||
|
rolloutManagement.start(rollout.getId());
|
||||||
|
|
||||||
|
// handleStartingRollout (no handleRunning called yet)
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, 1, 0);
|
||||||
|
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 0);
|
||||||
|
|
||||||
|
// partially fill the first dynamic (it is running and now create actions for 4 targets)
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, 1, 4);
|
||||||
|
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 4);
|
||||||
|
|
||||||
|
// fill first (2) and create fill partially the second (+2 new)
|
||||||
|
testdataFactory.createTargets(targetPrefix, 4, 4);
|
||||||
|
rolloutHandler.handleAll(); // fill first dynamic group and create a new dynamic2
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, 2, 6);
|
||||||
|
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 6);
|
||||||
|
groups = rolloutGroupManagement.findByRollout(
|
||||||
|
new OffsetBasedPageRequest(0, 10, Sort.by(Direction.ASC, "id")),
|
||||||
|
rollout.getId()).getContent();
|
||||||
|
final RolloutGroup dynamic2 = groups.get(1);
|
||||||
|
assertGroup(dynamic2, true, RolloutGroupStatus.SCHEDULED, 0);
|
||||||
|
|
||||||
|
// create scheduled actions for the dynamic2
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, 2, 6);
|
||||||
|
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 6);
|
||||||
|
assertGroup(dynamic2, true, RolloutGroupStatus.SCHEDULED, 0);
|
||||||
|
assertAndGetRunning(rollout, 6); // 6 from the first dynamic
|
||||||
|
assertScheduled(rollout, 0);
|
||||||
|
|
||||||
|
// executes dynamic1 without 1 target
|
||||||
|
assertAndGetRunning(rollout, 6)// 6 from the first dynamic
|
||||||
|
.stream()
|
||||||
|
// remove the last assigned to dynamic1 - it could be amountGroups * 3 + 2 or bigger by id
|
||||||
|
.filter(action -> Integer.parseInt(action.getTarget().getControllerId().substring(targetPrefix.length())) < 5)
|
||||||
|
.forEach(this::finishAction);
|
||||||
|
assertAndGetRunning(rollout, 1); // remains on in the first dynamic
|
||||||
|
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, 2, 6);
|
||||||
|
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 6);
|
||||||
|
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 0);
|
||||||
|
|
||||||
|
rolloutHandler.handleAll(); // add 2 action to now running second dynamic
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, 2, 8);
|
||||||
|
assertAndGetRunning(rollout, 3);
|
||||||
|
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 2);
|
||||||
|
|
||||||
|
testdataFactory.createTargets(targetPrefix, 8, 2);
|
||||||
|
rolloutManagement.pauseRollout(rollout.getId());
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
assertRollout(rollout, true, RolloutStatus.PAUSED, 2, 8);
|
||||||
|
assertAndGetRunning(rollout, 3);
|
||||||
|
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 2); // no new assignment
|
||||||
|
|
||||||
|
rolloutManagement.resumeRollout(rollout.getId());
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
assertRollout(rollout, true, RolloutStatus.RUNNING, 2, 10);
|
||||||
|
assertAndGetRunning(rollout, 5);
|
||||||
|
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 4); // assign the target created when paused
|
||||||
|
}
|
||||||
|
|
||||||
private void executeStaticWithoutOneTargetFromTheLastGroup(
|
private void executeStaticWithoutOneTargetFromTheLastGroup(
|
||||||
final List<RolloutGroup> groups,
|
final List<RolloutGroup> groups,
|
||||||
final Rollout rollout, final int amountGroups) {
|
final Rollout rollout, final int amountGroups) {
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
|||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetTagManagement;
|
import org.eclipse.hawkbit.repository.TargetTagManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetTypeManagement;
|
import org.eclipse.hawkbit.repository.TargetTypeManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.builder.DynamicRolloutGroupTemplate;
|
||||||
import org.eclipse.hawkbit.repository.builder.TagCreate;
|
import org.eclipse.hawkbit.repository.builder.TagCreate;
|
||||||
import org.eclipse.hawkbit.repository.builder.TargetCreate;
|
import org.eclipse.hawkbit.repository.builder.TargetCreate;
|
||||||
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
|
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
|
||||||
@@ -1239,6 +1240,14 @@ public class TestdataFactory {
|
|||||||
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
|
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 boolean confirmationRequired, final boolean dynamic) {
|
final Integer weight, final boolean confirmationRequired, final boolean dynamic) {
|
||||||
|
return createRolloutByVariables(rolloutName, rolloutDescription, groupSize, filterQuery, distributionSet,
|
||||||
|
successCondition, errorCondition, actionType, weight, confirmationRequired, dynamic, null);
|
||||||
|
}
|
||||||
|
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 Integer weight, final boolean confirmationRequired, final boolean dynamic,
|
||||||
|
final DynamicRolloutGroupTemplate dynamicRolloutGroupTemplate) {
|
||||||
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults()
|
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults()
|
||||||
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, successCondition)
|
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, successCondition)
|
||||||
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition)
|
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition)
|
||||||
@@ -1248,7 +1257,7 @@ public class TestdataFactory {
|
|||||||
entityFactory.rollout().create().name(rolloutName).description(rolloutDescription)
|
entityFactory.rollout().create().name(rolloutName).description(rolloutDescription)
|
||||||
.targetFilterQuery(filterQuery).distributionSetId(distributionSet).actionType(actionType).weight(weight)
|
.targetFilterQuery(filterQuery).distributionSetId(distributionSet).actionType(actionType).weight(weight)
|
||||||
.dynamic(dynamic),
|
.dynamic(dynamic),
|
||||||
groupSize, confirmationRequired, conditions);
|
groupSize, confirmationRequired, conditions, dynamicRolloutGroupTemplate);
|
||||||
|
|
||||||
// Run here, because Scheduler is disabled during tests
|
// Run here, because Scheduler is disabled during tests
|
||||||
rolloutHandler.handleAll();
|
rolloutHandler.handleAll();
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
|
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
|
||||||
|
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtDynamicRolloutGroupTemplate;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroup;
|
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroup;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
@@ -87,6 +88,10 @@ public class MgmtRolloutRestRequestBodyPost extends AbstractMgmtRolloutCondition
|
|||||||
@Schema(example = "true")
|
@Schema(example = "true")
|
||||||
private boolean dynamic;
|
private boolean dynamic;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@Schema(description = "Template for dynamic groups (only if dynamic flag is true)")
|
||||||
|
private MgmtDynamicRolloutGroupTemplate dynamicGroupTemplate;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@Schema(description = """
|
@Schema(description = """
|
||||||
(Available with user consent flow active) If the confirmation is required for this rollout. Value will be used
|
(Available with user consent flow active) If the confirmation is required for this rollout. Value will be used
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 Contributors to the Eclipse Foundation
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made
|
||||||
|
* available under the terms of the Eclipse Public License 2.0
|
||||||
|
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.mgmt.json.model.rolloutgroup;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model for defining the Attributes of a Rollout Group
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class MgmtDynamicRolloutGroupTemplate {
|
||||||
|
|
||||||
|
@Schema(description = "The name suffix of the dynamic groups", example = "-dynamic")
|
||||||
|
private String nameSuffix;
|
||||||
|
|
||||||
|
@Schema(description = "Count of targets a dynamic group shall include", example = "20")
|
||||||
|
private Long targetCount;
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
|
|||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.rollout.AbstractMgmtRolloutConditionsEntity;
|
import org.eclipse.hawkbit.mgmt.json.model.rollout.AbstractMgmtRolloutConditionsEntity;
|
||||||
@@ -26,12 +27,14 @@ import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutRestRequestBodyPos
|
|||||||
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutRestRequestBodyPut;
|
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutRestRequestBodyPut;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutSuccessAction;
|
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutSuccessAction;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutSuccessAction.SuccessAction;
|
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutSuccessAction.SuccessAction;
|
||||||
|
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtDynamicRolloutGroupTemplate;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroup;
|
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroup;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroupResponseBody;
|
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroupResponseBody;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi;
|
||||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||||
|
import org.eclipse.hawkbit.repository.builder.DynamicRolloutGroupTemplate;
|
||||||
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
|
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
|
||||||
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
||||||
import org.eclipse.hawkbit.repository.builder.RolloutUpdate;
|
import org.eclipse.hawkbit.repository.builder.RolloutUpdate;
|
||||||
@@ -157,12 +160,21 @@ final class MgmtRolloutMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static RolloutGroupCreate fromRequest(final EntityFactory entityFactory, final MgmtRolloutGroup restRequest) {
|
static RolloutGroupCreate fromRequest(final EntityFactory entityFactory, final MgmtRolloutGroup restRequest) {
|
||||||
|
|
||||||
return entityFactory.rolloutGroup().create().name(restRequest.getName())
|
return entityFactory.rolloutGroup().create().name(restRequest.getName())
|
||||||
.description(restRequest.getDescription()).targetFilterQuery(restRequest.getTargetFilterQuery())
|
.description(restRequest.getDescription()).targetFilterQuery(restRequest.getTargetFilterQuery())
|
||||||
.targetPercentage(restRequest.getTargetPercentage()).conditions(fromRequest(restRequest, false));
|
.targetPercentage(restRequest.getTargetPercentage()).conditions(fromRequest(restRequest, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DynamicRolloutGroupTemplate fromRequest(final MgmtDynamicRolloutGroupTemplate restRequest) {
|
||||||
|
if (restRequest == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DynamicRolloutGroupTemplate.builder()
|
||||||
|
.nameSuffix(Optional.ofNullable(restRequest.getNameSuffix()).orElse(""))
|
||||||
|
.targetCount(restRequest.getTargetCount())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
static RolloutGroupConditions fromRequest(final AbstractMgmtRolloutConditionsEntity restRequest,
|
static RolloutGroupConditions fromRequest(final AbstractMgmtRolloutConditionsEntity restRequest,
|
||||||
final boolean withDefaults) {
|
final boolean withDefaults) {
|
||||||
final RolloutGroupConditionBuilder conditions = new RolloutGroupConditionBuilder();
|
final RolloutGroupConditionBuilder conditions = new RolloutGroupConditionBuilder();
|
||||||
|
|||||||
@@ -146,8 +146,14 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
|
|||||||
final RolloutCreate create = MgmtRolloutMapper.fromRequest(entityFactory, rolloutRequestBody, distributionSet);
|
final RolloutCreate create = MgmtRolloutMapper.fromRequest(entityFactory, rolloutRequestBody, distributionSet);
|
||||||
final boolean confirmationFlowActive = tenantConfigHelper.isConfirmationFlowEnabled();
|
final boolean confirmationFlowActive = tenantConfigHelper.isConfirmationFlowEnabled();
|
||||||
|
|
||||||
Rollout rollout;
|
final Rollout rollout;
|
||||||
if (rolloutRequestBody.getGroups() != null) {
|
if (rolloutRequestBody.getGroups() != null) {
|
||||||
|
if (rolloutRequestBody.isDynamic()) {
|
||||||
|
throw new ValidationException("Dynamic rollouts are not supported with groups");
|
||||||
|
}
|
||||||
|
if (rolloutRequestBody.getAmountGroups() != null) {
|
||||||
|
throw new ValidationException("Either 'amountGroups' or 'groups' must be defined in the request");
|
||||||
|
}
|
||||||
final List<RolloutGroupCreate> rolloutGroups = rolloutRequestBody.getGroups().stream()
|
final List<RolloutGroupCreate> rolloutGroups = rolloutRequestBody.getGroups().stream()
|
||||||
.map(mgmtRolloutGroup -> {
|
.map(mgmtRolloutGroup -> {
|
||||||
final boolean confirmationRequired = isConfirmationRequiredForGroup(mgmtRolloutGroup,
|
final boolean confirmationRequired = isConfirmationRequiredForGroup(mgmtRolloutGroup,
|
||||||
@@ -156,14 +162,12 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
|
|||||||
.confirmationRequired(confirmationRequired);
|
.confirmationRequired(confirmationRequired);
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
rollout = rolloutManagement.create(create, rolloutGroups, rolloutGroupConditions);
|
rollout = rolloutManagement.create(create, rolloutGroups, rolloutGroupConditions);
|
||||||
|
|
||||||
} else if (rolloutRequestBody.getAmountGroups() != null) {
|
} else if (rolloutRequestBody.getAmountGroups() != null) {
|
||||||
final boolean confirmationRequired = rolloutRequestBody.getConfirmationRequired() == null
|
final boolean confirmationRequired = rolloutRequestBody.getConfirmationRequired() == null
|
||||||
? confirmationFlowActive
|
? confirmationFlowActive
|
||||||
: rolloutRequestBody.getConfirmationRequired();
|
: rolloutRequestBody.getConfirmationRequired();
|
||||||
rollout = rolloutManagement.create(create, rolloutRequestBody.getAmountGroups(), confirmationRequired,
|
rollout = rolloutManagement.create(create, rolloutRequestBody.getAmountGroups(), confirmationRequired,
|
||||||
rolloutGroupConditions);
|
rolloutGroupConditions, MgmtRolloutMapper.fromRequest(rolloutRequestBody.getDynamicGroupTemplate()));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new ValidationException("Either 'amountGroups' or 'groups' must be defined in the request");
|
throw new ValidationException("Either 'amountGroups' or 'groups' must be defined in the request");
|
||||||
}
|
}
|
||||||
@@ -332,8 +336,7 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
|
|||||||
final RolloutCreate create = MgmtRolloutMapper.fromRetriedRollout(entityFactory, rolloutForRetry);
|
final RolloutCreate create = MgmtRolloutMapper.fromRetriedRollout(entityFactory, rolloutForRetry);
|
||||||
final RolloutGroupConditions groupConditions = new RolloutGroupConditionBuilder().withDefaults().build();
|
final RolloutGroupConditions groupConditions = new RolloutGroupConditionBuilder().withDefaults().build();
|
||||||
|
|
||||||
final Rollout retriedRollout = rolloutManagement.create(create, 1, false,
|
final Rollout retriedRollout = rolloutManagement.create(create, 1, false, groupConditions, null);
|
||||||
groupConditions);
|
|
||||||
|
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body(MgmtRolloutMapper.toResponseRollout(retriedRollout, true));
|
return ResponseEntity.status(HttpStatus.CREATED).body(MgmtRolloutMapper.toResponseRollout(retriedRollout, true));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user