Integrated Maintenance Window fields in Management API and UI (#677)

* Added Maintenance Window properties to API and UI

* extended Management API with Maintenance Window schedule, duration, timezone and nextAt properties
* extended integration tests for the above properties
* extended Management UI with Maintenance Window column in Action History grid, added tooltip for next execution
* general refactoring

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>

* fixed Sonar issues

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>

* changed the documentation help link for maintenance window

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>

* added licence header, first refactoring after partial PR review

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>

* changes related to PR review findings

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>

* last PR review findings

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>
This commit is contained in:
Bondar Bogdan
2018-05-15 11:12:33 +02:00
committed by Dominic Schabel
parent 1deb47a4db
commit b5114081be
22 changed files with 474 additions and 210 deletions

View File

@@ -12,6 +12,7 @@ import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindowRequestBody;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadataBodyPut;
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
@@ -252,14 +253,16 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
final DistributionSetAssignmentResult assignDistributionSet = this.deployManagament
.assignDistributionSet(distributionSetId, assignments.stream().map(t -> {
if (t.getMaintenanceWindow() == null) {
final MgmtMaintenanceWindowRequestBody maintenanceWindow = t.getMaintenanceWindow();
if (maintenanceWindow == null) {
return new TargetWithActionType(t.getId(), MgmtRestModelMapper.convertActionType(t.getType()),
t.getForcetime());
}
final String cronSchedule = t.getMaintenanceWindow().getMaintenanceSchedule();
final String duration = t.getMaintenanceWindow().getMaintenanceWindowDuration();
final String timezone = t.getMaintenanceWindow().getMaintenanceWindowTimeZone();
final String cronSchedule = maintenanceWindow.getSchedule();
final String duration = maintenanceWindow.getDuration();
final String timezone = maintenanceWindow.getTimezone();
MaintenanceScheduleHelper.validateMaintenanceSchedule(cronSchedule, duration, timezone);

View File

@@ -19,6 +19,7 @@ import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindow;
import org.eclipse.hawkbit.mgmt.json.model.MgmtPollStatus;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionStatus;
@@ -194,6 +195,16 @@ public final class MgmtTargetMapper {
result.setStatus(MgmtAction.ACTION_FINISHED);
}
if (action.hasMaintenanceSchedule()) {
final MgmtMaintenanceWindow maintenanceWindow = new MgmtMaintenanceWindow();
maintenanceWindow.setSchedule(action.getMaintenanceWindowSchedule());
maintenanceWindow.setDuration(action.getMaintenanceWindowDuration());
maintenanceWindow.setTimezone(action.getMaintenanceWindowTimeZone());
action.getMaintenanceWindowStartTime()
.ifPresent(nextStart -> maintenanceWindow.setNextStartAt(nextStart.toInstant().toEpochMilli()));
result.setMaintenanceWindow(maintenanceWindow);
}
MgmtRestModelMapper.mapBaseToBase(result, action);
result.add(linkTo(methodOn(MgmtTargetRestApi.class).getAction(targetId, action.getId())).withSelfRel());

View File

@@ -15,7 +15,7 @@ import java.util.Map;
import javax.validation.ValidationException;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindow;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindowRequestBody;
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionRequestBodyPut;
@@ -276,7 +276,7 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
}
findTargetWithExceptionIfNotFound(controllerId);
final MgmtMaintenanceWindow maintenanceWindow = dsId.getMaintenanceWindow();
final MgmtMaintenanceWindowRequestBody maintenanceWindow = dsId.getMaintenanceWindow();
if (maintenanceWindow == null) {
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponse(this.deploymentManagement
@@ -284,9 +284,9 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
MgmtRestModelMapper.convertActionType(dsId.getType()), dsId.getForcetime())))));
}
final String cronSchedule = maintenanceWindow.getMaintenanceSchedule();
final String duration = maintenanceWindow.getMaintenanceWindowDuration();
final String timezone = maintenanceWindow.getMaintenanceWindowTimeZone();
final String cronSchedule = maintenanceWindow.getSchedule();
final String duration = maintenanceWindow.getDuration();
final String timezone = maintenanceWindow.getTimezone();
MaintenanceScheduleHelper.validateMaintenanceSchedule(cronSchedule, duration, timezone);

View File

@@ -853,17 +853,97 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
}
@Test
@Description("Ensures that the expected response is return when update was cancelled.")
@Description("Ensures that the expected response is returned for update action.")
public void getUpdateAction() throws Exception {
final String knownTargetId = "targetId";
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + actions.get(1).getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("id", equalTo(actions.get(1).getId().intValue())))
.andExpect(jsonPath("type", equalTo("update"))).andExpect(jsonPath("status", equalTo("pending")))
.andExpect(jsonPath("forceType", equalTo("forced")))
.andExpect(jsonPath("maintenanceWindow").doesNotExist())
.andExpect(jsonPath("_links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(1).getId()))))
.andExpect(jsonPath("_links.distributionset.href",
equalTo(generateActionDsLink(actions.get(1).getDistributionSet().getId()))))
.andExpect(jsonPath("_links.status.href",
equalTo(generateStatusreferenceLink(knownTargetId, actions.get(1).getId()))));
}
@Test
@Description("Ensures that the expected response is returned for update action with maintenance window.")
public void getUpdateActionWithMaintenanceWindow() throws Exception {
final String knownTargetId = "targetId";
final String schedule = getTestSchedule(10);
final String duration = getTestDuration(10);
final String timezone = getTestTimeZone();
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverrideWithMaintenanceWindow(knownTargetId,
schedule, duration, timezone);
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + actions.get(1).getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("id", equalTo(actions.get(1).getId().intValue())))
.andExpect(jsonPath("type", equalTo("update"))).andExpect(jsonPath("status", equalTo("pending")))
.andExpect(jsonPath("forceType", equalTo("forced")))
.andExpect(jsonPath("maintenanceWindow.schedule", equalTo(schedule)))
.andExpect(jsonPath("maintenanceWindow.duration", equalTo(duration)))
.andExpect(jsonPath("maintenanceWindow.timezone", equalTo(timezone)))
.andExpect(jsonPath("maintenanceWindow.nextStartAt",
equalTo(actions.get(1).getMaintenanceWindowStartTime().get().toInstant().toEpochMilli())))
.andExpect(jsonPath("_links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(1).getId()))))
.andExpect(jsonPath("_links.distributionset.href",
equalTo(generateActionDsLink(actions.get(1).getDistributionSet().getId()))))
.andExpect(jsonPath("_links.status.href",
equalTo(generateStatusreferenceLink(knownTargetId, actions.get(1).getId()))));
}
@Test
@Description("Ensures that the expected response is returned when update action was cancelled.")
public void getCancelAction() throws Exception {
final String knownTargetId = "targetId";
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + actions.get(0).getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("id", equalTo(actions.get(0).getId().intValue())))
.andExpect(jsonPath("type", equalTo("cancel"))).andExpect(jsonPath("status", equalTo("pending")))
.andExpect(jsonPath("forceType", equalTo("forced")))
.andExpect(jsonPath("maintenanceWindow").doesNotExist())
.andExpect(jsonPath("_links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(0).getId()))))
.andExpect(jsonPath("_links.canceledaction.href",
equalTo(generateCanceledactionreferenceLink(knownTargetId, actions.get(0)))))
.andExpect(jsonPath("_links.status.href",
equalTo(generateStatusreferenceLink(knownTargetId, actions.get(0).getId()))));
}
@Test
@Description("Ensures that the expected response is returned when update action with maintenance window was cancelled.")
public void getCancelActionWithMaintenanceWindow() throws Exception {
final String knownTargetId = "targetId";
final String schedule = getTestSchedule(10);
final String duration = getTestDuration(10);
final String timezone = getTestTimeZone();
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverrideWithMaintenanceWindow(knownTargetId,
schedule, duration, timezone);
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + actions.get(0).getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("id", equalTo(actions.get(0).getId().intValue())))
.andExpect(jsonPath("forceType", equalTo("forced"))).andExpect(jsonPath("type", equalTo("cancel")))
.andExpect(jsonPath("status", equalTo("pending")))
.andExpect(jsonPath("maintenanceWindow.schedule", equalTo(schedule)))
.andExpect(jsonPath("maintenanceWindow.duration", equalTo(duration)))
.andExpect(jsonPath("maintenanceWindow.timezone", equalTo(timezone)))
.andExpect(jsonPath("maintenanceWindow.nextStartAt",
equalTo(actions.get(0).getMaintenanceWindowStartTime().get().toInstant().toEpochMilli())))
.andExpect(jsonPath("_links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(0).getId()))))
.andExpect(jsonPath("_links.canceledaction.href",
@@ -896,6 +976,44 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2)));
}
@Test
@Description("Ensures that the expected response of getting actions with maintenance window of a target is returned.")
public void getMultipleActionsWithMaintenanceWindow() throws Exception {
final String knownTargetId = "targetId";
final String schedule = getTestSchedule(10);
final String duration = getTestDuration(10);
final String timezone = getTestTimeZone();
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverrideWithMaintenanceWindow(knownTargetId,
schedule, duration, timezone);
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS).param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "ID:ASC"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("content.[1].id", equalTo(actions.get(1).getId().intValue())))
.andExpect(jsonPath("content.[1].type", equalTo("update")))
.andExpect(jsonPath("content.[1].status", equalTo("pending")))
.andExpect(jsonPath("content.[1].maintenanceWindow.schedule", equalTo(schedule)))
.andExpect(jsonPath("content.[1].maintenanceWindow.duration", equalTo(duration)))
.andExpect(jsonPath("content.[1].maintenanceWindow.timezone", equalTo(timezone)))
.andExpect(jsonPath("content.[1].maintenanceWindow.nextStartAt",
equalTo(actions.get(1).getMaintenanceWindowStartTime().get().toInstant().toEpochMilli())))
.andExpect(jsonPath("content.[1]._links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(1).getId()))))
.andExpect(jsonPath("content.[0].id", equalTo(actions.get(0).getId().intValue())))
.andExpect(jsonPath("content.[0].type", equalTo("cancel")))
.andExpect(jsonPath("content.[0].status", equalTo("pending")))
.andExpect(jsonPath("content.[0].maintenanceWindow.schedule", equalTo(schedule)))
.andExpect(jsonPath("content.[0].maintenanceWindow.duration", equalTo(duration)))
.andExpect(jsonPath("content.[0].maintenanceWindow.timezone", equalTo(timezone)))
.andExpect(jsonPath("content.[0].maintenanceWindow.nextStartAt",
equalTo(actions.get(0).getMaintenanceWindowStartTime().get().toInstant().toEpochMilli())))
.andExpect(jsonPath("content.[0]._links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(0).getId()))))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_TOTAL, equalTo(2)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_SIZE, equalTo(2)))
.andExpect(jsonPath(JSON_PATH_PAGED_LIST_CONTENT, hasSize(2)));
}
@Test
@Description("Verifies that the API returns the status list with expected content.")
public void getMultipleActionStatus() throws Exception {
@@ -1054,6 +1172,10 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + actionId;
}
private String generateActionDsLink(final Long dsId) {
return "http://localhost" + MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + dsId;
}
private String generateCanceledactionreferenceLink(final String knownTargetId, final Action action) {
return "http://localhost" + MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + action.getId();
@@ -1067,20 +1189,33 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
private List<Action> generateTargetWithTwoUpdatesWithOneOverride(final String knownTargetId)
throws InterruptedException {
return generateTargetWithTwoUpdatesWithOneOverrideWithMaintenanceWindow(knownTargetId, null, null, null);
}
private List<Action> generateTargetWithTwoUpdatesWithOneOverrideWithMaintenanceWindow(final String knownTargetId,
final String schedule, final String duration, final String timezone) throws InterruptedException {
final Target target = testdataFactory.createTarget(knownTargetId);
final List<Target> targets = Arrays.asList(target);
final Iterator<DistributionSet> sets = testdataFactory.createDistributionSets(2).iterator();
final DistributionSet one = sets.next();
final DistributionSet two = sets.next();
// Update
final List<Target> updatedTargets = assignDistributionSet(one, targets).getAssignedEntity();
// 2nd update
// sleep 10ms to ensure that we can sort by reportedAt
Thread.sleep(10);
assignDistributionSet(two, updatedTargets);
if (schedule == null) {
final List<Target> updatedTargets = assignDistributionSet(one, Arrays.asList(target)).getAssignedEntity();
// 2nd update
// sleep 10ms to ensure that we can sort by reportedAt
Thread.sleep(10);
assignDistributionSet(two, updatedTargets);
} else {
final List<Target> updatedTargets = assignDistributionSetWithMaintenanceWindow(one.getId(),
target.getControllerId(), schedule, duration, timezone).getAssignedEntity();
// 2nd update
// sleep 10ms to ensure that we can sort by reportedAt
Thread.sleep(10);
assignDistributionSetWithMaintenanceWindow(two.getId(), updatedTargets.get(0).getControllerId(), schedule,
duration, timezone);
}
// two updates, one cancellation
final List<Action> actions = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE)
@@ -1090,26 +1225,6 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
return actions;
}
@Test
public void getUpdateAction() throws Exception {
final String knownTargetId = "targetId";
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS + "/" + actions.get(1).getId()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("id", equalTo(actions.get(1).getId().intValue())))
.andExpect(jsonPath("type", equalTo("update"))).andExpect(jsonPath("status", equalTo("pending")))
.andExpect(jsonPath("forceType", equalTo("forced")))
.andExpect(jsonPath("_links.self.href",
equalTo(generateActionSelfLink(knownTargetId, actions.get(1).getId()))))
.andExpect(jsonPath("_links.distributionset.href",
equalTo("http://localhost/rest/v1/distributionsets/"
+ actions.get(1).getDistributionSet().getId())))
.andExpect(jsonPath("_links.status.href",
equalTo(generateStatusreferenceLink(knownTargetId, actions.get(1).getId()))));
}
@Test
@Description("Verfies that an action is switched from soft to forced if requested by management API")
public void updateAction() throws Exception {
@@ -1266,6 +1381,28 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
.andExpect(status().isOk());
}
@Test
@Description("Assigns distribution set to target with maintenance window next execution start (should be ignored, calculated automaticaly based on schedule, duration and timezone)")
public void assignDistributionSetToTargetWithMaintenanceWindowNextExecutionStart() throws Exception {
final Target target = testdataFactory.createTarget("fsdfsd");
final DistributionSet set = testdataFactory.createDistributionSet("one");
final long nextExecutionStart = System.currentTimeMillis();
final String body = new JSONObject().put("id", set.getId())
.put("maintenanceWindow", getMaintenanceWindowWithNextStart(getTestSchedule(10), getTestDuration(10),
getTestTimeZone(), nextExecutionStart))
.toString();
mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + target.getControllerId() + "/assignedDS")
.content(body).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + target.getControllerId() + "/"
+ MgmtRestConstants.TARGET_V1_ACTIONS)).andExpect(status().isOk()).andDo(MockMvcResultPrinter.print())
.andExpect(jsonPath("content.[0].maintenanceWindow.nextStartAt", not(nextExecutionStart)));
}
@Test
@Description("Assigns distribution set to target with last maintenance window scheduled before current time.")
public void assignDistributionSetToTargetWithMaintenanceWindowEndTimeBeforeStartTime() throws Exception {