Prioritisation of assignments via mgmt-API (#895)

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

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

View File

@@ -25,11 +25,13 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.ddi.rest.resource.DdiApiConfiguration;
import org.eclipse.hawkbit.mgmt.rest.resource.MgmtApiConfiguration;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ArtifactUpload;
import org.eclipse.hawkbit.repository.model.DeploymentRequestBuilder;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
@@ -37,6 +39,7 @@ import org.eclipse.hawkbit.repository.test.TestConfiguration;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.eclipse.hawkbit.rest.RestConfiguration;
import org.eclipse.hawkbit.rest.util.FilterHttpResponse;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired;
@@ -98,8 +101,13 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
}
public static MyFieldFieldDesc requestFieldWithPath(final String path, final boolean mandatory) {
return requestFieldWithPath(path, mandatory, mandatory ? "X" : "");
}
private static MyFieldFieldDesc requestFieldWithPath(final String path, final boolean mandatory,
final String mandatoryMessage) {
final MyFieldFieldDesc myFieldDesc = new MyFieldFieldDesc(path);
myFieldDesc.attributes(key("mandatory").value(mandatory ? "X" : ""));
myFieldDesc.attributes(key("mandatory").value(mandatoryMessage));
// defaults
myFieldDesc.attributes(key("value").value(""));
@@ -118,6 +126,10 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
return requestFieldWithPath(path, false);
}
public static MyFieldFieldDesc requestFieldWithPathMandatoryInMultiAssignMode(final String path) {
return requestFieldWithPath(path, false, "when multi-assignment is enabled");
}
public static class MyFieldFieldDesc extends SubsectionDescriptor {
/**
@@ -154,11 +166,17 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
final Target savedTarget = targetManagement.create(entityFactory.target().create().controllerId(name)
.status(TargetUpdateStatus.UNKNOWN).address("http://192.168.0.1").description("My name is " + name)
.lastTargetQuery(System.currentTimeMillis()));
final List<Target> updatedTargets = maintenanceWindowSchedule == null
? assignWithoutMaintenanceWindow(distributionSet, savedTarget, timeforced)
: assignWithMaintenanceWindow(distributionSet, savedTarget, timeforced, maintenanceWindowSchedule,
maintenanceWindowDuration, maintenanceWindowTimeZone);
final DeploymentRequestBuilder deploymentRequestBuilder = DeploymentManagement
.deploymentRequest(savedTarget.getControllerId(), distributionSet.getId())
.setMaintenance(maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone);
if (timeforced) {
deploymentRequestBuilder.setActionType(ActionType.TIMEFORCED);
}
if (isMultiAssignmentsEnabled()) {
deploymentRequestBuilder.setWeight(600);
}
final List<Target> updatedTargets = makeAssignment(deploymentRequestBuilder.build()).getAssignedEntity()
.stream().map(Action::getTarget).collect(Collectors.toList());
if (inSync) {
feedbackToByInSync(distributionSet);
@@ -167,28 +185,6 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
return updatedTargets.get(0);
}
private List<Target> assignWithoutMaintenanceWindow(final DistributionSet distributionSet, final Target savedTarget,
final boolean timeforced) {
final List<Action> actions = timeforced
? assignDistributionSet(distributionSet.getId(), savedTarget.getControllerId(), ActionType.TIMEFORCED)
.getAssignedEntity()
: assignDistributionSet(distributionSet, savedTarget).getAssignedEntity();
return actions.stream().map(Action::getTarget).collect(Collectors.toList());
}
private List<Target> assignWithMaintenanceWindow(final DistributionSet distributionSet, final Target savedTarget,
final boolean timeforced, final String maintenanceWindowSchedule, final String maintenanceWindowDuration,
final String maintenanceWindowTimeZone) {
final List<Action> actions = timeforced
? assignDistributionSetWithMaintenanceWindow(distributionSet.getId(), savedTarget.getControllerId(),
ActionType.TIMEFORCED, maintenanceWindowSchedule, maintenanceWindowDuration,
maintenanceWindowTimeZone).getAssignedEntity()
: assignDistributionSetWithMaintenanceWindow(distributionSet.getId(), savedTarget.getControllerId(),
maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone)
.getAssignedEntity();
return actions.stream().map(Action::getTarget).collect(Collectors.toList());
}
protected DistributionSet createDistributionSet() {
DistributionSet distributionSet = testdataFactory.createDistributionSet("");
distributionSet = distributionSetManagement.update(entityFactory.distributionSet()
@@ -314,4 +310,9 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
parameterWithName("q").description(ApiModelPropertiesGeneric.FIQL));
}
protected boolean isMultiAssignmentsEnabled() {
return Boolean.TRUE.equals(tenantConfigurationManagement
.getConfigurationValue(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue());
}
}

View File

@@ -109,6 +109,8 @@ public final class MgmtApiModelProperties {
public static final String ROLLOUT_LINKS_GROUPS = "Link to retrieve the groups a rollout";
public static final String ROLLOUT_START_ASYNC = "Start the rollout asynchronous";
public static final String RESULTING_ACTIONS_WEIGHT = "Weight of the resulting Actions";
public static final String UPDATE_STATUS = "Current update status of the target.";
public static final String TARGET_ATTRIBUTES = "Target attributes.";
@@ -138,6 +140,8 @@ public final class MgmtApiModelProperties {
public static final String ACTION_LIST = "List of actions.";
public static final String ACTION_WEIGHT = "Weight of the action showing the importance of the update.";
public static final String IP_ADDRESS = "Last known IP address of the target. Only presented if IP address of the target itself is known (connected directly through DDI API).";
public static final String ADDRESS = "The last known address URI of the target. Includes information of the target is connected either directly (DDI) through HTTP or indirectly (DMF) through amqp.";
@@ -188,6 +192,7 @@ public final class MgmtApiModelProperties {
// request parameter
public static final String FORCETIME = "Forcetime in milliseconds.";
public static final String FORCE = "Force as boolean.";
public static final String ASSIGNMENT_WEIGHT = "Importance of the assignment.";
public static final String ASSIGNMENT_TYPE = "The type of the assignment.";
public static final String TARGET_ASSIGNED = "The number of targets that have been assigned as part of this operation.";
public static final String TARGET_ASSIGNED_ALREADY = "The number of targets which already had been the assignment.";

View File

@@ -385,6 +385,9 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
.description(MgmtApiModelProperties.OFFLINE_UPDATE).optional()),
requestFields(
requestFieldWithPath("[].id").description(ApiModelPropertiesGeneric.ITEM_ID),
requestFieldWithPathMandatoryInMultiAssignMode("[].weight")
.description(MgmtApiModelProperties.ASSIGNMENT_WEIGHT)
.type(JsonFieldType.NUMBER).attributes(key("value").value("0 - 1000")),
optionalRequestFieldWithPath("[].forcetime")
.description(MgmtApiModelProperties.FORCETIME),
optionalRequestFieldWithPath("[].maintenanceWindow")

View File

@@ -23,10 +23,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorAction;
@@ -52,6 +51,8 @@ import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.restdocs.snippet.Snippet;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
@@ -80,7 +81,7 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
@Description("Handles the GET request of retrieving all rollouts. Required Permission: "
+ SpPermission.READ_ROLLOUT)
public void getRollouts() throws Exception {
enableMultiAssignments();
createRolloutEntity();
mockMvc.perform(get(MgmtRestConstants.ROLLOUT_V1_REQUEST_MAPPING).accept(MediaTypes.HAL_JSON_VALUE))
@@ -113,6 +114,8 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "id").description(ApiModelPropertiesGeneric.ITEM_ID));
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "name").description(ApiModelPropertiesGeneric.NAME));
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "weight")
.description(MgmtApiModelProperties.RESULTING_ACTIONS_WEIGHT).type(JsonFieldType.NUMBER).optional());
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "deleted").description(ApiModelPropertiesGeneric.DELETED));
allFieldDescriptor
.add(fieldWithPath(arrayPrefix + "description").description(ApiModelPropertiesGeneric.DESCRPTION));
@@ -159,7 +162,7 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
@Description("Handles the GET request of retrieving a single rollout. Required Permission: "
+ SpPermission.READ_ROLLOUT)
public void getRollout() throws Exception {
enableMultiAssignments();
final Rollout rollout = createRolloutEntity();
mockMvc.perform(get(MgmtRestConstants.ROLLOUT_V1_REQUEST_MAPPING + "/{rolloutId}", rollout.getId())
@@ -195,47 +198,47 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
.contentType(MediaTypes.HAL_JSON_UTF8).accept(MediaTypes.HAL_JSON_VALUE))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
.andExpect(content().contentType(MediaTypes.HAL_JSON_UTF8))
.andDo(this.document.document(
requestFields(requestFieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
optionalRequestFieldWithPath("type").description(
MgmtApiModelProperties.ROLLOUT_TYPE).attributes(
key("value").value("['soft', 'forced', 'timeforced', 'downloadonly']")),
requestFieldWithPath("distributionSetId")
.description(MgmtApiModelProperties.ROLLOUT_DS_ID),
requestFieldWithPath("targetFilterQuery")
.description(MgmtApiModelProperties.ROLLOUT_FILTER_QUERY),
requestFieldWithPath("amountGroups")
.description(MgmtApiModelProperties.ROLLOUT_AMOUNT_GROUPS),
optionalRequestFieldWithPath("description")
.description(ApiModelPropertiesGeneric.DESCRPTION),
optionalRequestFieldWithPath("successCondition")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION),
optionalRequestFieldWithPath("successCondition.condition")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_CONDITION)
.attributes(key("value").value("['threshold']")),
optionalRequestFieldWithPath("successCondition.expression")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_EXP),
optionalRequestFieldWithPath("successAction")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION),
optionalRequestFieldWithPath("successAction.action")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_ACTION)
.attributes(key("value").value("['nextgroup']")),
optionalRequestFieldWithPath("successAction.expression")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_EXP),
optionalRequestFieldWithPath("errorCondition")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION),
optionalRequestFieldWithPath("errorCondition.condition")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_CONDITION)
.attributes(key("value").value("['threshold']")),
optionalRequestFieldWithPath("errorCondition.expression")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_EXP),
optionalRequestFieldWithPath("errorAction")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION),
optionalRequestFieldWithPath("errorAction.action")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_ACTION)
.attributes(key("value").value("['pause']")),
optionalRequestFieldWithPath("errorAction.expression")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_EXP)),
.andDo(this.document.document(requestFields(
requestFieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
requestFieldWithPathMandatoryInMultiAssignMode("weight").type(JsonFieldType.NUMBER)
.description(MgmtApiModelProperties.RESULTING_ACTIONS_WEIGHT)
.attributes(key("value").value("0 - 1000")),
requestFieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
optionalRequestFieldWithPath("type").description(MgmtApiModelProperties.ROLLOUT_TYPE)
.attributes(key("value").value("['soft', 'forced', 'timeforced', 'downloadonly']")),
requestFieldWithPath("distributionSetId").description(MgmtApiModelProperties.ROLLOUT_DS_ID),
requestFieldWithPath("targetFilterQuery")
.description(MgmtApiModelProperties.ROLLOUT_FILTER_QUERY),
requestFieldWithPath("amountGroups").description(MgmtApiModelProperties.ROLLOUT_AMOUNT_GROUPS),
optionalRequestFieldWithPath("description").description(ApiModelPropertiesGeneric.DESCRPTION),
optionalRequestFieldWithPath("successCondition")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION),
optionalRequestFieldWithPath("successCondition.condition")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_CONDITION)
.attributes(key("value").value("['threshold']")),
optionalRequestFieldWithPath("successCondition.expression")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_EXP),
optionalRequestFieldWithPath("successAction")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION),
optionalRequestFieldWithPath("successAction.action")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_ACTION)
.attributes(key("value").value("['nextgroup']")),
optionalRequestFieldWithPath("successAction.expression")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_EXP),
optionalRequestFieldWithPath("errorCondition")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION),
optionalRequestFieldWithPath("errorCondition.condition")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_CONDITION)
.attributes(key("value").value("['threshold']")),
optionalRequestFieldWithPath("errorCondition.expression")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_EXP),
optionalRequestFieldWithPath("errorAction")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION),
optionalRequestFieldWithPath("errorAction.action")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_ACTION)
.attributes(key("value").value("['pause']")),
optionalRequestFieldWithPath("errorAction.expression")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_EXP)),
getRolloutResponseFields(false, true)));
@@ -283,77 +286,83 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
.contentType(MediaType.APPLICATION_JSON).accept(MediaTypes.HAL_JSON_VALUE))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
.andExpect(content().contentType(MediaTypes.HAL_JSON_UTF8))
.andDo(this.document.document(requestFields(
requestFieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
requestFieldWithPath("distributionSetId").description(MgmtApiModelProperties.ROLLOUT_DS_ID),
requestFieldWithPath("targetFilterQuery")
.description(MgmtApiModelProperties.ROLLOUT_FILTER_QUERY),
optionalRequestFieldWithPath("description").description(ApiModelPropertiesGeneric.DESCRPTION),
optionalRequestFieldWithPath("successCondition")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION),
optionalRequestFieldWithPath("successCondition.condition")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_CONDITION)
.attributes(key("value").value("['threshold']")),
optionalRequestFieldWithPath("successCondition.expression")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_EXP),
optionalRequestFieldWithPath("successAction")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION),
optionalRequestFieldWithPath("successAction.action")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_ACTION)
.attributes(key("value").value("['nextgroup']")),
optionalRequestFieldWithPath("successAction.expression")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_EXP),
optionalRequestFieldWithPath("errorCondition")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION),
optionalRequestFieldWithPath("errorCondition.condition")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_CONDITION)
.attributes(key("value").value("['threshold']")),
optionalRequestFieldWithPath("errorCondition.expression")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_EXP),
optionalRequestFieldWithPath("errorAction")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION),
optionalRequestFieldWithPath("errorAction.action")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_ACTION)
.attributes(key("value").value("['pause']")),
optionalRequestFieldWithPath("errorAction.expression")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_EXP),
requestFieldWithPath("groups").description(MgmtApiModelProperties.ROLLOUT_GROUPS),
requestFieldWithPath("groups[].name").description(ApiModelPropertiesGeneric.NAME),
requestFieldWithPath("groups[].description").description(ApiModelPropertiesGeneric.DESCRPTION),
optionalRequestFieldWithPath("groups[].targetFilterQuery")
.description(MgmtApiModelProperties.ROLLOUT_GROUP_FILTER_QUERY),
optionalRequestFieldWithPath("groups[].targetPercentage")
.description(MgmtApiModelProperties.ROLLOUT_GROUP_TARGET_PERCENTAGE)
.attributes(key("value").value("0..100")),
optionalRequestFieldWithPath("groups[].successCondition")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION),
optionalRequestFieldWithPath("groups[].successCondition.condition")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_CONDITION)
.attributes(key("value").value("['threshold']")),
optionalRequestFieldWithPath("groups[].successCondition.expression")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_EXP),
optionalRequestFieldWithPath("groups[].successAction")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION),
optionalRequestFieldWithPath("groups[].successAction.action")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_ACTION)
.attributes(key("value").value("['nextgroup']")),
optionalRequestFieldWithPath("groups[].successAction.expression")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_EXP),
optionalRequestFieldWithPath("groups[].errorCondition")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION),
optionalRequestFieldWithPath("groups[].errorCondition.condition")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_CONDITION)
.attributes(key("value").value("['threshold']")),
optionalRequestFieldWithPath("groups[].errorCondition.expression")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_EXP),
optionalRequestFieldWithPath("groups[].errorAction")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION),
optionalRequestFieldWithPath("groups[].errorAction.action")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_ACTION)
.attributes(key("value").value("['pause']")),
optionalRequestFieldWithPath("groups[].errorAction.expression")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_EXP)),
.andDo(this.document.document(
requestFields(requestFieldWithPath("name").description(ApiModelPropertiesGeneric.NAME),
requestFieldWithPathMandatoryInMultiAssignMode("weight")
.type(JsonFieldType.NUMBER)
.description(MgmtApiModelProperties.RESULTING_ACTIONS_WEIGHT)
.attributes(key("value").value("0 - 1000")),
requestFieldWithPath("distributionSetId")
.description(MgmtApiModelProperties.ROLLOUT_DS_ID),
requestFieldWithPath("targetFilterQuery")
.description(MgmtApiModelProperties.ROLLOUT_FILTER_QUERY),
optionalRequestFieldWithPath("description")
.description(ApiModelPropertiesGeneric.DESCRPTION),
optionalRequestFieldWithPath("successCondition")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION),
optionalRequestFieldWithPath("successCondition.condition")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_CONDITION)
.attributes(key("value").value("['threshold']")),
optionalRequestFieldWithPath("successCondition.expression")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_EXP),
optionalRequestFieldWithPath("successAction")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION),
optionalRequestFieldWithPath("successAction.action")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_ACTION)
.attributes(key("value").value("['nextgroup']")),
optionalRequestFieldWithPath("successAction.expression")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_EXP),
optionalRequestFieldWithPath("errorCondition")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION),
optionalRequestFieldWithPath("errorCondition.condition")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_CONDITION)
.attributes(key("value").value("['threshold']")),
optionalRequestFieldWithPath("errorCondition.expression")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_EXP),
optionalRequestFieldWithPath("errorAction")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION),
optionalRequestFieldWithPath("errorAction.action")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_ACTION)
.attributes(key("value").value("['pause']")),
optionalRequestFieldWithPath("errorAction.expression")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_EXP),
requestFieldWithPath("groups").description(MgmtApiModelProperties.ROLLOUT_GROUPS),
requestFieldWithPath("groups[].name").description(ApiModelPropertiesGeneric.NAME),
requestFieldWithPath("groups[].description")
.description(ApiModelPropertiesGeneric.DESCRPTION),
optionalRequestFieldWithPath("groups[].targetFilterQuery")
.description(MgmtApiModelProperties.ROLLOUT_GROUP_FILTER_QUERY),
optionalRequestFieldWithPath("groups[].targetPercentage")
.description(MgmtApiModelProperties.ROLLOUT_GROUP_TARGET_PERCENTAGE)
.attributes(key("value").value("0..100")),
optionalRequestFieldWithPath("groups[].successCondition")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION),
optionalRequestFieldWithPath("groups[].successCondition.condition")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_CONDITION)
.attributes(key("value").value("['threshold']")),
optionalRequestFieldWithPath("groups[].successCondition.expression")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_CONDITION_EXP),
optionalRequestFieldWithPath("groups[].successAction")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION),
optionalRequestFieldWithPath("groups[].successAction.action")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_ACTION)
.attributes(key("value").value("['nextgroup']")),
optionalRequestFieldWithPath("groups[].successAction.expression")
.description(MgmtApiModelProperties.ROLLOUT_SUCCESS_ACTION_EXP),
optionalRequestFieldWithPath("groups[].errorCondition")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION),
optionalRequestFieldWithPath("groups[].errorCondition.condition")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_CONDITION)
.attributes(key("value").value("['threshold']")),
optionalRequestFieldWithPath("groups[].errorCondition.expression")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_CONDITION_EXP),
optionalRequestFieldWithPath("groups[].errorAction")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION),
optionalRequestFieldWithPath("groups[].errorAction.action")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_ACTION)
.attributes(key("value").value("['pause']")),
optionalRequestFieldWithPath("groups[].errorAction.expression")
.description(MgmtApiModelProperties.ROLLOUT_ERROR_ACTION_EXP)),
getRolloutResponseFields(false, true)));
}
@@ -626,11 +635,13 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
private Rollout createRolloutEntity() {
testdataFactory.createTargets(20, "exampleTarget");
final Rollout rollout = rolloutManagement.create(
entityFactory.rollout().create().name("exampleRollout")
.targetFilterQuery("controllerId==exampleTarget*").set(testdataFactory.createDistributionSet()),
10, new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "10").build());
final RolloutCreate rolloutCreate = entityFactory.rollout().create().name("exampleRollout")
.targetFilterQuery("controllerId==exampleTarget*").set(testdataFactory.createDistributionSet());
if (isMultiAssignmentsEnabled()) {
rolloutCreate.weight(400);
}
final Rollout rollout = rolloutManagement.create(rolloutCreate, 10, new RolloutGroupConditionBuilder()
.withDefaults().successCondition(RolloutGroupSuccessCondition.THRESHOLD, "10").build());
// Run here, because Scheduler is disabled during tests
rolloutManagement.handleRollouts();

View File

@@ -31,6 +31,7 @@ import org.eclipse.hawkbit.rest.documentation.AbstractApiRestDocumentation;
import org.eclipse.hawkbit.rest.documentation.ApiModelPropertiesGeneric;
import org.eclipse.hawkbit.rest.documentation.MgmtApiModelProperties;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.MediaType;
@@ -183,15 +184,16 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
@Test
@Description("Handles the POST request of setting a distribution set for auto assignment within SP. Required Permission: CREATE_TARGET.")
public void postAutoAssignDS() throws Exception {
enableMultiAssignments();
final TargetFilterQuery tfq = createTargetFilterQuery();
final DistributionSet distributionSet = createDistributionSet();
final String filterByDistSet = "{\"id\":\"" + distributionSet.getId() + "\", \"type\":\""
+ MgmtActionType.SOFT.getName() + "\"}";
final String autoAssignBody = new JSONObject().put("id", distributionSet.getId())
.put("type", MgmtActionType.SOFT.getName()).put("weight", 200).toString();
this.mockMvc
.perform(
post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/{targetFilterQueryId}/autoAssignDS",
tfq.getId()).contentType(MediaType.APPLICATION_JSON).content(filterByDistSet))
tfq.getId()).contentType(MediaType.APPLICATION_JSON).content(autoAssignBody.toString()))
.andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andDo(this.document.document(
pathParameters(parameterWithName("targetFilterQueryId")
@@ -199,7 +201,10 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
requestFields(requestFieldWithPath("id").description(MgmtApiModelProperties.DS_ID),
optionalRequestFieldWithPath("type")
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE)
.attributes(key("value").value("['forced', 'soft', 'downloadonly']"))),
.attributes(key("value").value("['forced', 'soft', 'downloadonly']")),
requestFieldWithPathMandatoryInMultiAssignMode("weight")
.description(MgmtApiModelProperties.RESULTING_ACTIONS_WEIGHT)
.attributes(key("value").value("0 - 1000"))),
getResponseFieldTargetFilterQuery(false)));
}
@@ -227,6 +232,9 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
fieldWithPath(arrayPrefix + "autoAssignActionType")
.description(MgmtApiModelProperties.ACTION_FORCE_TYPE).type(JsonFieldType.STRING.toString())
.attributes(key("value").value("['forced', 'soft', 'downloadonly']")),
fieldWithPath(arrayPrefix + "autoAssignWeight")
.description(MgmtApiModelProperties.RESULTING_ACTIONS_WEIGHT)
.type(JsonFieldType.NUMBER.toString()),
fieldWithPath(arrayPrefix + "createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
fieldWithPath(arrayPrefix + "createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
fieldWithPath(arrayPrefix + "lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT),
@@ -235,7 +243,8 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
.description(MgmtApiModelProperties.TARGET_FILTER_QUERY_LINK_AUTO_ASSIGN_DS));
}
private String createTargetFilterQueryJson(final String name, final String query) throws JsonProcessingException {
private String createTargetFilterQueryJson(final String name, final String query)
throws JsonProcessingException {
final Map<String, Object> target = new HashMap<>();
target.put("name", name);
target.put("query", query);
@@ -249,6 +258,7 @@ public class TargetFilterQueriesResourceDocumentationTest extends AbstractApiRes
private TargetFilterQuery createTargetFilterQueryWithDS(final DistributionSet distributionSet) {
final TargetFilterQuery targetFilterQuery = createTargetFilterQuery();
return targetFilterQueryManagement.updateAutoAssignDS(targetFilterQuery.getId(), distributionSet.getId());
return targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(targetFilterQuery.getId()).ds(distributionSet.getId()));
}
}

View File

@@ -203,6 +203,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test
@Description("Handles the GET request of retrieving the full action history of a specific target. Required Permission: READ_TARGET.")
public void getActionsFromTarget() throws Exception {
enableMultiAssignments();
generateActionForTarget(targetId);
mockMvc.perform(
@@ -229,12 +230,14 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
.description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
.attributes(key("value").value("['finished', 'pending']")),
fieldWithPath("content[]._links").description(MgmtApiModelProperties.LINK_TO_ACTION),
fieldWithPath("content[].id").description(MgmtApiModelProperties.ACTION_ID))));
fieldWithPath("content[].id").description(MgmtApiModelProperties.ACTION_ID),
fieldWithPath("content[].weight").description(MgmtApiModelProperties.ACTION_WEIGHT))));
}
@Test
@Description("Handles the GET request of retrieving the full action history of a specific target with maintenance window. Required Permission: READ_TARGET.")
public void getActionsFromTargetWithMaintenanceWindow() throws Exception {
enableMultiAssignments();
generateActionForTarget(targetId, true, false, getTestSchedule(2), getTestDuration(1), getTestTimeZone());
mockMvc.perform(
@@ -262,6 +265,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
.attributes(key("value").value("['finished', 'pending']")),
fieldWithPath("content[]._links").description(MgmtApiModelProperties.LINK_TO_ACTION),
fieldWithPath("content[].id").description(MgmtApiModelProperties.ACTION_ID),
fieldWithPath("content[].weight").description(MgmtApiModelProperties.ACTION_WEIGHT),
fieldWithPath("content[].maintenanceWindow")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW),
fieldWithPath("content[].maintenanceWindow.schedule")
@@ -317,6 +321,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test
@Description("Handles the GET request of retrieving a specific action on a specific target. Required Permission: READ_TARGET.")
public void getActionFromTarget() throws Exception {
enableMultiAssignments();
final Action action = generateActionForTarget(targetId, true, true);
assertThat(deploymentManagement.findAction(action.getId()).get().getActionType())
.isEqualTo(ActionType.TIMEFORCED);
@@ -330,6 +335,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
responseFields(fieldWithPath("createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
fieldWithPath("createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
fieldWithPath("id").description(MgmtApiModelProperties.ACTION_ID),
fieldWithPath("weight").description(MgmtApiModelProperties.ACTION_WEIGHT),
fieldWithPath("lastModifiedBy").description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY)
.type("String"),
fieldWithPath("lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT)
@@ -351,6 +357,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Test
@Description("Handles the GET request of retrieving a specific action on a specific target. Required Permission: READ_TARGET.")
public void getActionFromTargetWithMaintenanceWindow() throws Exception {
enableMultiAssignments();
final Action action = generateActionForTarget(targetId, true, true, getTestSchedule(2), getTestDuration(1),
getTestTimeZone());
@@ -363,6 +370,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
responseFields(fieldWithPath("createdBy").description(ApiModelPropertiesGeneric.CREATED_BY),
fieldWithPath("createdAt").description(ApiModelPropertiesGeneric.CREATED_AT),
fieldWithPath("id").description(MgmtApiModelProperties.ACTION_ID),
fieldWithPath("weight").description(MgmtApiModelProperties.ACTION_WEIGHT),
fieldWithPath("lastModifiedBy").description(ApiModelPropertiesGeneric.LAST_MODIFIED_BY)
.type("String"),
fieldWithPath("lastModifiedAt").description(ApiModelPropertiesGeneric.LAST_MODIFIED_AT)
@@ -514,6 +522,9 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
.description(MgmtApiModelProperties.OFFLINE_UPDATE).optional()),
requestFields(
requestFieldWithPath("id").description(ApiModelPropertiesGeneric.ITEM_ID),
requestFieldWithPathMandatoryInMultiAssignMode("weight")
.description(MgmtApiModelProperties.ASSIGNMENT_WEIGHT)
.type(JsonFieldType.NUMBER).attributes(key("value").value("0 - 1000")),
optionalRequestFieldWithPath("forcetime").description(MgmtApiModelProperties.FORCETIME),
optionalRequestFieldWithPath("maintenanceWindow")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW),
@@ -537,12 +548,12 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
final long forceTime = System.currentTimeMillis();
final JSONArray body = new JSONArray();
body.put(new JSONObject().put("id", sets.get(1).getId()).put("type", "timeforced")
body.put(new JSONObject().put("id", sets.get(1).getId()).put("weight", 500).put("type", "timeforced")
.put("forcetime", forceTime)
.put("maintenanceWindow", new JSONObject().put("schedule", getTestSchedule(100))
.put("duration", getTestDuration(10)).put("timezone", getTestTimeZone())))
.toString();
body.put(new JSONObject().put("id", sets.get(0).getId()).put("type", "forced"));
body.put(new JSONObject().put("id", sets.get(0).getId()).put("type", "forced").put("weight", 800));
enableMultiAssignments();
mockMvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/"
@@ -555,6 +566,9 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
.description(MgmtApiModelProperties.OFFLINE_UPDATE).optional()),
requestFields(
requestFieldWithPath("[].id").description(ApiModelPropertiesGeneric.ITEM_ID),
requestFieldWithPathMandatoryInMultiAssignMode("[].weight")
.description(MgmtApiModelProperties.ASSIGNMENT_WEIGHT)
.attributes(key("value").value("0 - 1000")),
optionalRequestFieldWithPath("[].forcetime")
.description(MgmtApiModelProperties.FORCETIME),
optionalRequestFieldWithPath("[].maintenanceWindow")