Support user consent flow (#1293)

* Introduce user consent flow
* Add permissions to confirmation management
* rename from consent to confirmation
* Reformat code. Remove unused imports. Change and add permission checks when configuring auto-confirmation.
* Do not include null values for DDI confirmation base endpoint
* fix confirmation required checkbox id
* Remove unused import. Fix consume/produce type of new API's.
* Change term processing to proceeding when activating user consent flow
* Align formatting and extend integration test cases for DMF and DDI.
* Extend DMF test cases to consider auto-confirmation
* Refactor action management to fix problem of handling action status updates on closed actions.
* remove unsupported validation
* use new confirmation api for DMF. Extend test cases.,
* Remove unnecessary fields.
* Extend API documentation for DDI and MGMT API.
* adapt ddi api docs adoc file
* Fixed the duplicate migration version for db files
* fix method to support confirmation
* Fixed PR comments
* Addressed PR comments
* Fixed after merge compilation issue
* Fixed after merge compilation issue
* Fix failing tests in MgmtRolloutResourceTest
* Fixed the permissions issue reflected by integration tests
* Added back the missing line of code lost during merge
* Fix the failing test on Jenkins

Signed-off-by: Stanislav Trailov <stanislav.trailov@bosch.io>
Signed-off-by: Dimitar Shterev <dimitar.shterev@bosch.io>
Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>
Signed-off-by: Shruthi Manavalli Ramanna <shruthimanavalli.ramanna@bosch-si.com>
Co-authored-by: Shruthi Manavalli Ramanna <shruthimanavalli.ramanna@bosch-si.com>
This commit is contained in:
Michael Herdt
2023-01-25 12:11:05 +01:00
committed by GitHub
parent b919ceda5c
commit 21f1569881
208 changed files with 7830 additions and 831 deletions

View File

@@ -29,6 +29,7 @@ import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.exception.SpServerError;
@@ -54,6 +55,9 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
@@ -143,7 +147,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
// create Targets
final String[] knownTargetIds = new String[] { "1", "2" };
final JSONArray list = createTargetAndJsonArray(null, null, null, null, knownTargetIds);
final JSONArray list = createTargetAndJsonArray(null, null, null, null, null, knownTargetIds);
// assign DisSet to target and test assignment
assignDistributionSet(disSet.getId(), knownTargetIds[0]);
mvc.perform(
@@ -257,7 +261,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
// prepare targets
final String[] knownTargetIds = new String[] { "1", "2", "3", "4", "5" };
final JSONArray list = createTargetAndJsonArray(null, null, null, null, knownTargetIds);
final JSONArray list = createTargetAndJsonArray(null, null, null, null, null, knownTargetIds);
// assign already one target to DS
assignDistributionSet(createdDs.getId(), knownTargetIds[0]);
@@ -378,7 +382,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final DistributionSet createdDs = testdataFactory.createDistributionSet();
// prepare targets
final String[] knownTargetIds = new String[] { "1", "2", "3", "4", "5" };
final JSONArray list = createTargetAndJsonArray(getTestSchedule(0), null, null, null, knownTargetIds);
final JSONArray list = createTargetAndJsonArray(getTestSchedule(0), null, null, null, null, knownTargetIds);
// assign already one target to DS
assignDistributionSet(createdDs.getId(), knownTargetIds[0]);
@@ -395,7 +399,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final DistributionSet createdDs = testdataFactory.createDistributionSet();
// prepare targets
final String[] knownTargetIds = new String[] { "1", "2", "3", "4", "5" };
final JSONArray list = createTargetAndJsonArray(null, getTestDuration(10), null, null, knownTargetIds);
final JSONArray list = createTargetAndJsonArray(null, getTestDuration(10), null, null, null, knownTargetIds);
// assign already one target to DS
assignDistributionSet(createdDs.getId(), knownTargetIds[0]);
@@ -413,7 +417,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
// prepare targets
final String[] knownTargetIds = new String[] { "1", "2", "3", "4", "5" };
final JSONArray list = createTargetAndJsonArray(getTestSchedule(10), getTestDuration(10), getTestTimeZone(),
null, knownTargetIds);
null, null, knownTargetIds);
// assign already one target to DS
assignDistributionSet(createdDs.getId(), knownTargetIds[0]);
@@ -431,7 +435,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
// prepare targets
final String[] knownTargetIds = new String[] { "1", "2", "3", "4", "5" };
final JSONArray list = createTargetAndJsonArray(getTestSchedule(-30), getTestDuration(5), getTestTimeZone(),
null, knownTargetIds);
null, null, knownTargetIds);
// assign already one target to DS
assignDistributionSet(createdDs.getId(), knownTargetIds[0]);
@@ -474,7 +478,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final DistributionSet createdDs = testdataFactory.createDistributionSet();
final String[] knownTargetIds = new String[] { "1", "2", "3" };
final JSONArray assignTargetJson = createTargetAndJsonArray(null, null, null, "forced", knownTargetIds);
final JSONArray assignTargetJson = createTargetAndJsonArray(null, null, null, "forced", null, knownTargetIds);
assignDistributionSet(createdDs.getId(), knownTargetIds[0]);
assignTargetJson.put(new JSONObject().put("id", "notexistingtarget").put("type", "forced"));
@@ -487,7 +491,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
}
private JSONArray createTargetAndJsonArray(final String schedule, final String duration, final String timezone,
final String type, final String... targetIds) throws Exception {
final String type, final Boolean confirmationRequired, final String... targetIds) throws Exception {
final JSONArray result = new JSONArray();
for (final String targetId : targetIds) {
testdataFactory.createTarget(targetId);
@@ -501,7 +505,6 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
if (schedule != null || duration != null || timezone != null) {
final JSONObject maintenanceJsonObject = new JSONObject();
targetJsonObject.put("maintenanceWindow", maintenanceJsonObject);
if (schedule != null) {
maintenanceJsonObject.put("schedule", schedule);
}
@@ -511,6 +514,10 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
if (timezone != null) {
maintenanceJsonObject.put("timezone", timezone);
}
targetJsonObject.put("maintenanceWindow", maintenanceJsonObject);
}
if (confirmationRequired != null) {
targetJsonObject.put("confirmationRequired", confirmationRequired);
}
}
return result;
@@ -1263,7 +1270,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
// prepare targets
final String[] knownTargetIds = new String[] { "1", "2", "3", "4", "5" };
final JSONArray list = createTargetAndJsonArray(null, null, null, null, knownTargetIds);
final JSONArray list = createTargetAndJsonArray(null, null, null, null, null, knownTargetIds);
// assign already one target to DS
assignDistributionSet(createdDs.getId(), knownTargetIds[0], Action.ActionType.DOWNLOAD_ONLY);
@@ -1277,6 +1284,41 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
.as("Five targets in repository have DS assigned").hasSize(5);
}
@ParameterizedTest
@MethodSource("confirmationOptions")
@Description("Ensures that confirmation option is considered in assignment request.")
public void assignTargetsToDistributionSetWithConfirmationOptions(final boolean confirmationFlowActive,
final Boolean confirmationRequired) throws Exception {
final DistributionSet createdDs = testdataFactory.createDistributionSet();
if (confirmationFlowActive) {
enableConfirmationFlow();
}
// prepare targets
final String targetId = "1";
final JSONArray list = createTargetAndJsonArray(null, null, null, null, confirmationRequired, targetId);
mvc.perform(post("/rest/v1/distributionsets/{ds}/assignedTargets", createdDs.getId())
.contentType(MediaType.APPLICATION_JSON).content(list.toString())).andExpect(status().isOk())
.andExpect(jsonPath("$.assigned", equalTo(1))).andExpect(jsonPath("$.alreadyAssigned", equalTo(0)))
.andExpect(jsonPath("$.total", equalTo(1)));
assertThat(deploymentManagement.findActionsByDistributionSet(PAGE, createdDs.getId()).getContent()).hasSize(1)
.allMatch(action -> {
if (!confirmationFlowActive) {
return !action.isWaitingConfirmation();
}
return confirmationRequired == null ? action.isWaitingConfirmation()
: confirmationRequired == action.isWaitingConfirmation();
});
}
private static Stream<Arguments> confirmationOptions() {
return Stream.of(Arguments.of(true, true), Arguments.of(true, false), Arguments.of(false, true),
Arguments.of(false, false), Arguments.of(true, null), Arguments.of(false, null));
}
@Test
@Description("A request for assigning a target multiple times results in a Bad Request when multiassignment is disabled.")
public void multiassignmentRequestNotAllowedIfDisabled() throws Exception {

View File

@@ -27,6 +27,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import org.awaitility.Awaitility;
@@ -52,6 +53,10 @@ import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort.Direction;
@@ -209,7 +214,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
final RolloutGroupConditions rolloutGroupConditions = new RolloutGroupConditionBuilder().withDefaults().build();
mvc.perform(post("/rest/v1/rollouts")
.content(JsonBuilder.rollout("rollout2", "desc", null, dsA.getId(), "id==ro-target*",
.content(JsonBuilder.rolloutWithGroups("rollout2", "desc", null, dsA.getId(), "id==ro-target*",
rolloutGroupConditions, rolloutGroups))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated()).andReturn();
@@ -239,7 +244,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
final RolloutGroupConditions rolloutGroupConditions = new RolloutGroupConditionBuilder().withDefaults().build();
mvc.perform(post("/rest/v1/rollouts")
.content(JsonBuilder.rollout("rollout4", "desc", null, dsA.getId(), "id==ro-target*",
.content(JsonBuilder.rolloutWithGroups("rollout4", "desc", null, dsA.getId(), "id==ro-target*",
rolloutGroupConditions, rolloutGroups))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest())
@@ -270,7 +275,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
final RolloutGroupConditions rolloutGroupConditions = new RolloutGroupConditionBuilder().withDefaults().build();
mvc.perform(post("/rest/v1/rollouts")
.content(JsonBuilder.rollout("rollout4", "desc", null, dsA.getId(), "id==ro-target*",
.content(JsonBuilder.rolloutWithGroups("rollout4", "desc", null, dsA.getId(), "id==ro-target*",
rolloutGroupConditions, rolloutGroups))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest())
@@ -296,13 +301,11 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
final DistributionSet dsA = testdataFactory.createDistributionSet("");
// create rollout including the created targets with prefix 'rollout'
final Rollout rollout = rolloutManagement.create(entityFactory.rollout()
.create()
.name("rollout1")
.set(dsA.getId())
.targetFilterQuery("controllerId==rollout*"), 4, new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100")
.build());
final Rollout rollout = rolloutManagement.create(
entityFactory.rollout().create().name("rollout1").set(dsA.getId())
.targetFilterQuery("controllerId==rollout*"),
4, false, new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build());
retrieveAndVerifyRolloutInCreating(dsA, rollout);
retrieveAndVerifyRolloutInReady(rollout);
@@ -310,6 +313,134 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
retrieveAndVerifyRolloutInRunning(rollout);
}
@ParameterizedTest
@ValueSource(booleans = { true, false })
@Description("Verify the confirmation required flag is not part of the rollout parent entity")
void verifyConfirmationFlagIsNeverPartOfRolloutEntity(final boolean confirmationFlowActive) throws Exception {
testdataFactory.createTargets(20, "rollout", "rollout");
final DistributionSet dsA = testdataFactory.createDistributionSet("");
if (confirmationFlowActive) {
enableConfirmationFlow();
}
// create rollout including the created targets with prefix 'rollout'
final Rollout rollout = rolloutManagement.create(
entityFactory.rollout().create().name("rollout1").set(dsA.getId())
.targetFilterQuery("controllerId==rollout*"),
4, false, new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build());
mvc.perform(get("/rest/v1/rollouts/" + rollout.getId()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.id", equalTo(rollout.getId().intValue())))
.andExpect(jsonPath("$.confirmationRequired").doesNotExist());
}
@ParameterizedTest
@ValueSource(booleans = { true, false })
@Description("Verify the confirmation required flag will be set based on the feature state")
void verifyConfirmationStateIfNotProvided(final boolean confirmationFlowActive) throws Exception {
if (confirmationFlowActive) {
enableConfirmationFlow();
}
testdataFactory.createTargets(20, "target", "rollout");
final DistributionSet dsA = testdataFactory.createDistributionSet("");
postRollout("rollout1", 5, dsA.getId(), "id==target*", 20, Action.ActionType.FORCED);
final List<Rollout> content = rolloutManagement.findAll(PAGE, false).getContent();
assertThat(content).hasSizeGreaterThan(0).allSatisfy(rollout -> {
assertThat(rolloutGroupManagement.findByRollout(PAGE, rollout.getId()))
.describedAs("Confirmation required flag depends on feature active.")
.allMatch(group -> group.isConfirmationRequired() == confirmationFlowActive);
});
}
@Test
@Description("Confirmation required flag will be read from the Rollout, if specified.")
void verifyRolloutGroupWillUseRolloutPropertyFirst() throws Exception {
enableConfirmationFlow();
final DistributionSet dsA = testdataFactory.createDistributionSet("ro");
final int amountTargets = 10;
testdataFactory.createTargets(amountTargets, "ro-target", "rollout");
final float percentTargetsInGroup1 = 20;
final float percentTargetsInGroup2 = 100;
final RolloutGroupConditions rolloutGroupConditions = new RolloutGroupConditionBuilder().withDefaults().build();
final List<String> rolloutGroups = Arrays.asList(
JsonBuilder.rolloutGroup("Group1", "Group1desc", null, percentTargetsInGroup1, true,
rolloutGroupConditions),
JsonBuilder.rolloutGroup("Group2", "Group1desc", null, percentTargetsInGroup2, null,
rolloutGroupConditions));
mvc.perform(post("/rest/v1/rollouts")
.content(JsonBuilder.rollout("rollout2", "desc", null, dsA.getId(), "id==ro-target*",
rolloutGroupConditions, rolloutGroups, null, null, false))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated()).andReturn();
final List<Rollout> content = rolloutManagement.findAll(PAGE, false).getContent();
assertThat(content).hasSize(1).allSatisfy(rollout -> {
final List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(PAGE, rollout.getId()).getContent();
assertThat(groups).hasSize(2).allMatch(group -> {
if (group.getName().equals("Group1")) {
return group.isConfirmationRequired();
} else if (group.getName().equals("Group2")) {
return !group.isConfirmationRequired();
}
return false;
});
});
}
@Test
@Description("Confirmation required flag will be read from the tenant config (confirmation flow state), if never specified.")
void verifyRolloutGroupWillUseConfigIfNotProvidedWithRollout() throws Exception {
enableConfirmationFlow();
final DistributionSet dsA = testdataFactory.createDistributionSet("ro");
final int amountTargets = 10;
testdataFactory.createTargets(amountTargets, "ro-target", "rollout");
final float percentTargetsInGroup1 = 20;
final float percentTargetsInGroup2 = 100;
final RolloutGroupConditions rolloutGroupConditions = new RolloutGroupConditionBuilder().withDefaults().build();
final List<String> rolloutGroups = Arrays.asList(
JsonBuilder.rolloutGroup("Group1", "Group1desc", null, percentTargetsInGroup1, false,
rolloutGroupConditions),
JsonBuilder.rolloutGroup("Group2", "Group1desc", null, percentTargetsInGroup2, null,
rolloutGroupConditions));
mvc.perform(post("/rest/v1/rollouts")
.content(JsonBuilder.rollout("rollout2", "desc", null, dsA.getId(), "id==ro-target*",
rolloutGroupConditions, rolloutGroups, null, null, null))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated()).andReturn();
final List<Rollout> content = rolloutManagement.findAll(PAGE, false).getContent();
assertThat(content).hasSize(1).allSatisfy(rollout -> {
final List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(PAGE, rollout.getId()).getContent();
assertThat(groups).hasSize(2).allMatch(group -> {
if (group.getName().equals("Group1")) {
return !group.isConfirmationRequired();
} else if (group.getName().equals("Group2")) {
return group.isConfirmationRequired();
}
return false;
});
});
}
@Step
private void retrieveAndVerifyRolloutInRunning(final Rollout rollout) throws Exception {
rolloutManagement.handleRollouts();
@@ -459,16 +590,22 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
.andExpect(jsonPath("$.content", hasSize(1))).andExpect(jsonPath("$.total", equalTo(2)));
}
@Test
@ParameterizedTest
@MethodSource("confirmationOptions")
@Description("Testing that rollout paged list is limited by the query param limit")
void retrieveRolloutGroupsForSpecificRollout() throws Exception {
void retrieveRolloutGroupsForSpecificRollout(final boolean confirmationFlowEnabled, final boolean confirmationRequired) throws Exception {
// setup
final int amountTargets = 20;
testdataFactory.createTargets(amountTargets, "rollout", "rollout");
final DistributionSet dsA = testdataFactory.createDistributionSet("");
if (confirmationFlowEnabled) {
enableConfirmationFlow();
}
// create rollout including the created targets with prefix 'rollout'
final Rollout rollout = createRollout("rollout1", 4, dsA.getId(), "controllerId==rollout*");
final Rollout rollout = createRollout("rollout1", 4, dsA.getId(), "controllerId==rollout*",
confirmationRequired);
// retrieve rollout groups from created rollout
mvc.perform(
@@ -479,7 +616,19 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
.andExpect(jsonPath("$.content[0].status", equalTo("ready")))
.andExpect(jsonPath("$.content[1].status", equalTo("ready")))
.andExpect(jsonPath("$.content[2].status", equalTo("ready")))
.andExpect(jsonPath("$.content[3].status", equalTo("ready")));
.andExpect(jsonPath("$.content[3].status", equalTo("ready")))
.andExpect(isConfirmationFlowEnabled()
? jsonPath("$.content[0].confirmationRequired", equalTo(confirmationRequired))
: jsonPath("confirmationRequired").doesNotExist())
.andExpect(isConfirmationFlowEnabled()
? jsonPath("$.content[1].confirmationRequired", equalTo(confirmationRequired))
: jsonPath("confirmationRequired").doesNotExist())
.andExpect(isConfirmationFlowEnabled()
? jsonPath("$.content[2].confirmationRequired", equalTo(confirmationRequired))
: jsonPath("confirmationRequired").doesNotExist())
.andExpect(isConfirmationFlowEnabled()
? jsonPath("$.content[3].confirmationRequired", equalTo(confirmationRequired))
: jsonPath("confirmationRequired").doesNotExist());
}
@Test
@@ -648,20 +797,25 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
.andExpect(jsonPath("$.content[1].status", equalTo("scheduled")));
}
@Test
@ParameterizedTest
@MethodSource("confirmationOptions")
@Description("Testing that a single rollout group can be retrieved")
void retrieveSingleRolloutGroup() throws Exception {
void retrieveSingleRolloutGroup(final boolean confirmationFlowEnabled, final boolean confirmationRequired)
throws Exception {
// setup
final int amountTargets = 20;
testdataFactory.createTargets(amountTargets, "rollout", "rollout");
final DistributionSet dsA = testdataFactory.createDistributionSet("");
if (confirmationFlowEnabled) {
enableConfirmationFlow();
}
// create rollout including the created targets with prefix 'rollout'
final Rollout rollout = rolloutManagement.create(entityFactory.rollout()
.create()
.name("rollout1")
.set(dsA.getId())
.targetFilterQuery("controllerId==rollout*"), 4, new RolloutGroupConditionBuilder().withDefaults()
final Rollout rollout = rolloutManagement.create(
entityFactory.rollout().create().name("rollout1").set(dsA.getId())
.targetFilterQuery("controllerId==rollout*"),
4, confirmationRequired, new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build());
final RolloutGroup firstGroup = rolloutGroupManagement
@@ -671,12 +825,18 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
retrieveAndVerifyRolloutGroupInCreating(rollout, firstGroup);
retrieveAndVerifyRolloutGroupInReady(rollout, firstGroup);
retrieveAndVerifyRolloutGroupInRunningAndScheduled(rollout, firstGroup, secondGroup);
retrieveAndVerifyRolloutGroupInRunningAndScheduled(rollout, firstGroup, secondGroup, confirmationFlowEnabled,
confirmationRequired);
}
private static Stream<Arguments> confirmationOptions() {
return Stream.of(Arguments.of(true, false), Arguments.of(true, true), Arguments.of(false, true));
}
@Step
private void retrieveAndVerifyRolloutGroupInRunningAndScheduled(final Rollout rollout,
final RolloutGroup firstGroup, final RolloutGroup secondGroup) throws Exception {
final RolloutGroup firstGroup, final RolloutGroup secondGroup, final boolean confirmationFlowEnabled,
final boolean confirmationRequired) throws Exception {
rolloutManagement.start(rollout.getId());
rolloutManagement.handleRollouts();
mvc.perform(get("/rest/v1/rollouts/{rolloutId}/deploygroups/{groupId}", rollout.getId(), firstGroup.getId())
@@ -688,7 +848,9 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
.andExpect(jsonPath("$.totalTargetsPerStatus.scheduled", equalTo(0)))
.andExpect(jsonPath("$.totalTargetsPerStatus.cancelled", equalTo(0)))
.andExpect(jsonPath("$.totalTargetsPerStatus.finished", equalTo(0)))
.andExpect(jsonPath("$.totalTargetsPerStatus.error", equalTo(0)));
.andExpect(jsonPath("$.totalTargetsPerStatus.error", equalTo(0)))
.andExpect(isConfirmationFlowEnabled() ? jsonPath("confirmationRequired", equalTo(confirmationRequired))
: jsonPath("confirmationRequired").doesNotExist());
mvc.perform(get("/rest/v1/rollouts/{rolloutId}/deploygroups/{groupId}", rollout.getId(), secondGroup.getId())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
@@ -699,7 +861,10 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
.andExpect(jsonPath("$.totalTargetsPerStatus.scheduled", equalTo(5)))
.andExpect(jsonPath("$.totalTargetsPerStatus.cancelled", equalTo(0)))
.andExpect(jsonPath("$.totalTargetsPerStatus.finished", equalTo(0)))
.andExpect(jsonPath("$.totalTargetsPerStatus.error", equalTo(0)));
.andExpect(jsonPath("$.totalTargetsPerStatus.error", equalTo(0)))
.andExpect(isConfirmationFlowEnabled() ? jsonPath("confirmationRequired", equalTo(confirmationRequired))
: jsonPath("confirmationRequired").doesNotExist());
;
}
@Step
@@ -718,7 +883,10 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
.andExpect(jsonPath("$.totalTargetsPerStatus.scheduled", equalTo(0)))
.andExpect(jsonPath("$.totalTargetsPerStatus.cancelled", equalTo(0)))
.andExpect(jsonPath("$.totalTargetsPerStatus.finished", equalTo(0)))
.andExpect(jsonPath("$.totalTargetsPerStatus.error", equalTo(0)));
.andExpect(jsonPath("$.totalTargetsPerStatus.error", equalTo(0)))
.andExpect(isConfirmationFlowEnabled() ? jsonPath("confirmationRequired").exists()
: jsonPath("confirmationRequired").doesNotExist());
;
}
@Step
@@ -729,6 +897,8 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("id", equalTo(firstGroup.getId().intValue())))
.andExpect(isConfirmationFlowEnabled() ? jsonPath("confirmationRequired").exists()
: jsonPath("confirmationRequired").doesNotExist())
.andExpect(jsonPath("status", equalTo("creating"))).andExpect(jsonPath("name", endsWith("1")))
.andExpect(jsonPath("description", endsWith("1")))
.andExpect(jsonPath("$.targetFilterQuery", equalTo("")))
@@ -983,9 +1153,9 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
final int weight = 66;
final String invalideWeightRequest = JsonBuilder.rollout("withWeight", "d", 2, dsId, "id==rollout*",
new RolloutGroupConditionBuilder().withDefaults().build(), null, null, Action.WEIGHT_MIN - 1);
new RolloutGroupConditionBuilder().withDefaults().build(), null, null, Action.WEIGHT_MIN - 1, null);
final String valideWeightRequest = JsonBuilder.rollout("withWeight", "d", 2, dsId, "id==rollout*",
new RolloutGroupConditionBuilder().withDefaults().build(), null, null, weight);
new RolloutGroupConditionBuilder().withDefaults().build(), null, null, weight, null);
mvc.perform(post("/rest/v1/rollouts").content(valideWeightRequest).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
@@ -1009,7 +1179,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
final String targetFilterQuery, final int targets, final Action.ActionType type) throws Exception {
final String actionType = MgmtRestModelMapper.convertActionType(type).getName();
final String rollout = JsonBuilder.rollout(name, "desc", groupSize, distributionSetId, targetFilterQuery,
new RolloutGroupConditionBuilder().withDefaults().build(), null, actionType, null);
new RolloutGroupConditionBuilder().withDefaults().build(), null, actionType, null, null);
mvc.perform(post("/rest/v1/rollouts").content(rollout).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
@@ -1040,7 +1210,15 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
private Rollout createRollout(final String name, final int amountGroups, final long distributionSetId,
final String targetFilterQuery) {
final Rollout rollout = createRolloutInCreatingSatate(name, amountGroups, distributionSetId, targetFilterQuery);
return createRollout(name, amountGroups, distributionSetId, targetFilterQuery, false);
}
private Rollout createRollout(final String name, final int amountGroups, final long distributionSetId,
final String targetFilterQuery, final boolean confirmationRequired) {
final Rollout rollout = rolloutManagement.create(
entityFactory.rollout().create().name(name).set(distributionSetId).targetFilterQuery(targetFilterQuery),
amountGroups, confirmationRequired, new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build());
// Run here, because Scheduler is disabled during tests
rolloutManagement.handleRollouts();
@@ -1048,14 +1226,6 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
return rolloutManagement.get(rollout.getId()).orElseThrow(NoSuchElementException::new);
}
private Rollout createRolloutInCreatingSatate(final String name, final int amountGroups,
final long distributionSetId, final String targetFilterQuery) {
return rolloutManagement.create(
entityFactory.rollout().create().name(name).set(distributionSetId).targetFilterQuery(targetFilterQuery),
amountGroups, new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build());
}
@Test
@Description("Trigger next rollout group")
void triggeringNextGroupRollout() throws Exception {
@@ -1085,7 +1255,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest {
final DistributionSet dsA = testdataFactory.createDistributionSet("");
// CREATING state
final Rollout rollout = createRolloutInCreatingSatate("rollout1", 3, dsA.getId(), "controllerId==rollout*");
final Rollout rollout = createRollout("rollout1", 3, dsA.getId(), "controllerId==rollout*", false);
triggerNextGroupAndExpect(rollout, status().isBadRequest());
// READY state

View File

@@ -21,6 +21,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.List;
import java.util.stream.Stream;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
@@ -37,6 +38,10 @@ import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
@@ -59,6 +64,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
private static final String JSON_PATH_FIELD_ID = ".id";
private static final String JSON_PATH_FIELD_NAME = ".name";
private static final String JSON_PATH_FIELD_QUERY = ".query";
private static final String JSON_PATH_FIELD_CONFIRMATION_REQUIRED = ".confirmationRequired";
private static final String JSON_PATH_FIELD_CONTENT = ".content";
private static final String JSON_PATH_FIELD_SIZE = ".size";
private static final String JSON_PATH_FIELD_TOTAL = ".total";
@@ -76,6 +82,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
private static final String JSON_PATH_NAME = JSON_PATH_ROOT + JSON_PATH_FIELD_NAME;
private static final String JSON_PATH_ID = JSON_PATH_ROOT + JSON_PATH_FIELD_ID;
private static final String JSON_PATH_QUERY = JSON_PATH_ROOT + JSON_PATH_FIELD_QUERY;
private static final String JSON_PATH_CONFIRMATION_REQUIRED = JSON_PATH_ROOT
+ JSON_PATH_FIELD_CONFIRMATION_REQUIRED;
private static final String JSON_PATH_AUTO_ASSIGN_DS = JSON_PATH_ROOT + JSON_PATH_FIELD_AUTO_ASSIGN_DS;
private static final String JSON_PATH_AUTO_ASSIGN_ACTION_TYPE = JSON_PATH_ROOT
+ JSON_PATH_FIELD_AUTO_ASSIGN_ACTION_TYPE;
@@ -126,7 +134,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
.contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk())
.andExpect(jsonPath(JSON_PATH_ID, equalTo(tfq.getId().intValue())))
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(filterQuery2)))
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(filterName)));
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(filterName)))
.andExpect(jsonPath(JSON_PATH_CONFIRMATION_REQUIRED).doesNotExist());
final TargetFilterQuery tfqCheck = targetFilterQueryManagement.get(tfq.getId()).get();
assertThat(tfqCheck.getQuery()).isEqualTo(filterQuery2);
@@ -149,7 +158,9 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
.contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk())
.andExpect(jsonPath(JSON_PATH_ID, equalTo(tfq.getId().intValue())))
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(filterQuery)))
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(filterName2)));
.andExpect(jsonPath(JSON_PATH_NAME, equalTo(filterName2)))
.andExpect(jsonPath(JSON_PATH_CONFIRMATION_REQUIRED).doesNotExist());
;
final TargetFilterQuery tfqCheck = targetFilterQueryManagement.get(tfq.getId()).get();
assertThat(tfqCheck.getQuery()).isEqualTo(filterQuery);
@@ -257,6 +268,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId())).andDo(print())
.andExpect(status().isOk()).andExpect(jsonPath(JSON_PATH_NAME, equalTo(knownName)))
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(knownQuery)))
.andExpect(jsonPath(JSON_PATH_CONFIRMATION_REQUIRED).doesNotExist())
.andExpect(jsonPath("$._links.self.href", equalTo(hrefPrefix)))
.andExpect(jsonPath("$._links.autoAssignDS.href", equalTo(hrefPrefix + "/autoAssignDS")));
}
@@ -364,24 +376,30 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
}
@Test
@Description("Ensures that the distribution set auto-assignment works as intended with distribution set and action type validation")
public void setAutoAssignDistributionSetToTargetFilterQuery() throws Exception {
@ParameterizedTest
@MethodSource("confirmationOptions")
@Description("Ensures that the distribution set auto-assignment works as intended with distribution set, action type and confirmation validation")
public void setAutoAssignDistributionSetToTargetFilterQuery(final boolean confirmationFlowActive,
final Boolean confirmationRequired) throws Exception {
final String knownQuery = "name==test05";
final String knownName = "filter05";
if (confirmationFlowActive) {
enableConfirmationFlow();
}
final DistributionSet set = testdataFactory.createDistributionSet();
final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery);
verifyAutoAssignmentWithoutActionType(tfq, set);
verifyAutoAssignmentWithoutActionType(tfq, set, confirmationRequired);
verifyAutoAssignmentWithForcedActionType(tfq, set);
verifyAutoAssignmentWithForcedActionType(tfq, set, confirmationRequired);
verifyAutoAssignmentWithSoftActionType(tfq, set);
verifyAutoAssignmentWithSoftActionType(tfq, set, confirmationRequired);
verifyAutoAssignmentWithTimeForcedActionType(tfq, set);
verifyAutoAssignmentWithDownloadOnlyActionType(tfq, set);
verifyAutoAssignmentWithDownloadOnlyActionType(tfq, set, confirmationRequired);
verifyAutoAssignmentWithUnknownActionType(tfq, set);
@@ -390,34 +408,68 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
verifyAutoAssignmentWithSoftDeletedDs(tfq);
}
@Step
private void verifyAutoAssignmentWithoutActionType(final TargetFilterQuery tfq, final DistributionSet set)
throws Exception {
verifyAutoAssignmentByActionType(tfq, set, null);
@ParameterizedTest
@ValueSource(booleans = { true, false })
@Description("Verify the confirmation required flag will be set based on the feature state")
void verifyConfirmationStateIfNotProvided(final boolean confirmationFlowActive) throws Exception {
final String knownQuery = "name==test05";
final String knownName = "filter05";
if (confirmationFlowActive) {
enableConfirmationFlow();
}
final DistributionSet set = testdataFactory.createDistributionSet();
final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery);
// do not provide something about the confirmation
verifyAutoAssignmentByActionType(tfq, set, null, null);
assertThat(targetFilterQueryManagement.get(tfq.getId())).hasValueSatisfying(filter -> {
assertThat(filter.isConfirmationRequired()).isEqualTo(confirmationFlowActive);
});
}
private static Stream<Arguments> confirmationOptions() {
return Stream.of(Arguments.of(true, false), Arguments.of(true, true), Arguments.of(false, true),
Arguments.of(true, null), Arguments.of(false, null));
}
@Step
private void verifyAutoAssignmentWithForcedActionType(final TargetFilterQuery tfq, final DistributionSet set)
throws Exception {
verifyAutoAssignmentByActionType(tfq, set, MgmtActionType.FORCED);
private void verifyAutoAssignmentWithoutActionType(final TargetFilterQuery tfq, final DistributionSet set,
final Boolean confirmationRequired) throws Exception {
verifyAutoAssignmentByActionType(tfq, set, null, confirmationRequired);
}
@Step
private void verifyAutoAssignmentWithSoftActionType(final TargetFilterQuery tfq, final DistributionSet set)
throws Exception {
verifyAutoAssignmentByActionType(tfq, set, MgmtActionType.SOFT);
private void verifyAutoAssignmentWithForcedActionType(final TargetFilterQuery tfq, final DistributionSet set,
final Boolean confirmationRequired) throws Exception {
verifyAutoAssignmentByActionType(tfq, set, MgmtActionType.FORCED, confirmationRequired);
}
@Step
private void verifyAutoAssignmentWithSoftActionType(final TargetFilterQuery tfq, final DistributionSet set,
final Boolean confirmationRequired) throws Exception {
verifyAutoAssignmentByActionType(tfq, set, MgmtActionType.SOFT, confirmationRequired);
}
private void verifyAutoAssignmentByActionType(final TargetFilterQuery tfq, final DistributionSet set,
final MgmtActionType actionType) throws Exception {
final MgmtActionType actionType, final Boolean confirmationRequired) throws Exception {
final String hrefPrefix = "http://localhost" + MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"
+ tfq.getId();
final String payload = actionType != null
? "{\"id\":" + set.getId() + ", \"type\":\"" + actionType.getName() + "\"}"
: "{\"id\":" + set.getId() + "}";
final JSONObject jsonObject = new JSONObject();
jsonObject.put("id", set.getId());
if (actionType != null) {
jsonObject.put("type", actionType.getName());
}
if (confirmationRequired != null) {
jsonObject.put("confirmationRequired", confirmationRequired);
}
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
.content(payload).contentType(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk());
.content(jsonObject.toString()).contentType(MediaType.APPLICATION_JSON)).andDo(print())
.andExpect(status().isOk());
final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(tfq.getId()).get();
final MgmtActionType expectedActionType = actionType != null ? actionType : MgmtActionType.FORCED;
@@ -429,6 +481,10 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId())).andDo(print())
.andExpect(status().isOk()).andExpect(jsonPath(JSON_PATH_NAME, equalTo(tfq.getName())))
.andExpect(jsonPath(JSON_PATH_QUERY, equalTo(tfq.getQuery())))
.andExpect(isConfirmationFlowEnabled()
? jsonPath(JSON_PATH_CONFIRMATION_REQUIRED,
equalTo(confirmationRequired == null || confirmationRequired))
: jsonPath(JSON_PATH_CONFIRMATION_REQUIRED).doesNotExist())
.andExpect(jsonPath(JSON_PATH_AUTO_ASSIGN_DS, equalTo(set.getId().intValue())))
.andExpect(jsonPath(JSON_PATH_AUTO_ASSIGN_ACTION_TYPE, equalTo(expectedActionType.getName())))
.andExpect(jsonPath("$._links.self.href", equalTo(hrefPrefix)))
@@ -448,9 +504,9 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
}
@Step
private void verifyAutoAssignmentWithDownloadOnlyActionType(final TargetFilterQuery tfq, final DistributionSet set)
throws Exception {
verifyAutoAssignmentByActionType(tfq, set, MgmtActionType.DOWNLOAD_ONLY);
private void verifyAutoAssignmentWithDownloadOnlyActionType(final TargetFilterQuery tfq, final DistributionSet set,
final Boolean confirmationRequired) throws Exception {
verifyAutoAssignmentByActionType(tfq, set, MgmtActionType.DOWNLOAD_ONLY, confirmationRequired);
}
@Step

View File

@@ -9,6 +9,7 @@
package org.eclipse.hawkbit.mgmt.rest.resource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.TARGET_V1_AUTO_CONFIRM;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
@@ -36,6 +37,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.validation.ConstraintViolationException;
@@ -73,6 +75,9 @@ import org.hamcrest.Matchers;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.data.domain.PageRequest;
@@ -764,9 +769,10 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
final Target test1 = entityFactory.target().create().controllerId("id1")
.name(RandomStringUtils.randomAlphanumeric(NamedEntity.NAME_MAX_SIZE + 1)).build();
final MvcResult mvcResult = mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING)
.content(JsonBuilder.targets(Collections.singletonList(test1), true))
.contentType(MediaType.APPLICATION_JSON))
final MvcResult mvcResult = mvc
.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING)
.content(JsonBuilder.targets(Collections.singletonList(test1), true))
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest()).andReturn();
assertThat(targetManagement.count()).isZero();
@@ -1253,8 +1259,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
// Update
if (schedule == null) {
final List<Target> updatedTargets = assignDistributionSet(one, Collections.singletonList(target))
.getAssignedEntity()
.stream().map(Action::getTarget).collect(Collectors.toList());
.getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList());
// 2nd update
// sleep 10ms to ensure that we can sort by reportedAt
Awaitility.await().atMost(Duration.ONE_HUNDRED_MILLISECONDS).atLeast(5, TimeUnit.MILLISECONDS)
@@ -1336,6 +1341,48 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
assertThat(targetManagement.getByControllerID(target.getControllerId()).get()).isEqualTo(target);
}
@ParameterizedTest
@MethodSource("confirmationOptions")
@Description("Ensures that confirmation option is considered in assignment request.")
void assignDistributionSetToTargetWithConfirmationOptions(final boolean confirmationFlowActive,
final Boolean confirmationRequired) throws Exception {
final Target target = testdataFactory.createTarget();
final DistributionSet set = testdataFactory.createDistributionSet("one");
if (confirmationFlowActive) {
enableConfirmationFlow();
}
final JSONObject jsonPayload = new JSONObject();
jsonPayload.put("id", set.getId());
if (confirmationRequired != null) {
jsonPayload.put("confirmationRequired", confirmationRequired);
}
mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + target.getControllerId() + "/assignedDS")
.content(jsonPayload.toString()).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("assigned", equalTo(1))).andExpect(jsonPath("alreadyAssigned", equalTo(0)))
.andExpect(jsonPath("total", equalTo(1)));
assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get()).isEqualTo(set);
assertThat(deploymentManagement.findActionsByDistributionSet(PAGE, set.getId()).getContent()).hasSize(1)
.allMatch(action -> {
if (!confirmationFlowActive) {
return !action.isWaitingConfirmation();
}
return confirmationRequired == null ? action.isWaitingConfirmation()
: confirmationRequired == action.isWaitingConfirmation();
});
}
private static Stream<Arguments> confirmationOptions() {
return Stream.of(Arguments.of(true, true), Arguments.of(true, false), Arguments.of(false, true),
Arguments.of(false, false), Arguments.of(true, null), Arguments.of(false, null));
}
@Test
@Description("Verfies that a DOWNLOAD_ONLY DS to target assignment is properly handled")
void assignDownloadOnlyDistributionSetToTarget() throws Exception {
@@ -2003,9 +2050,9 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
final JSONObject bodyValid = getAssignmentObject(dsId, MgmtActionType.FORCED, 98);
mvc.perform(post("/rest/v1/targets/{targetId}/assignedDS", targetId).content(bodyValid.toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.multiassignmentNotEnabled")));
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errorCode", equalTo("hawkbit.server.error.multiassignmentNotEnabled")));
}
@Test
@@ -2057,7 +2104,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
}
}
@Test
@Description("An action provides information of the rollout it was created for (if any).")
void getActionWithRolloutInfo() throws Exception {
@@ -2094,8 +2141,10 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
@Test
@Description("Ensures that a post request for creating targets with target type works.")
void createTargetsWithTargetType() throws Exception {
final TargetType type1 = testdataFactory.createTargetType("typeWithDs", Collections.singletonList(standardDsType));
final TargetType type2 = testdataFactory.createTargetType("typeWithOutDs", Collections.singletonList(standardDsType));
final TargetType type1 = testdataFactory.createTargetType("typeWithDs",
Collections.singletonList(standardDsType));
final TargetType type2 = testdataFactory.createTargetType("typeWithOutDs",
Collections.singletonList(standardDsType));
final Target test1 = entityFactory.target().create().controllerId("id1").name("targetWithoutType")
.securityToken("token").address("amqp://test123/foobar").description("testid1").build();
@@ -2132,8 +2181,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
.andExpect(jsonPath("[2].description", equalTo("testid3")))
.andExpect(jsonPath("[2].createdAt", not(equalTo(0))))
.andExpect(jsonPath("[2].createdBy", equalTo("bumlux")))
.andExpect(jsonPath("[2].targetType", equalTo(type2.getId().intValue())))
.andReturn();
.andExpect(jsonPath("[2].targetType", equalTo(type2.getId().intValue()))).andReturn();
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + test2.getControllerId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
@@ -2159,10 +2207,11 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
@Description("Ensures that a post request for creating target with target type works.")
void createTargetWithExistingTargetType() throws Exception {
// create target type
List<TargetType> targetTypes = testdataFactory.createTargetTypes("targettype",1);
List<TargetType> targetTypes = testdataFactory.createTargetTypes("targettype", 1);
assertThat(targetTypes).hasSize(1);
final Target target = entityFactory.target().create().controllerId("targetcontroller").name("testtarget").targetType(targetTypes.get(0).getId()).build();
final Target target = entityFactory.target().create().controllerId("targetcontroller").name("testtarget")
.targetType(targetTypes.get(0).getId()).build();
final String targetList = JsonBuilder.targets(Collections.singletonList(target), false);
@@ -2172,14 +2221,15 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
.andExpect(jsonPath("[0].controllerId", equalTo("targetcontroller")))
.andExpect(jsonPath("[0].targetType", equalTo(targetTypes.get(0).getId().intValue())));
assertThat(targetManagement.getByControllerID("targetcontroller").get().getTargetType().getId()).isEqualTo(targetTypes.get(0).getId());
assertThat(targetManagement.getByControllerID("targetcontroller").get().getTargetType().getId())
.isEqualTo(targetTypes.get(0).getId());
}
@Test
@Description("Ensures that a put request for updating targets with target type works.")
void updateTargetTypeInTarget() throws Exception {
// create target type
List<TargetType> targetTypes = testdataFactory.createTargetTypes("targettype",2);
List<TargetType> targetTypes = testdataFactory.createTargetTypes("targettype", 2);
assertThat(targetTypes).hasSize(2);
String controllerId = "targetcontroller";
@@ -2234,7 +2284,8 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
.content("{\"id\":" + targetType.getId() + "}").contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(targetManagement.getByControllerID(targetControllerId).get().getTargetType().getId()).isEqualTo(targetType.getId());
assertThat(targetManagement.getByControllerID(targetControllerId).get().getTargetType().getId())
.isEqualTo(targetType.getId());
}
@Test
@@ -2254,8 +2305,10 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// verify response json exception message if body does not include id field
final MvcResult mvcResult = mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + targetControllerId + "/targettype")
.content("{\"unknownfield\":" + invalidTargetTypeId + "}").contentType(MediaType.APPLICATION_JSON))
final MvcResult mvcResult = mvc
.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + targetControllerId + "/targettype")
.content("{\"unknownfield\":" + invalidTargetTypeId + "}")
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest()).andReturn();
final ExceptionInfo exceptionInfo = ResourceUtility
.convertException(mvcResult.getResponse().getContentAsString());
@@ -2268,7 +2321,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
@Description("Ensures that a delete request for unassign target type from target works.")
void unassignTargetTypeFromTarget() throws Exception {
// create target type
List<TargetType> targetTypes = testdataFactory.createTargetTypes("targettype",1);
List<TargetType> targetTypes = testdataFactory.createTargetTypes("targettype", 1);
assertThat(targetTypes).hasSize(1);
String targetControllerId = "targetcontroller";
@@ -2296,24 +2349,24 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
knownTargetId)).andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
// PUT is not allowed
mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING
+ MgmtRestConstants.TARGET_TARGET_TYPE_V1_REQUEST_MAPPING, knownTargetId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(put(
MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + MgmtRestConstants.TARGET_TARGET_TYPE_V1_REQUEST_MAPPING,
knownTargetId)).andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
// POST does not exist with path parameter targettype
mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING
+ MgmtRestConstants.TARGET_TARGET_TYPE_V1_REQUEST_MAPPING + "/123", knownTargetId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// DELETE does not exist with path parameter targettype
// DELETE does not exist with path parameter targettype
mvc.perform(delete(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING
+ MgmtRestConstants.TARGET_TARGET_TYPE_V1_REQUEST_MAPPING + "/123", knownTargetId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// Invalid content
mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + MgmtRestConstants.TARGET_TARGET_TYPE_V1_REQUEST_MAPPING,
knownTargetId)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isUnsupportedMediaType());
mvc.perform(post(
MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + MgmtRestConstants.TARGET_TARGET_TYPE_V1_REQUEST_MAPPING,
knownTargetId)).andDo(MockMvcResultPrinter.print()).andExpect(status().isUnsupportedMediaType());
// Bad request if id field is missing
mvc.perform(post(
@@ -2321,4 +2374,87 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
knownTargetId).content("{\"unknownfield\":123}").contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
}
@ParameterizedTest
@MethodSource("possibleActiveStates")
void getAutoConfirmActive(final String initiator, final String remark) throws Exception {
final String knownTargetId = "targetId";
testdataFactory.createTarget(knownTargetId);
confirmationManagement.activateAutoConfirmation(knownTargetId, initiator, remark);
// GET with all possible responses
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/" + TARGET_V1_AUTO_CONFIRM,
knownTargetId)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("active", equalTo(Boolean.TRUE)))
.andExpect(initiator == null ? jsonPath("initiator").doesNotExist()
: jsonPath("initiator", equalTo(initiator)))
.andExpect(remark == null ? jsonPath("remark").doesNotExist() : jsonPath("remark", equalTo(remark)))
.andExpect(jsonPath("_links.deactivate").exists())
.andExpect(jsonPath("_links.activate").doesNotExist());
}
@Test
void getAutoConfirmStateFromTargetsEndpoint() throws Exception {
final String knownTargetId = "targetId";
testdataFactory.createTarget(knownTargetId);
// GET if active
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(jsonPath("content.[0].autoConfirmActive").doesNotExist());
enableConfirmationFlow();
// GET if not active
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk())
.andExpect(jsonPath("content.[0].autoConfirmActive", equalTo(Boolean.FALSE)));
confirmationManagement.activateAutoConfirmation(knownTargetId, "test", "remark");
// GET if active
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(jsonPath("content.[0].autoConfirmActive", equalTo(Boolean.TRUE)));
}
@Test
void getAutoConfirmNotActive() throws Exception {
final String knownTargetId = "targetId";
// GET for not existing target
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/" + TARGET_V1_AUTO_CONFIRM,
knownTargetId)).andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
testdataFactory.createTarget(knownTargetId);
// GET for auto-confirm not active
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/" + TARGET_V1_AUTO_CONFIRM,
knownTargetId)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("active", equalTo(Boolean.FALSE))).andExpect(jsonPath("initiator").doesNotExist())
.andExpect(jsonPath("remark").doesNotExist()).andExpect(jsonPath("_links.activate").exists());
}
@Test
void autoConfirmStateReferenceOnTarget() throws Exception {
final String knownTargetId = "targetId";
testdataFactory.createTarget(knownTargetId);
// GET with confirmation flow not active should not expose
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}", knownTargetId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("autoConfirmActive").doesNotExist())
.andExpect(jsonPath("_links.autoConfirm").doesNotExist());
enableConfirmationFlow();
// GET with confirmation flow active should expose
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}", knownTargetId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("autoConfirmActive").exists()).andExpect(jsonPath("_links.autoConfirm").exists());
}
private static Stream<Arguments> possibleActiveStates() {
return Stream.of(Arguments.of("someInitiator", "someRemark"), Arguments.of(null, "someRemark"),
Arguments.of("someInitiator", null), Arguments.of(null, null));
}
}