ManagementApi: Action does not have property containing rollout name (#1116)

* Mgmt Rest API: Enhance Action payload with rollout properties

Signed-off-by: Stefan Behl <stefan.behl@bosch.io>

* Enhance Mgmt REST API docs

Signed-off-by: Stefan Behl <stefan.behl@bosch.io>

* Minor changes

Signed-off-by: Stefan Behl <stefan.behl@bosch.io>
This commit is contained in:
Stefan Behl
2021-05-18 13:50:05 +02:00
committed by GitHub
parent efa21469cd
commit 069d717be1
7 changed files with 150 additions and 18 deletions

View File

@@ -65,6 +65,12 @@ public class MgmtAction extends MgmtBaseEntity {
@JsonProperty @JsonProperty
private MgmtMaintenanceWindow maintenanceWindow; private MgmtMaintenanceWindow maintenanceWindow;
@JsonProperty
private Long rollout;
@JsonProperty
private String rolloutName;
public MgmtMaintenanceWindow getMaintenanceWindow() { public MgmtMaintenanceWindow getMaintenanceWindow() {
return maintenanceWindow; return maintenanceWindow;
@@ -121,4 +127,21 @@ public class MgmtAction extends MgmtBaseEntity {
public void setType(final String type) { public void setType(final String type) {
this.type = type; this.type = type;
} }
public Long getRollout() {
return rollout;
}
public void setRollout(Long rollout) {
this.rollout = rollout;
}
public String getRolloutName() {
return rolloutName;
}
public void setRolloutName(String rolloutName) {
this.rolloutName = rolloutName;
}
} }

View File

@@ -84,6 +84,10 @@ public final class MgmtRestConstants {
* The target URL mapping, href link for canceled actions. * The target URL mapping, href link for canceled actions.
*/ */
public static final String TARGET_V1_ACTION_STATUS = "status"; public static final String TARGET_V1_ACTION_STATUS = "status";
/**
* The target URL mapping, href link for a rollout.
*/
public static final String TARGET_V1_ROLLOUT = "rollout";
/** /**
* The target URL mapping rest resource. * The target URL mapping rest resource.

View File

@@ -28,6 +28,7 @@ import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody; import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetRestApi;
import org.eclipse.hawkbit.repository.ActionFields; import org.eclipse.hawkbit.repository.ActionFields;
import org.eclipse.hawkbit.repository.ActionStatusFields; import org.eclipse.hawkbit.repository.ActionStatusFields;
@@ -39,6 +40,7 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.MetaData; import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.PollStatus; import org.eclipse.hawkbit.repository.model.PollStatus;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata; import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.rest.data.ResponseList; import org.eclipse.hawkbit.rest.data.ResponseList;
@@ -215,6 +217,12 @@ public final class MgmtTargetMapper {
result.setStatus(MgmtAction.ACTION_FINISHED); result.setStatus(MgmtAction.ACTION_FINISHED);
} }
Rollout rollout = action.getRollout();
if (rollout != null) {
result.setRollout(rollout.getId());
result.setRolloutName(rollout.getName());
}
if (action.hasMaintenanceSchedule()) { if (action.hasMaintenanceSchedule()) {
final MgmtMaintenanceWindow maintenanceWindow = new MgmtMaintenanceWindow(); final MgmtMaintenanceWindow maintenanceWindow = new MgmtMaintenanceWindow();
maintenanceWindow.setSchedule(action.getMaintenanceWindowSchedule()); maintenanceWindow.setSchedule(action.getMaintenanceWindowSchedule());
@@ -248,6 +256,12 @@ public final class MgmtTargetMapper {
ActionStatusFields.ID.getFieldName() + ":" + SortDirection.DESC)) ActionStatusFields.ID.getFieldName() + ":" + SortDirection.DESC))
.withRel(MgmtRestConstants.TARGET_V1_ACTION_STATUS)); .withRel(MgmtRestConstants.TARGET_V1_ACTION_STATUS));
final Rollout rollout = action.getRollout();
if (rollout != null) {
result.add(linkTo(methodOn(MgmtRolloutRestApi.class).getRollout(rollout.getId()))
.withRel(MgmtRestConstants.TARGET_V1_ROLLOUT));
}
return result; return result;
} }

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.mgmt.rest.resource;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItem;
@@ -51,6 +52,7 @@ import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.MetaData; import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.NamedEntity; import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata; import org.eclipse.hawkbit.repository.model.TargetMetadata;
@@ -2039,5 +2041,38 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
} }
} }
@Test
@Description("An action provides information of the rollout it was created for (if any).")
public void getActionWithRolloutInfo() throws Exception {
// setup
final int amountTargets = 10;
final List<Target> targets = testdataFactory.createTargets(amountTargets, "trg", "trg");
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Rollout rollout = testdataFactory.createRolloutByVariables("My Rollout", "My Rollout Description", 1,
"name==trg*", ds, "50", "5");
rolloutManagement.start(rollout.getId());
rolloutManagement.handleRollouts();
// get all actions for the first target
final Target target = targets.get(0);
mvc.perform(get("/rest/v1/targets/{targetId}/actions", target.getControllerId())).andExpect(status().isOk())
.andDo(MockMvcResultPrinter.print())
.andExpect(jsonPath("content.[0].rollout", equalTo(rollout.getId().intValue())))
.andExpect(jsonPath("content.[0].rolloutName", equalTo(rollout.getName())));
// get the first action for the first target;
// verify that also the rollout link is present
final Slice<Action> action = deploymentManagement.findActionsByTarget(target.getControllerId(),
PageRequest.of(0, 100));
assertThat(action.getContent()).hasSize(1);
mvc.perform(get("/rest/v1/targets/{targetId}/actions/{actionId}", target.getControllerId(),
action.getContent().get(0).getId())).andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andExpect(jsonPath("$.rollout", equalTo(rollout.getId().intValue())))
.andExpect(jsonPath("$.rolloutName", equalTo(rollout.getName()))).andExpect(jsonPath(
"$._links.rollout.href", containsString("/rest/v1/rollouts/" + rollout.getId().intValue())));
}
} }

View File

@@ -19,6 +19,7 @@ import static org.springframework.restdocs.snippet.Attributes.key;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -33,6 +34,7 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ArtifactUpload; import org.eclipse.hawkbit.repository.model.ArtifactUpload;
import org.eclipse.hawkbit.repository.model.DeploymentRequestBuilder; import org.eclipse.hawkbit.repository.model.DeploymentRequestBuilder;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.TestConfiguration; import org.eclipse.hawkbit.repository.test.TestConfiguration;
@@ -161,28 +163,52 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
protected Target createTargetByGivenNameWithAttributes(final String name, final boolean inSync, protected Target createTargetByGivenNameWithAttributes(final String name, final boolean inSync,
final boolean timeforced, final DistributionSet distributionSet) { final boolean timeforced, final DistributionSet distributionSet) {
return createTargetByGivenNameWithAttributes(name, inSync, timeforced, distributionSet, null, null, null); return createTargetByGivenNameWithAttributes(name, inSync, timeforced, distributionSet, null, null, null, false);
}
protected Target createTargetByGivenNameWithAttributes(final String name, final boolean inSync,
final boolean timeforced, final DistributionSet distributionSet, final boolean createRollout) {
return createTargetByGivenNameWithAttributes(name, inSync, timeforced, distributionSet, null, null, null, createRollout);
} }
protected Target createTargetByGivenNameWithAttributes(final String name, final boolean inSync, protected Target createTargetByGivenNameWithAttributes(final String name, final boolean inSync,
final boolean timeforced, final DistributionSet distributionSet, final String maintenanceWindowSchedule, final boolean timeforced, final DistributionSet distributionSet, final String maintenanceWindowSchedule,
final String maintenanceWindowDuration, final String maintenanceWindowTimeZone) { final String maintenanceWindowDuration, final String maintenanceWindowTimeZone, final boolean createRollout) {
final Target savedTarget = targetManagement.create(entityFactory.target().create().controllerId(name) final Target savedTarget = targetManagement.create(entityFactory.target().create().controllerId(name)
.status(TargetUpdateStatus.UNKNOWN).address("http://192.168.0.1").description("My name is " + name) .status(TargetUpdateStatus.UNKNOWN).address("http://192.168.0.1").description("My name is " + name)
.lastTargetQuery(System.currentTimeMillis())); .lastTargetQuery(System.currentTimeMillis()));
final DeploymentRequestBuilder deploymentRequestBuilder = DeploymentManagement
.deploymentRequest(savedTarget.getControllerId(), distributionSet.getId()) final List<Target> updatedTargets;
.setMaintenance(maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone); if (createRollout) {
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());
final Rollout rollout = testdataFactory.createRolloutByVariables("rollout", "rollout desc", 1,
"name==" + name, distributionSet, "50", "5", timeforced ? ActionType.TIMEFORCED : ActionType.FORCED,
isMultiAssignmentsEnabled() ? 600 : null);
// start the rollout and handle it
rolloutManagement.start(rollout.getId());
rolloutManagement.handleRollouts();
updatedTargets = Collections.singletonList(savedTarget);
} else {
final DeploymentRequestBuilder deploymentRequestBuilder = DeploymentManagement
.deploymentRequest(savedTarget.getControllerId(), distributionSet.getId())
.setMaintenance(maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone);
if (timeforced) {
deploymentRequestBuilder.setActionType(ActionType.TIMEFORCED);
}
if (isMultiAssignmentsEnabled()) {
deploymentRequestBuilder.setWeight(600);
}
updatedTargets = makeAssignment(deploymentRequestBuilder.build()).getAssignedEntity().stream()
.map(Action::getTarget).collect(Collectors.toList());
}
if (inSync) { if (inSync) {
feedbackToByInSync(distributionSet); feedbackToByInSync(distributionSet);
} }

View File

@@ -31,6 +31,7 @@ public final class MgmtApiModelProperties {
public static final String LINK_TO_METADATA = "The link to the metadata."; public static final String LINK_TO_METADATA = "The link to the metadata.";
public static final String LINK_TO_MANDATORY_SMT = "Link to mandatory software modules types in this distribution set type."; public static final String LINK_TO_MANDATORY_SMT = "Link to mandatory software modules types in this distribution set type.";
public static final String LINK_TO_OPTIONAL_SMT = "Link to optional software modules types in this distribution set type."; public static final String LINK_TO_OPTIONAL_SMT = "Link to optional software modules types in this distribution set type.";
public static final String LINK_TO_ROLLOUT = "The link to the rollout.";
// software module types // software module types
public static final String SMT_TYPE = "The type of the software module identified by its key."; public static final String SMT_TYPE = "The type of the software module identified by its key.";
@@ -143,6 +144,10 @@ public final class MgmtApiModelProperties {
public static final String ACTION_WEIGHT = "Weight of the action showing the importance of the update."; public static final String ACTION_WEIGHT = "Weight of the action showing the importance of the update.";
public static final String ACTION_ROLLOUT = "The ID of the rollout this action was created for.";
public static final String ACTION_ROLLOUT_NAME = "The name of the rollout this action was created for.";
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 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."; 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.";

View File

@@ -201,7 +201,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Description("Handles the GET request of retrieving the full action history of a specific target. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving the full action history of a specific target. Required Permission: READ_TARGET.")
public void getActionsFromTarget() throws Exception { public void getActionsFromTarget() throws Exception {
enableMultiAssignments(); enableMultiAssignments();
generateActionForTarget(targetId); generateRolloutActionForTarget(targetId);
mockMvc.perform( mockMvc.perform(
get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/" + MgmtRestConstants.TARGET_V1_ACTIONS, get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/" + MgmtRestConstants.TARGET_V1_ACTIONS,
@@ -228,7 +228,10 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
.attributes(key("value").value("['finished', 'pending']")), .attributes(key("value").value("['finished', 'pending']")),
fieldWithPath("content[]._links").description(MgmtApiModelProperties.LINK_TO_ACTION), 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)))); fieldWithPath("content[].weight").description(MgmtApiModelProperties.ACTION_WEIGHT),
fieldWithPath("content[].rollout").description(MgmtApiModelProperties.ACTION_ROLLOUT),
fieldWithPath("content[].rolloutName")
.description(MgmtApiModelProperties.ACTION_ROLLOUT_NAME))));
} }
@Test @Test
@@ -323,7 +326,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
@Description("Handles the GET request of retrieving a specific action on a specific target. Required Permission: READ_TARGET.") @Description("Handles the GET request of retrieving a specific action on a specific target. Required Permission: READ_TARGET.")
public void getActionFromTarget() throws Exception { public void getActionFromTarget() throws Exception {
enableMultiAssignments(); enableMultiAssignments();
final Action action = generateActionForTarget(targetId, true, true); final Action action = generateRolloutActionForTarget(targetId, true, true);
assertThat(deploymentManagement.findAction(action.getId()).get().getActionType()) assertThat(deploymentManagement.findAction(action.getId()).get().getActionType())
.isEqualTo(ActionType.TIMEFORCED); .isEqualTo(ActionType.TIMEFORCED);
@@ -349,10 +352,15 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
.type("String"), .type("String"),
fieldWithPath("status").description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS) fieldWithPath("status").description(MgmtApiModelProperties.ACTION_EXECUTION_STATUS)
.attributes(key("value").value("['finished', 'pending']")), .attributes(key("value").value("['finished', 'pending']")),
fieldWithPath("rollout").description(MgmtApiModelProperties.ACTION_ROLLOUT),
fieldWithPath("rolloutName")
.description(MgmtApiModelProperties.ACTION_ROLLOUT_NAME),
fieldWithPath("_links.self").ignored(), fieldWithPath("_links.self").ignored(),
fieldWithPath("_links.distributionset").description(MgmtApiModelProperties.LINK_TO_DS), fieldWithPath("_links.distributionset").description(MgmtApiModelProperties.LINK_TO_DS),
fieldWithPath("_links.status") fieldWithPath("_links.status")
.description(MgmtApiModelProperties.LINKS_ACTION_STATUSES)))); .description(MgmtApiModelProperties.LINKS_ACTION_STATUSES),
fieldWithPath("_links.rollout")
.description(MgmtApiModelProperties.LINK_TO_ROLLOUT))));
} }
@Test @Test
@@ -820,10 +828,19 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
return generateActionForTarget(knownControllerId, true, false, null, null, null); return generateActionForTarget(knownControllerId, true, false, null, null, null);
} }
private Action generateRolloutActionForTarget(final String knownControllerId) throws Exception {
return generateActionForTarget(knownControllerId, true, false, null, null, null, true);
}
private Action generateActionForTarget(final String knownControllerId, final boolean inSync) throws Exception { private Action generateActionForTarget(final String knownControllerId, final boolean inSync) throws Exception {
return generateActionForTarget(knownControllerId, inSync, false, null, null, null); return generateActionForTarget(knownControllerId, inSync, false, null, null, null);
} }
private Action generateRolloutActionForTarget(final String knownControllerId, final boolean inSync,
final boolean timeforced) throws Exception {
return generateActionForTarget(knownControllerId, inSync, timeforced, null, null, null, true);
}
private Action generateActionForTarget(final String knownControllerId, final boolean inSync, private Action generateActionForTarget(final String knownControllerId, final boolean inSync,
final boolean timeforced) throws Exception { final boolean timeforced) throws Exception {
return generateActionForTarget(knownControllerId, inSync, timeforced, null, null, null); return generateActionForTarget(knownControllerId, inSync, timeforced, null, null, null);
@@ -832,14 +849,22 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
private Action generateActionForTarget(final String knownControllerId, final boolean inSync, private Action generateActionForTarget(final String knownControllerId, final boolean inSync,
final boolean timeforced, final String maintenanceWindowSchedule, final String maintenanceWindowDuration, final boolean timeforced, final String maintenanceWindowSchedule, final String maintenanceWindowDuration,
final String maintenanceWindowTimeZone) throws Exception { final String maintenanceWindowTimeZone) throws Exception {
return generateActionForTarget(knownControllerId, inSync, timeforced, maintenanceWindowSchedule,
maintenanceWindowDuration, maintenanceWindowTimeZone, false);
}
private Action generateActionForTarget(final String knownControllerId, final boolean inSync,
final boolean timeforced, final String maintenanceWindowSchedule, final String maintenanceWindowDuration,
final String maintenanceWindowTimeZone, final boolean createRollout) throws Exception {
final PageRequest pageRequest = PageRequest.of(0, 1, Direction.ASC, ActionStatusFields.ID.getFieldName()); final PageRequest pageRequest = PageRequest.of(0, 1, Direction.ASC, ActionStatusFields.ID.getFieldName());
createTargetByGivenNameWithAttributes(knownControllerId, inSync, timeforced, createDistributionSet(), createTargetByGivenNameWithAttributes(knownControllerId, inSync, timeforced, createDistributionSet(),
maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone); maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone, createRollout);
final List<Action> actions = deploymentManagement.findActionsAll(pageRequest).getContent(); final List<Action> actions = deploymentManagement.findActionsAll(pageRequest).getContent();
assertThat(actions).hasSize(1); assertThat(actions).hasSize(1);
return actions.get(0); return actions.get(0);
} }
} }