* [#1548] Add support for dynamic rollouts -- Current status -- Initial draft only !!!, to be improved TODO: * evaluate the target count - if update group/rollout total count fails dynamic updates could (?), actually, contain more targets * is it needed to break handler on group creating? * if dynamic group schedulers occur to be heavy - maybe a handler per tenant will ensure that one tenant won't break all *Concept for dynamic groups*: Rollouts are static and dynamic. Static rollouts consist of static groups only while dynamic rollouts have a number of static groups (first groups) and then an unlimited number of dynamic groups. Group targets assignments: * static groups include ALL matching targets created at the time the rollout was created, nevertheless they have active actions with bigger weight or not. Actions for the rollout and included targeets however are created at the start time. * dynamic groups however are filled in when started and consider the action weight. The targets included in a dynamic group are: * matching (filter and distribution set compatible) * not included in this or following rollout static groups (if already included in any of the following rollouts - it's intended to be overridden) * not in active actions of any rollouts with equal or bigger weight In general, when you create a rollout it contains all matching targets available at create time overriding any previous rollouts, actions, and so on. If the rollout is dynamic when its dynamic group becomes running it gets only matching targets that doesn't belong to static groups or have actions with great or equal weight Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com> * [#1548] Add 1000 weight for actions, rollouts and auto assignments without weight Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com> --------- Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -106,6 +106,7 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
|
||||
generateAction.setDistributionSet(dsA);
|
||||
generateAction.setStatus(Status.RUNNING);
|
||||
generateAction.setInitiatedBy(tenantAware.getCurrentUsername());
|
||||
generateAction.setWeight(1000);
|
||||
|
||||
final Action action = actionRepository.save(generateAction);
|
||||
|
||||
@@ -132,6 +133,7 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
|
||||
generateAction.setDistributionSet(dsA);
|
||||
generateAction.setStatus(Status.RUNNING);
|
||||
generateAction.setInitiatedBy(tenantAware.getCurrentUsername());
|
||||
generateAction.setWeight(1000);
|
||||
|
||||
final Action action = actionRepository.save(generateAction);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
@@ -86,6 +87,7 @@ public class ActionEventTest extends AbstractRemoteEntityEventTest<Action> {
|
||||
generateAction.setDistributionSet(distributionSet);
|
||||
generateAction.setStatus(Status.RUNNING);
|
||||
generateAction.setInitiatedBy(tenantAware.getCurrentUsername());
|
||||
generateAction.setWeight(1000);
|
||||
return actionRepository.save(generateAction);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ import javax.persistence.PersistenceContext;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
|
||||
@@ -40,6 +43,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
|
||||
@@ -52,6 +56,7 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
||||
import org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
@@ -59,6 +64,8 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ContextConfiguration(classes = {
|
||||
RepositoryApplicationConfiguration.class, TestConfiguration.class,
|
||||
TestSupportBinderAutoConfiguration.class })
|
||||
@@ -159,4 +166,42 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
|
||||
targets.stream().map(Target::getControllerId).collect(Collectors.toList()), type.getId());
|
||||
}
|
||||
|
||||
protected void assertRollout(final Rollout rollout, final boolean dynamic, final Rollout.RolloutStatus status, final int groupCreated, final long totalTargets) {
|
||||
final Rollout refreshed = refresh(rollout);
|
||||
assertThat(refreshed.isDynamic()).isEqualTo(dynamic);
|
||||
assertThat(refreshed.getStatus()).isEqualTo(status);
|
||||
assertThat(refreshed.getRolloutGroupsCreated()).isEqualTo(groupCreated);
|
||||
assertThat(refreshed.getTotalTargets()).isEqualTo(totalTargets);
|
||||
}
|
||||
|
||||
protected void assertGroup(final RolloutGroup group, final boolean dynamic, final RolloutGroup.RolloutGroupStatus status, final long totalTargets) {
|
||||
final RolloutGroup refreshed = refresh(group);
|
||||
assertThat(refreshed.isDynamic()).isEqualTo(dynamic);
|
||||
assertThat(refreshed.getStatus()).isEqualTo(status);
|
||||
assertThat(refreshed.getTotalTargets()).isEqualTo(totalTargets);
|
||||
}
|
||||
|
||||
protected Page<JpaAction> assertAndGetRunning(final Rollout rollout, final int count) {
|
||||
final Page<JpaAction> running = actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), Action.Status.RUNNING);
|
||||
assertThat(running.getTotalElements()).isEqualTo(count);
|
||||
return running;
|
||||
}
|
||||
|
||||
protected void assertScheduled(final Rollout rollout, final int count) {
|
||||
final Page<JpaAction> running = actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), Action.Status.SCHEDULED);
|
||||
assertThat(running.getTotalElements()).isEqualTo(count);
|
||||
}
|
||||
|
||||
protected void finishAction(final Action action) {
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(action.getId()).status(Action.Status.FINISHED));
|
||||
}
|
||||
|
||||
private JpaRollout refresh(final Rollout rollout) {
|
||||
return rolloutRepository.findById(rollout.getId()).get();
|
||||
}
|
||||
|
||||
protected JpaRolloutGroup refresh(final RolloutGroup group) {
|
||||
return rolloutGroupRepository.findById(group.getId()).get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
|
||||
assertThat(actions).hasSize(amountOfTargets);
|
||||
assertThat(actions).allMatch(action -> !action.getWeight().isPresent());
|
||||
assertThat(actions).allMatch(action -> action.getWeight().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -115,6 +115,7 @@ public class AutoActionCleanupTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
assertThat(actionRepository.count()).isEqualTo(3);
|
||||
|
||||
waitNextMillis();
|
||||
autoActionCleanup.run();
|
||||
|
||||
assertThat(actionRepository.count()).isEqualTo(1);
|
||||
|
||||
@@ -1101,6 +1101,9 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(actionId)
|
||||
.status(Action.Status.RUNNING).occurredAt(System.currentTimeMillis())
|
||||
.messages(Lists.newArrayList("proceeding message 1")));
|
||||
|
||||
final long createTime = System.currentTimeMillis();
|
||||
waitNextMillis();
|
||||
controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(actionId)
|
||||
.status(Action.Status.RUNNING).occurredAt(System.currentTimeMillis())
|
||||
.messages(Lists.newArrayList("proceeding message 2")));
|
||||
|
||||
@@ -972,18 +972,14 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@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) })
|
||||
void weightNotAllowedWhenMultiAssignmentModeNotEnabled() {
|
||||
void weightAllowedWhenMultiAssignmentModeNotEnabled() {
|
||||
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)));
|
||||
deploymentManagement.assignDistributionSets(Collections.singletonList(assignWithoutWeight));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* Copyright (c) 2023 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.jpa.management;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Junit tests for RolloutManagement.
|
||||
*/
|
||||
@Feature("Component Tests - Repository")
|
||||
@Story("Rollout Management (Flow)")
|
||||
class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@BeforeEach
|
||||
void reset() {
|
||||
this.approvalStrategy.setApprovalNeeded(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies a simple rollout flow")
|
||||
void rolloutFlow() {
|
||||
final String rolloutName = "rollout-std";
|
||||
final int amountGroups = 5; // static only
|
||||
final String targetPrefix = "controller-rollout-std-";
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
|
||||
|
||||
testdataFactory.createTargets(targetPrefix, 0, amountGroups * 3);
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30", false, false);
|
||||
final List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(
|
||||
new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id")),
|
||||
rollout.getId()).getContent();
|
||||
|
||||
// add 2 targets not to be included
|
||||
testdataFactory.createTargets(targetPrefix, amountGroups * 3, 2);
|
||||
// start rollout
|
||||
rolloutManagement.start(rollout.getId());
|
||||
|
||||
// handleStartingRollout (no handleRunning called yet)
|
||||
rolloutHandler.handleAll();
|
||||
assertRollout(rollout, false, RolloutStatus.RUNNING, amountGroups, amountGroups * 3);
|
||||
for (int i = 0; i < amountGroups; i++) {
|
||||
assertGroup(groups.get(i), false, i == 0 ? RolloutGroupStatus.RUNNING : RolloutGroupStatus.SCHEDULED, 3);
|
||||
}
|
||||
|
||||
// execute groups (without on of the last)
|
||||
assertThat(refresh(groups.get(0)).getStatus()).isEqualTo(RolloutGroupStatus.RUNNING);
|
||||
for (int i = 0; i < amountGroups; i++) {
|
||||
if (i + 1 != amountGroups) {
|
||||
assertThat(refresh(groups.get(i + 1)).getStatus()).isEqualTo(RolloutGroupStatus.SCHEDULED);
|
||||
}
|
||||
assertAndGetRunning(rollout, 3)
|
||||
.stream()
|
||||
.filter(action -> !(targetPrefix + (amountGroups * 3 - 1)).equals(action.getTarget().getControllerId()))
|
||||
.forEach(this::finishAction);
|
||||
rolloutHandler.handleAll();
|
||||
assertThat(refresh(groups.get(i)).getStatus()).isEqualTo(i + 1 == amountGroups ? RolloutGroupStatus.RUNNING : RolloutGroupStatus.FINISHED);
|
||||
if (i + 1 != amountGroups) {
|
||||
assertThat(refresh(groups.get(i + 1)).getStatus()).isEqualTo(RolloutGroupStatus.RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
rolloutManagement.pauseRollout(rollout.getId());
|
||||
rolloutHandler.handleAll();
|
||||
assertRollout(rollout, false, RolloutStatus.PAUSED, amountGroups, amountGroups * 3);
|
||||
assertAndGetRunning(rollout, 1); // keep running
|
||||
|
||||
rolloutManagement.resumeRollout(rollout.getId());
|
||||
rolloutHandler.handleAll();
|
||||
assertRollout(rollout, false, RolloutStatus.RUNNING, amountGroups, amountGroups * 3);
|
||||
assertAndGetRunning(rollout, 1); // keep running
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies a simple dynamic rollout flow")
|
||||
void dynamicRolloutFlow() {
|
||||
final String rolloutName = "dynamic-rollout-std";
|
||||
final int amountGroups = 5; // static only
|
||||
final String targetPrefix = "controller-dynamic-rollout-std-";
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
|
||||
|
||||
testdataFactory.createTargets(targetPrefix, 0, amountGroups * 3);
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30", false, true);
|
||||
|
||||
// rollout is READY
|
||||
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 2 targets for the first dynamic group
|
||||
testdataFactory.createTargets(targetPrefix, amountGroups * 3, 2);
|
||||
// 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);
|
||||
|
||||
// execute statics (without on of the last) which start dynamic
|
||||
assertThat(refresh(groups.get(0)).getStatus()).isEqualTo(RolloutGroupStatus.RUNNING);
|
||||
for (int i = 0; i < amountGroups; i++) {
|
||||
assertThat(refresh(groups.get(i + 1)).getStatus()).isEqualTo(RolloutGroupStatus.SCHEDULED);
|
||||
assertAndGetRunning(rollout, 3)
|
||||
.stream()
|
||||
.filter(action -> !(targetPrefix + (amountGroups * 3 - 1)).equals(action.getTarget().getControllerId()))
|
||||
.forEach(this::finishAction);
|
||||
rolloutHandler.handleAll();
|
||||
assertThat(refresh(groups.get(i)).getStatus()).isEqualTo(i + 1 == amountGroups ? RolloutGroupStatus.RUNNING : RolloutGroupStatus.FINISHED);
|
||||
assertThat(refresh(i + 1 == amountGroups ? dynamic1 : groups.get(i + 1)).getStatus()).isEqualTo(RolloutGroupStatus.RUNNING); // on last round check dynamic
|
||||
}
|
||||
|
||||
// partially fill the first dynamic (it is running and now create actions for 2 targets)
|
||||
rolloutHandler.handleAll();
|
||||
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 1, amountGroups * 3 + 2);
|
||||
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 2);
|
||||
|
||||
// fill first and create second
|
||||
testdataFactory.createTargets(targetPrefix, amountGroups * 3 + 2, 2);
|
||||
rolloutHandler.handleAll(); // fill first dynamic group and create a new dynamic2
|
||||
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 3);
|
||||
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 3);
|
||||
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 + 3);
|
||||
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 3);
|
||||
assertGroup(dynamic2, true, RolloutGroupStatus.SCHEDULED, 0);
|
||||
assertAndGetRunning(rollout, 4); // one from the last static group and 3 from the first dynamic
|
||||
assertScheduled(rollout, 0);
|
||||
|
||||
// executes last from static and dynamic1 without 1 target
|
||||
assertAndGetRunning(rollout, 4)// one from the last static and 3 for 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 + 2)
|
||||
.forEach(this::finishAction);
|
||||
assertAndGetRunning(rollout, 1); // remains on in the first dynamic
|
||||
|
||||
|
||||
rolloutHandler.handleAll();
|
||||
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 3);
|
||||
assertGroup(groups.get(amountGroups - 1), false, RolloutGroupStatus.FINISHED, 3);
|
||||
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 3);
|
||||
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 0);
|
||||
|
||||
rolloutHandler.handleAll(); // add 1 action to now running second dynamic
|
||||
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 4);
|
||||
assertAndGetRunning(rollout, 2);
|
||||
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 1);
|
||||
|
||||
testdataFactory.createTargets(targetPrefix, amountGroups * 3 + 4, 1);
|
||||
rolloutManagement.pauseRollout(rollout.getId());
|
||||
rolloutHandler.handleAll();
|
||||
assertRollout(rollout, true, RolloutStatus.PAUSED, amountGroups + 2, amountGroups * 3 + 4);
|
||||
assertAndGetRunning(rollout, 2);
|
||||
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 1); // no new assignment
|
||||
|
||||
rolloutManagement.resumeRollout(rollout.getId());
|
||||
rolloutHandler.handleAll();
|
||||
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 5);
|
||||
assertAndGetRunning(rollout, 3);
|
||||
assertGroup(dynamic2, true, RolloutGroupStatus.RUNNING, 2); // assign the target created when paused
|
||||
}
|
||||
}
|
||||
@@ -440,11 +440,6 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
}
|
||||
|
||||
private void finishAction(final Action action) {
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(action.getId()).status(Status.FINISHED));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifying that the error handling action of a group is executed to pause the current rollout")
|
||||
void checkErrorHitOfGroupCallsErrorActionToPauseTheRollout() {
|
||||
@@ -1432,7 +1427,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
final RolloutGroupCreate group1 = entityFactory.rolloutGroup().create().conditions(conditions).name("group1")
|
||||
.targetPercentage(50.0F);
|
||||
final RolloutGroupCreate group2 = entityFactory.rolloutGroup().create().conditions(conditions).name("group2")
|
||||
.targetPercentage(100.0F);
|
||||
.targetPercentage(50.0F);
|
||||
|
||||
// group1 exceeds the quota
|
||||
assertThatExceptionOfType(AssignmentQuotaExceededException.class).isThrownBy(() -> rolloutManagement.create(
|
||||
@@ -1456,9 +1451,9 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
final RolloutGroupCreate group5 = entityFactory.rolloutGroup().create().conditions(conditions).name("group5")
|
||||
.targetPercentage(33.3F);
|
||||
final RolloutGroupCreate group6 = entityFactory.rolloutGroup().create().conditions(conditions).name("group6")
|
||||
.targetPercentage(66.6F);
|
||||
.targetPercentage(33.3F);
|
||||
final RolloutGroupCreate group7 = entityFactory.rolloutGroup().create().conditions(conditions).name("group7")
|
||||
.targetPercentage(100.0F);
|
||||
.targetPercentage(33.3F);
|
||||
|
||||
// should work fine
|
||||
assertThat(rolloutManagement.create(
|
||||
@@ -1531,17 +1526,17 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
final int amountTargetsInGroup1 = 10;
|
||||
final int percentTargetsInGroup1 = 100;
|
||||
|
||||
final int amountTargetsInGroup1and2 = 20;
|
||||
final int amountTargetsInGroup2and3 = 20;
|
||||
final int percentTargetsInGroup2 = 20;
|
||||
final int percentTargetsInGroup3 = 100;
|
||||
final int percentTargetsInGroup3 = 80;
|
||||
|
||||
final int countTargetsInGroup2 = (int) Math
|
||||
.ceil((double) percentTargetsInGroup2 / 100 * amountTargetsInGroup1and2);
|
||||
final int countTargetsInGroup3 = amountTargetsInGroup1and2 - countTargetsInGroup2;
|
||||
.ceil((double) percentTargetsInGroup2 / 100 * amountTargetsInGroup2and3);
|
||||
final int countTargetsInGroup3 = amountTargetsInGroup2and3 - countTargetsInGroup2;
|
||||
|
||||
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults().build();
|
||||
// Generate Targets for group 2 and 3 and generate the Rollout
|
||||
final RolloutCreate rolloutcreate = generateTargetsAndRollout(rolloutName, amountTargetsInGroup1and2);
|
||||
final RolloutCreate rolloutcreate = generateTargetsAndRollout(rolloutName, amountTargetsInGroup2and3);
|
||||
|
||||
// Generate Targets for group 1
|
||||
testdataFactory.createTargets(amountTargetsInGroup1, rolloutName + "-gr1-", rolloutName);
|
||||
@@ -1568,7 +1563,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
myRollout = getRollout(myRollout.getId());
|
||||
assertThat(myRollout.getStatus()).isEqualTo(RolloutStatus.READY);
|
||||
assertThat(myRollout.getTotalTargets()).isEqualTo(amountTargetsInGroup1and2 + amountTargetsInGroup1);
|
||||
assertThat(myRollout.getTotalTargets()).isEqualTo(amountTargetsInGroup2and3 + amountTargetsInGroup1);
|
||||
|
||||
final List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(PAGE, myRollout.getId()).getContent();
|
||||
|
||||
@@ -1979,11 +1974,10 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Creating a rollout with a weight causes an error when multi assignment in disabled.")
|
||||
void weightNotAllowedWhenMultiAssignmentModeNotEnabled() {
|
||||
Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class)
|
||||
.isThrownBy(() -> testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 2, "50",
|
||||
void weightAllowedWhenMultiAssignmentModeNotEnabled() {
|
||||
testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 2, "50",
|
||||
"80",
|
||||
ActionType.FORCED, 66));
|
||||
ActionType.FORCED, 66);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2028,7 +2022,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Rollout can be created without weight in single assignment and be started in multi assignment")
|
||||
void createInSingleStartInMultiassigMode() {
|
||||
void createInSingleStartInMultiassignMode() {
|
||||
final int amountOfTargets = 5;
|
||||
final Long rolloutId = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet(amountOfTargets, 2,
|
||||
amountOfTargets,
|
||||
@@ -2038,7 +2032,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
rolloutManagement.start(rolloutId);
|
||||
rolloutHandler.handleAll();
|
||||
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
|
||||
assertThat(actions).hasSize(amountOfTargets).allMatch(action -> !action.getWeight().isPresent());
|
||||
// wight replaced with default
|
||||
assertThat(actions).hasSize(5).allMatch(action -> action.getWeight().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2319,4 +2314,66 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
.isThrownBy(() -> rolloutManagement.triggerNextGroup(createdRollout.getId()))
|
||||
.withMessageContaining(errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests static assignment aspects of the dynamic group assignment filters.
|
||||
*/
|
||||
@Test
|
||||
@Description("Dynamic group doesn't override newer static group assignments")
|
||||
public void dynamicGroupDoesntOverrideItsOrNewerStaticGroups() {
|
||||
final int amountGroups = 1; // static only
|
||||
final String targetPrefix = "controller-dynamic-rollout-";
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet("ds");
|
||||
|
||||
testdataFactory.createTargets(targetPrefix, 0, amountGroups * 2);
|
||||
final Rollout dynamicRollout = testdataFactory.createRolloutByVariables("dynamic", "static rollout", amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "0", "30", ActionType.FORCED, 1000, false, true);
|
||||
rolloutManagement.start(dynamicRollout.getId());
|
||||
rolloutHandler.handleAll();
|
||||
assertRollout(dynamicRollout, true, RolloutStatus.RUNNING, amountGroups + 1, amountGroups * 2);
|
||||
final List<RolloutGroup> dynamicGroups = rolloutGroupManagement.findByRollout(
|
||||
new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id")),
|
||||
dynamicRollout.getId()).getContent();
|
||||
for (int i = 0; i < dynamicGroups.size(); i++) {
|
||||
final RolloutGroup group = dynamicGroups.get(i);
|
||||
if (i + 1 == dynamicGroups.size()) {
|
||||
assertGroup(group, true, RolloutGroupStatus.SCHEDULED, 0);
|
||||
} else {
|
||||
assertGroup(group, false, RolloutGroupStatus.RUNNING, 2);
|
||||
}
|
||||
}
|
||||
assertAndGetRunning(dynamicRollout, 2).forEach(this::finishAction);
|
||||
rolloutHandler.handleAll();
|
||||
for (int i = 0; i < dynamicGroups.size(); i++) {
|
||||
final RolloutGroup group = dynamicGroups.get(i);
|
||||
if (i + 1 == dynamicGroups.size()) {
|
||||
assertGroup(group, true, RolloutGroupStatus.RUNNING, 0);
|
||||
} else {
|
||||
assertGroup(group, false, RolloutGroupStatus.FINISHED, 2);
|
||||
}
|
||||
}
|
||||
assertAndGetRunning(dynamicRollout, 0);
|
||||
rolloutHandler.handleAll();
|
||||
// NB: asserts that dynamic group doesn't get from its static groups (already finished action targets)
|
||||
assertGroup(dynamicGroups.get(dynamicGroups.size() - 1), true, RolloutGroupStatus.RUNNING, 0);
|
||||
assertAndGetRunning(dynamicRollout, 0);
|
||||
rolloutManagement.pauseRollout(dynamicRollout.getId());
|
||||
rolloutHandler.handleAll();
|
||||
|
||||
testdataFactory.createTargets(targetPrefix, amountGroups * 2, amountGroups);
|
||||
final Rollout staticRollout = testdataFactory.createRolloutByVariables("static", "static rollout", amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "0", "30", ActionType.FORCED, 0, false, false);
|
||||
rolloutManagement.start(staticRollout.getId());
|
||||
rolloutHandler.handleAll();
|
||||
assertRollout(staticRollout, false, RolloutStatus.RUNNING, amountGroups, amountGroups * 3);
|
||||
final List<RolloutGroup> staticGroups = rolloutGroupManagement.findByRollout(
|
||||
new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id")),
|
||||
staticRollout.getId()).getContent();
|
||||
staticGroups.forEach(group -> assertGroup(group, false, RolloutGroupStatus.RUNNING, 3));
|
||||
|
||||
rolloutManagement.resumeRollout(dynamicRollout.getId());
|
||||
rolloutHandler.handleAll(); // resume, do not get last devices (they are assigned to a newer group, nevertheless newer is with bigger weight
|
||||
assertGroup(dynamicGroups.get(dynamicGroups.size() - 1), true, RolloutGroupStatus.RUNNING, 0);
|
||||
assertAndGetRunning(dynamicRollout, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,17 +463,15 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
|
||||
|
||||
@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() {
|
||||
public void weightAllowedWhenMultiAssignmentModeNotEnabled() {
|
||||
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)));
|
||||
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create()
|
||||
.name("b").query("name==*").autoAssignDistributionSet(ds).autoAssignWeight(342));
|
||||
targetFilterQueryManagement.updateAutoAssignDS(
|
||||
entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(ds.getId()).weight(343));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -53,13 +53,17 @@ import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
@@ -1328,6 +1332,65 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
"name==*")).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests action based aspects of the dynamic group assignment filters.
|
||||
*/
|
||||
@Test
|
||||
@Description("Target matches filter no active action with ge weight.")
|
||||
void findByNotInGEGroupAndNotInActiveActionGEWeightOrInRolloutAndTargetFilterQueryAndCompatibleAndUpdatable() {
|
||||
final String targetPrefix = "dyn_action_filter_";
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
final List<Target> targets = testdataFactory.createTargets(targetPrefix, 9);
|
||||
final Rollout rolloutOlder = testdataFactory.createRollout();
|
||||
final Rollout rollout = testdataFactory.createRollout();
|
||||
final Rollout rolloutNewer = testdataFactory.createRollout();
|
||||
|
||||
// old ro with less weight - match
|
||||
createAction(targets.get(0), rolloutOlder, 0, Status.RUNNING, distributionSet);
|
||||
// old ro with less weight - match
|
||||
createAction(targets.get(1), rolloutOlder, 5, Status.SCHEDULED, distributionSet);
|
||||
// old ro with equal weight - match
|
||||
createAction(targets.get(2), rolloutOlder, 10, Status.RUNNING, distributionSet);
|
||||
// old ro with BIGGER weight - doesn't match
|
||||
createAction(targets.get(3), rolloutOlder, 20, Status.WAIT_FOR_CONFIRMATION, distributionSet);
|
||||
// same ro - doesn't match
|
||||
createAction(targets.get(4), rollout, 10, Status.RUNNING, distributionSet);
|
||||
// new ro with less weight - match
|
||||
createAction(targets.get(5), rolloutNewer, 0, Status.RUNNING, distributionSet);
|
||||
// new ro with less weight - match
|
||||
createAction(targets.get(6), rolloutNewer, 5, Status.WARNING, distributionSet);
|
||||
// NEW ro with EQUAL weight - doesn't match
|
||||
createAction(targets.get(7), rolloutNewer, 10, Status.RUNNING, distributionSet);
|
||||
// new ro with BIGGER weight - doesn't match
|
||||
createAction(targets.get(8), rolloutNewer, 20, Status.DOWNLOADED, distributionSet);
|
||||
|
||||
final Slice<Target> matching = targetManagement.findByNotInGEGroupAndNotInActiveActionGEWeightOrInRolloutAndTargetFilterQueryAndCompatibleAndUpdatable(
|
||||
PAGE, rollout.getId(), 10, Long.MAX_VALUE,"controllerid==dyn_action_filter_*", distributionSet.getType());
|
||||
|
||||
assertThat(matching.getNumberOfElements()).isEqualTo(5);
|
||||
assertThat(matching.stream()
|
||||
.map(Target::getControllerId)
|
||||
.map(s -> s.substring(targetPrefix.length()))
|
||||
.map(Integer::parseInt)
|
||||
.sorted()
|
||||
.toList()).isEqualTo(List.of(0, 1, 2, 5, 6));
|
||||
}
|
||||
private void createAction(final Target target, final Rollout rollout, final Integer weight, final Action.Status status, final DistributionSet distributionSet) {
|
||||
final JpaAction action = new JpaAction();
|
||||
action.setActionType(Action.ActionType.FORCED);
|
||||
action.setTarget(target);
|
||||
action.setInitiatedBy("test");
|
||||
if (rollout != null) {
|
||||
action.setRollout(rollout);
|
||||
}
|
||||
if (weight != null) {
|
||||
action.setWeight(weight);
|
||||
}
|
||||
action.setStatus(status);
|
||||
action.setDistributionSet(distributionSet);
|
||||
actionRepository.save(action);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Target matches filter for not existing DS.")
|
||||
void matchesFilterDsNotExists() {
|
||||
|
||||
Reference in New Issue
Block a user