Assign multiple distribution sets to a target via mgmt api (#886)

* Add multiassignment to mgmt api target endpoint
* Remove single assignment ds to targets offline
* Fix tests
* Add quota for maxResultingActionsPerManualAssignment
* Fix assignment with same target or distribution set multiple times in one request
* Log UI error
* Add tests
* Enable single assignment requests with multiple DSs and types
* Remove redundant target to DS assignment methods
* Add tests, fix assignment
* Fix possible nullpointer during target assignment request
* Update api docu
* Clean up deployment management code
* Enforce MaxActions quota for offline assignment
* Fix review findings
* Rename property, add migration into
* Add builder for DeploymentRequest
* Change offline assignment method to accept an assignment list, like online assignment
* Fix PR findings

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>
This commit is contained in:
Stefan Klotz
2019-09-17 14:20:26 +02:00
committed by Stefan Behl
parent dba972423b
commit 8687510131
50 changed files with 1466 additions and 559 deletions

View File

@@ -42,7 +42,6 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
@@ -154,10 +153,9 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
getOsModule(ds), "test1.signature", ARTIFACT_SIZE);
final Target savedTarget = createTargetAndAssertNoActiveActions();
final List<Target> targetsAssignedToDs = deploymentManagement
.assignDistributionSet(ds.getId(), ActionType.FORCED, RepositoryModelConstants.NO_FORCE_TIME,
Collections.singletonList(savedTarget.getControllerId()))
.getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList());;
final List<Target> targetsAssignedToDs = assignDistributionSet(ds.getId(), savedTarget.getControllerId(),
ActionType.FORCED).getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList());
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
@@ -208,9 +206,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Target target = testdataFactory.createTarget(DEFAULT_CONTROLLER_ID);
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
final Long actionId = getFirstAssignedActionId(
deploymentManagement.assignDistributionSet(ds.getId(), ActionType.TIMEFORCED,
System.currentTimeMillis() + 2_000, Collections.singletonList(target.getControllerId())));
final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds.getId(), target.getControllerId(),
ActionType.TIMEFORCED, System.currentTimeMillis() + 2_000));
MvcResult mvcResult = performGet("/{tenant}/controller/v1/" + DEFAULT_CONTROLLER_ID, MediaTypes.HAL_JSON,
status().isOk(), tenantAware.getCurrentTenant()).andReturn();
@@ -257,8 +254,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Target savedTarget = createTargetAndAssertNoActiveActions();
final List<Target> saved = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.SOFT,
RepositoryModelConstants.NO_FORCE_TIME, Collections.singletonList(savedTarget.getControllerId()))
final List<Target> saved = assignDistributionSet(ds.getId(), savedTarget.getControllerId(), ActionType.SOFT)
.getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList());
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
@@ -317,8 +313,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Target savedTarget = createTargetAndAssertNoActiveActions();
final List<Target> saved = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.TIMEFORCED,
System.currentTimeMillis(), Collections.singletonList(savedTarget.getControllerId()))
final List<Target> saved = assignDistributionSet(ds.getId(), savedTarget.getControllerId(),
ActionType.TIMEFORCED)
.getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList());
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
@@ -385,8 +381,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Target savedTarget = createTargetAndAssertNoActiveActions();
final List<Target> saved = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.DOWNLOAD_ONLY,
RepositoryModelConstants.NO_FORCE_TIME, Collections.singletonList(savedTarget.getControllerId()))
final List<Target> saved = assignDistributionSet(ds.getId(), savedTarget.getControllerId(),
ActionType.DOWNLOAD_ONLY)
.getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList());
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);

View File

@@ -8,8 +8,8 @@
*/
package org.eclipse.hawkbit.mgmt.json.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A generic abstract rest model which contains only a ID for use-case e.g.
@@ -19,12 +19,28 @@ import com.fasterxml.jackson.annotation.JsonProperty;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtId {
@JsonProperty
private Long id;
/**
* Constructor
*/
public MgmtId() {
}
/**
* @return the id
* Constructor
*
* @param id
* ID of object
*/
@JsonCreator
public MgmtId(final Long id) {
this.id = id;
}
/**
* @return the ID
*/
public Long getId() {
return id;
@@ -32,7 +48,7 @@ public class MgmtId {
/**
* @param id
* the id to set
* the ID to set
*/
public void setId(final Long id) {
this.id = id;

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.mgmt.json.model.distributionset;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindowRequestBody;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -20,18 +21,21 @@ import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtTargetAssignmentRequestBody {
@JsonProperty
private String id;
private long forcetime;
private MgmtActionType type;
private MgmtMaintenanceWindowRequestBody maintenanceWindow;
/**
* {@link MgmtMaintenanceWindowRequestBody} object containing schedule,
* duration and timezone.
* JsonCreator Constructor
*
* @param id
* Mandatory ID of the target that should be assigned
*/
private MgmtMaintenanceWindowRequestBody maintenanceWindow;
@JsonCreator
public MgmtTargetAssignmentRequestBody(@JsonProperty(required = true, value = "id") final String id) {
this.id = id;
}
public String getId() {
return id;

View File

@@ -18,5 +18,4 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtSoftwareModuleTypeAssigment extends MgmtId {
}

View File

@@ -7,19 +7,29 @@ import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindowRequestBody;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request Body of DistributionSet for assignment operations (ID only).
*
*/
public class MgmtDistributionSetAssignment extends MgmtId {
private long forcetime;
private MgmtActionType type;
private MgmtMaintenanceWindowRequestBody maintenanceWindow;
/**
* {@link MgmtMaintenanceWindowRequestBody} object defining a schedule,
* duration and timezone.
* Constructor
*
* @param id
* ID of object
*/
private MgmtMaintenanceWindowRequestBody maintenanceWindow;
@JsonCreator
public MgmtDistributionSetAssignment(@JsonProperty(required = true, value = "id") final Long id) {
super(id);
}
public MgmtActionType getType() {
return type;

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) 2019 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.target;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
/**
* Class to hold multiple distribution set assignments. A JSON object
* representing a single {@link MgmtDistributionSetAssignment} can be
* deserialized to an object of this class.
*/
@JsonDeserialize(using = MgmtDistributionSetAssignmentsDeserializer.class)
public class MgmtDistributionSetAssignments extends ArrayList<MgmtDistributionSetAssignment> {
private static final long serialVersionUID = 1L;
/**
* Constructor for an object that contains no distribution set assignment
*
*/
public MgmtDistributionSetAssignments() {
super();
}
/**
* Constructor for an object that contains a single distribution set
* assignment
*
* @param assignment
* the assignment
*/
public MgmtDistributionSetAssignments(final MgmtDistributionSetAssignment assignment) {
super();
add(assignment);
}
/**
* Constructor for an object that contains multiple distribution set
* assignments
*
* @param assignments
* the assignments
*/
public MgmtDistributionSetAssignments(final List<MgmtDistributionSetAssignment> assignments) {
super(assignments);
}
}

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2019 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.target;
import java.io.IOException;
import java.util.Arrays;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
/**
* Deserializes a single object or a List of
* {@link MgmtDistributionSetAssignment}s
*/
public class MgmtDistributionSetAssignmentsDeserializer extends StdDeserializer<MgmtDistributionSetAssignments> {
private static final long serialVersionUID = 1L;
/**
* Mandatory constructor
*/
public MgmtDistributionSetAssignmentsDeserializer() {
this(null);
}
protected MgmtDistributionSetAssignmentsDeserializer(final Class<?> vc) {
super(vc);
}
@Override
public MgmtDistributionSetAssignments deserialize(final JsonParser jp, final DeserializationContext ctx)
throws IOException {
final MgmtDistributionSetAssignments assignments = new MgmtDistributionSetAssignments();
final ObjectCodec codec = jp.getCodec();
final JsonNode node = codec.readTree(jp);
if (node.isArray()) {
assignments.addAll(Arrays.asList(codec.treeToValue(node, MgmtDistributionSetAssignment[].class)));
} else {
assignments.add(codec.treeToValue(node, MgmtDistributionSetAssignment.class));
}
return assignments;
}
}

View File

@@ -18,7 +18,7 @@ import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionRequestBodyPut;
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionStatus;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentResponseBody;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtDistributionSetAssignment;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtDistributionSetAssignments;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetAttributes;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody;
@@ -261,8 +261,8 @@ public interface MgmtTargetRestApi {
*
* @param targetId
* of the target to change
* @param dsId
* of the distributionset that is to be assigned
* @param dsAssignments
* the requested Assignments that shall be made
* @param offline
* to <code>true</code> if update was executed offline, i.e. not
* managed by hawkBit.
@@ -275,7 +275,7 @@ public interface MgmtTargetRestApi {
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTargetAssignmentResponseBody> postAssignedDistributionSet(
@PathVariable("targetId") String targetId, MgmtDistributionSetAssignment dsId,
@PathVariable("targetId") String targetId, MgmtDistributionSetAssignments dsAssignments,
@RequestParam(value = "offline", required = false) boolean offline);
/**

View File

@@ -0,0 +1,74 @@
/**
* Copyright (c) 2019 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.rest.resource;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindowRequestBody;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentRequestBody;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtDistributionSetAssignment;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
import org.eclipse.hawkbit.repository.model.DeploymentRequestBuilder;
/**
* A mapper for assignment requests
*/
public final class MgmtDeploymentRequestMapper {
private MgmtDeploymentRequestMapper() {
// Utility class
}
/**
* Convert assignment information to an {@link DeploymentRequest}
*
* @param dsAssignment
* DS assignment information
* @param targetId
* target to assign the DS to
* @return resulting {@link DeploymentRequest}
*/
public static DeploymentRequest createAssignmentRequest(final MgmtDistributionSetAssignment dsAssignment,
final String targetId) {
return createAssignmentRequest(targetId, dsAssignment.getId(), dsAssignment.getType(),
dsAssignment.getForcetime(), dsAssignment.getMaintenanceWindow());
}
/**
* Convert assignment information to an {@link DeploymentRequest}
*
* @param targetAssignment
* target assignment information
* @param dsId
* DS to assign the target to
* @return resulting {@link DeploymentRequest}
*/
public static DeploymentRequest createAssignmentRequest(final MgmtTargetAssignmentRequestBody targetAssignment,
final Long dsId) {
return createAssignmentRequest(targetAssignment.getId(), dsId, targetAssignment.getType(),
targetAssignment.getForcetime(), targetAssignment.getMaintenanceWindow());
}
private static DeploymentRequest createAssignmentRequest(final String targetId, final Long dsId,
final MgmtActionType type, final long forcetime, final MgmtMaintenanceWindowRequestBody maintenanceWindow) {
final DeploymentRequestBuilder request = DeploymentManagement.deploymentRequest(targetId, dsId)
.setActionType(MgmtRestModelMapper.convertActionType(type)).setForceTime(forcetime);
if (maintenanceWindow != null) {
final String cronSchedule = maintenanceWindow.getSchedule();
final String duration = maintenanceWindow.getDuration();
final String timezone = maintenanceWindow.getTimezone();
MaintenanceScheduleHelper.validateMaintenanceSchedule(cronSchedule, duration, timezone);
request.setMaintenance(cronSchedule, duration, timezone);
}
return request.build();
}
}

View File

@@ -144,6 +144,20 @@ public final class MgmtDistributionSetMapper {
return result;
}
static MgmtTargetAssignmentResponseBody toResponse(
final List<DistributionSetAssignmentResult> dsAssignmentResults) {
final MgmtTargetAssignmentResponseBody result = new MgmtTargetAssignmentResponseBody();
final int alreadyAssigned = dsAssignmentResults.stream()
.mapToInt(DistributionSetAssignmentResult::getAlreadyAssigned).sum();
final List<MgmtActionId> assignedActions = dsAssignmentResults.stream()
.flatMap(assignmentResult -> assignmentResult.getAssignedEntity().stream())
.map(action -> new MgmtActionId(action.getTarget().getControllerId(), action.getId()))
.collect(Collectors.toList());
result.setAlreadyAssigned(alreadyAssigned);
result.setAssignedActions(assignedActions);
return result;
}
static List<MgmtDistributionSet> toResponseDistributionSets(final Collection<DistributionSet> sets) {
if (sets == null) {
return Collections.emptyList();

View File

@@ -8,11 +8,12 @@
*/
package org.eclipse.hawkbit.mgmt.rest.resource;
import java.util.AbstractMap.SimpleEntry;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
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;
@@ -30,20 +31,19 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -250,33 +250,22 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
@PathVariable("distributionSetId") final Long distributionSetId,
@RequestBody final List<MgmtTargetAssignmentRequestBody> assignments,
@RequestParam(value = "offline", required = false) final boolean offline) {
if (offline) {
return ResponseEntity.ok(MgmtDistributionSetMapper
.toResponse(this.deployManagament.offlineAssignedDistributionSet(distributionSetId, assignments
.stream().map(MgmtTargetAssignmentRequestBody::getId).collect(Collectors.toList()))));
final List<Entry<String, Long>> offlineAssignments = assignments.stream()
.map(assignment -> new SimpleEntry<String, Long>(assignment.getId(), distributionSetId))
.collect(Collectors.toList());
return ResponseEntity
.ok(MgmtDistributionSetMapper
.toResponse(deployManagament.offlineAssignedDistributionSets(offlineAssignments)));
}
final List<DeploymentRequest> deploymentRequests = assignments.stream()
.map(assignment -> MgmtDeploymentRequestMapper.createAssignmentRequest(assignment, distributionSetId))
.collect(Collectors.toList());
final DistributionSetAssignmentResult assignDistributionSet = this.deployManagament
.assignDistributionSet(distributionSetId, assignments.stream().map(t -> {
final MgmtMaintenanceWindowRequestBody maintenanceWindow = t.getMaintenanceWindow();
if (maintenanceWindow == null) {
return new TargetWithActionType(t.getId(), MgmtRestModelMapper.convertActionType(t.getType()),
t.getForcetime());
}
final String cronSchedule = maintenanceWindow.getSchedule();
final String duration = maintenanceWindow.getDuration();
final String timezone = maintenanceWindow.getTimezone();
MaintenanceScheduleHelper.validateMaintenanceSchedule(cronSchedule, duration, timezone);
return new TargetWithActionType(t.getId(), MgmtRestModelMapper.convertActionType(t.getType()),
t.getForcetime(), cronSchedule, duration, timezone);
}).collect(Collectors.toList()));
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponse(assignDistributionSet));
final List<DistributionSetAssignmentResult> assignmentResults = deployManagament
.assignDistributionSets(deploymentRequests);
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponse(assignmentResults));
}

View File

@@ -8,14 +8,16 @@
*/
package org.eclipse.hawkbit.mgmt.rest.resource;
import java.util.AbstractMap.SimpleEntry;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import javax.validation.Valid;
import javax.validation.ValidationException;
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;
@@ -25,7 +27,7 @@ import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionStatus;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentResponseBody;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtDistributionSetAssignment;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtDistributionSetAssignments;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetAttributes;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody;
@@ -33,15 +35,15 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetRestApi;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
@@ -281,36 +283,25 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
@Override
public ResponseEntity<MgmtTargetAssignmentResponseBody> postAssignedDistributionSet(
@PathVariable("targetId") final String targetId, @RequestBody final MgmtDistributionSetAssignment dsId,
@PathVariable("targetId") final String targetId,
@Valid @RequestBody final MgmtDistributionSetAssignments dsAssignments,
@RequestParam(value = "offline", required = false) final boolean offline) {
if (offline) {
final List<Entry<String, Long>> offlineAssignments = dsAssignments.stream()
.map(dsAssignment -> new SimpleEntry<String, Long>(targetId, dsAssignment.getId()))
.collect(Collectors.toList());
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponse(
deploymentManagement.offlineAssignedDistributionSet(dsId.getId(),
Collections.singletonList(targetId))));
deploymentManagement.offlineAssignedDistributionSets(offlineAssignments)));
}
findTargetWithExceptionIfNotFound(targetId);
final MgmtMaintenanceWindowRequestBody maintenanceWindow = dsId.getMaintenanceWindow();
if (maintenanceWindow == null) {
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponse(this.deploymentManagement
.assignDistributionSet(dsId.getId(), Collections.singletonList(new TargetWithActionType(targetId,
MgmtRestModelMapper.convertActionType(dsId.getType()), dsId.getForcetime())))));
}
final String cronSchedule = maintenanceWindow.getSchedule();
final String duration = maintenanceWindow.getDuration();
final String timezone = maintenanceWindow.getTimezone();
MaintenanceScheduleHelper.validateMaintenanceSchedule(cronSchedule, duration, timezone);
return ResponseEntity
.ok(MgmtDistributionSetMapper.toResponse(this.deploymentManagement.assignDistributionSet(dsId.getId(),
Collections.singletonList(new TargetWithActionType(targetId,
MgmtRestModelMapper.convertActionType(dsId.getType()), dsId.getForcetime(),
cronSchedule, duration, timezone)))));
final List<DeploymentRequest> deploymentRequests = dsAssignments.stream()
.map(dsAssignment -> MgmtDeploymentRequestMapper.createAssignmentRequest(dsAssignment, targetId))
.collect(Collectors.toList());
final List<DistributionSetAssignmentResult> assignmentResults = deploymentManagement
.assignDistributionSets(deploymentRequests);
return ResponseEntity.ok(MgmtDistributionSetMapper.toResponse(assignmentResults));
}
@Override

View File

@@ -27,10 +27,12 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
import org.eclipse.hawkbit.repository.model.Action;
@@ -40,7 +42,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
@@ -299,11 +300,10 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
}
@Test
@Description("Ensures that multi target assignment is protected by our 'max targets per manual assignment' quota.")
@Description("Ensures that multi target assignment is protected by our getMaxTargetDistributionSetAssignmentsPerManualAssignment quota.")
public void assignMultipleTargetsToDistributionSetUntilQuotaIsExceeded() throws Exception {
final int maxTargets = quotaManagement.getMaxTargetsPerManualAssignment();
final List<Target> targets = testdataFactory.createTargets(maxTargets + 1);
final int maxActions = quotaManagement.getMaxTargetDistributionSetAssignmentsPerManualAssignment();
final List<Target> targets = testdataFactory.createTargets(maxActions + 1);
final DistributionSet ds = testdataFactory.createDistributionSet();
final JSONArray payload = new JSONArray();
@@ -943,8 +943,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
assertThat(distributionSetManagement.findByCompleted(PAGE, true)).hasSize(0);
final DistributionSet set = testdataFactory.createDistributionSet("one");
deploymentManagement.assignDistributionSet(set.getId(),
Arrays.asList(new TargetWithActionType(testdataFactory.createTarget().getControllerId())));
assignDistributionSet(set.getId(), testdataFactory.createTarget().getControllerId());
assertThat(distributionSetManagement.count()).isEqualTo(1);
@@ -1273,4 +1272,63 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
.as("Five targets in repository have DS assigned").hasSize(5);
}
@Test
@Description("A request for assigning a target multiple times results in a Bad Request when multiassignment is disabled.")
public void multiassignmentRequestNotAllowedIfDisabled() throws Exception {
final String targetId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
final JSONArray body = new JSONArray();
body.put(getAssignmentObject(targetId, MgmtActionType.SOFT));
body.put(getAssignmentObject(targetId, MgmtActionType.FORCED));
mvc.perform(post("/rest/v1/distributionsets/{ds}/assignedTargets", dsId).content(body.toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isBadRequest());
}
@Test
@Description("Identical assignments in a single request are removed when multiassignment is disabled.")
public void identicalAssignmentInRequestAreRemovedIfMultiassignmentsDisabled() throws Exception {
final String targetId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
final JSONArray body = new JSONArray();
body.put(getAssignmentObject(targetId, MgmtActionType.FORCED));
body.put(getAssignmentObject(targetId, MgmtActionType.FORCED));
mvc.perform(post("/rest/v1/distributionsets/{ds}/assignedTargets", dsId).content(body.toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("total", equalTo(1)));
}
@Test
@Description("Assigning targets multiple times to a DS in one request works in multiassignment mode.")
public void multiAssignment() throws Exception {
final List<String> targetIds = testdataFactory.createTargets(2).stream().map(Target::getControllerId)
.collect(Collectors.toList());
final Long dsId = testdataFactory.createDistributionSet().getId();
final JSONArray body = new JSONArray();
body.put(getAssignmentObject(targetIds.get(0), MgmtActionType.FORCED));
body.put(getAssignmentObject(targetIds.get(0), MgmtActionType.FORCED));
body.put(getAssignmentObject(targetIds.get(1), MgmtActionType.FORCED));
body.put(getAssignmentObject(targetIds.get(1), MgmtActionType.SOFT));
enableMultiAssignments();
mvc.perform(post("/rest/v1/distributionsets/{ds}/assignedTargets", dsId).content(body.toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("total", equalTo(body.length())));
}
public static JSONObject getAssignmentObject(final String targetId, final MgmtActionType type)
throws JSONException {
final JSONObject obj = new JSONObject();
obj.put("id", targetId);
obj.put("type", type.getName());
return obj;
}
}

View File

@@ -26,6 +26,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -60,6 +61,7 @@ import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.eclipse.hawkbit.util.IpUtil;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;
import org.springframework.data.domain.PageRequest;
@@ -1240,8 +1242,8 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
public void updateAction() throws Exception {
final Target target = testdataFactory.createTarget();
final DistributionSet set = testdataFactory.createDistributionSet();
final Long actionId = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(set.getId(),
ActionType.SOFT, 0, Collections.singletonList(target.getControllerId())));
final Long actionId = getFirstAssignedActionId(
assignDistributionSet(set.getId(), target.getControllerId(), ActionType.SOFT));
assertThat(deploymentManagement.findAction(actionId).get().getActionType()).isEqualTo(ActionType.SOFT);
final String body = new JSONObject().put("forceType", "forced").toString();
@@ -1293,7 +1295,7 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
@Description("Verfies that a DOWNLOAD_ONLY DS to target assignment is properly handled")
public void assignDownloadOnlyDistributionSetToTarget() throws Exception {
Target target = testdataFactory.createTarget();
final Target target = testdataFactory.createTarget();
final DistributionSet set = testdataFactory.createDistributionSet("one");
mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + target.getControllerId() + "/assignedDS")
@@ -1303,7 +1305,7 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
.andExpect(jsonPath("total", equalTo(1)));
assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get()).isEqualTo(set);
Slice<Action> actions = deploymentManagement.findActionsByTarget("targetExist", PageRequest.of(0, 100));
final Slice<Action> actions = deploymentManagement.findActionsByTarget("targetExist", PageRequest.of(0, 100));
assertThat(actions.getSize()).isGreaterThan(0);
actions.stream().filter(a -> a.getDistributionSet().equals(set))
.forEach(a -> ActionType.DOWNLOAD_ONLY.equals(a.getActionType()));
@@ -1866,4 +1868,69 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
.andExpect(jsonPath("total", equalTo(1))).andExpect(jsonPath("content[0].key", equalTo("knownKey1")))
.andExpect(jsonPath("content[0].value", equalTo("knownValue1")));
}
@Test
@Description("A request for assigning multiple DS to a target results in a Bad Request when multiassignment in disabled.")
public void multiassignmentRequestNotAllowedIfDisabled() throws Exception {
final String targetId = testdataFactory.createTarget().getControllerId();
final List<Long> dsIds = testdataFactory.createDistributionSets(2).stream().map(DistributionSet::getId)
.collect(Collectors.toList());
final JSONArray body = getAssignmentBody(dsIds);
mvc.perform(post("/rest/v1/targets/{targetId}/assignedDS", targetId).content(body.toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isBadRequest());
}
@Test
@Description("Passing an array in assignment request is allowed if multiassignment is disabled and array size in 1.")
public void multiassignmentRequestAllowedIfDisabledButHasSizeOne() throws Exception {
final String targetId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
final JSONArray body = getAssignmentBody(Collections.singletonList(dsId));
mvc.perform(post("/rest/v1/targets/{targetId}/assignedDS", targetId).content(body.toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
}
@Test
@Description("Identical assignments in a single request are removed when multiassignment in disabled.")
public void identicalAssignmentInRequestAreRemovedIfMultiassignmentsDisabled() throws Exception {
final String targetId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
final JSONArray body = getAssignmentBody(Arrays.asList(dsId, dsId));
mvc.perform(post("/rest/v1/targets/{targetId}/assignedDS", targetId).content(body.toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("total", equalTo(1)));
}
@Test
@Description("Assign multiple DSs to a target in one request with multiassignments enabled.")
public void multiAssignment() throws Exception {
final String targetId = testdataFactory.createTarget().getControllerId();
final List<Long> dsIds = testdataFactory.createDistributionSets(2).stream().map(DistributionSet::getId)
.collect(Collectors.toList());
final JSONArray body = getAssignmentBody(dsIds);
enableMultiAssignments();
mvc.perform(post("/rest/v1/targets/{targetId}/assignedDS", targetId).content(body.toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(jsonPath("total", equalTo(2)));
}
public static JSONArray getAssignmentBody(final Collection<Long> dsIds) throws JSONException {
final JSONArray body = new JSONArray();
for (final Long id : dsIds) {
final JSONObject obj = new JSONObject();
obj.put("id", id);
body.put(obj);
}
return body;
}
}

View File

@@ -80,6 +80,7 @@ public class ResponseExceptionHandler {
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_AUTO_ASSIGN_ACTION_TYPE_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID, HttpStatus.BAD_REQUEST);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_CONFIGURATION_VALUE_CHANGE_NOT_ALLOWED, HttpStatus.FORBIDDEN);
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_MULTIASSIGNMENT_NOT_ENABLED, HttpStatus.BAD_REQUEST);
}
private static HttpStatus getStatusOrDefault(final SpServerError error) {

View File

@@ -547,13 +547,13 @@ include::../errors/429.adoc[]
|===
== POST /rest/v1/targets/{targetId}/assignedDS
== POST /rest/v1/targets/{targetId}/assignedDS (assign single distribution set)
=== Implementation Notes
Handles the POST request for assigning a distribution set to a specific target. Required Permission: READ_REPOSITORY and UPDATE_TARGET
=== Asssin distribution set to target
=== Assign distribution set to target
==== Curl
@@ -601,6 +601,60 @@ include::../errors/429.adoc[]
|===
== POST /rest/v1/targets/{targetId}/assignedDS (assign multiple distribution sets)
=== Implementation Notes
Handles the POST request for assigning multiple distribution sets to a specific target (only allowed id 'multi assignments' is enabled). Required Permission: READ_REPOSITORY and UPDATE_TARGET
=== Assign distribution sets to target
==== Curl
include::{snippets}/targets/post-assign-distribution-sets-to-target/curl-request.adoc[]
==== Request path parameter
include::{snippets}/targets/post-assign-distribution-sets-to-target/path-parameters.adoc[]
==== Request query parameter
include::{snippets}/targets/post-assign-distribution-sets-to-target/request-parameters.adoc[]
==== Request fields
include::{snippets}/targets/post-assign-distribution-sets-to-target/request-fields.adoc[]
==== Request URL
include::{snippets}/targets/post-assign-distribution-sets-to-target/http-request.adoc[]
=== Response (Status 200)
==== Response fields
include::{snippets}/targets/post-assign-distribution-sets-to-target/response-fields.adoc[]
==== Response example
include::{snippets}/targets/post-assign-distribution-sets-to-target/http-response.adoc[]
=== Error responses
|===
| HTTP Status Code | Reason | Response Model
include::../errors/400_multiassignment.adoc[]
include::../errors/401.adoc[]
include::../errors/403.adoc[]
include::../errors/404.adoc[]
include::../errors/405.adoc[]
include::../errors/406.adoc[]
include::../errors/409.adoc[]
include::../errors/415.adoc[]
include::../errors/429.adoc[]
|===
== GET /rest/v1/targets/{targetId}/attributes
=== Implementation Notes

View File

@@ -0,0 +1,3 @@
| `400 Bad Request`
| Bad Request - e.g. invalid parameters or 'multi assignments' is disabled
|

View File

@@ -21,7 +21,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.ByteArrayInputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -73,7 +72,7 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
final DistributionSet set = testdataFactory.createDistributionSet("one");
final Target target = targetManagement.create(entityFactory.target().create().controllerId(CONTROLLER_ID));
deploymentManagement.assignDistributionSet(set.getId(), Arrays.asList(target.getTargetWithActionType()));
assignDistributionSet(set.getId(), target.getControllerId());
mockMvc.perform(get(DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}",
tenantAware.getCurrentTenant(), target.getControllerId()).accept(MediaTypes.HAL_JSON_VALUE))
@@ -101,8 +100,8 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
final DistributionSet setTwo = testdataFactory.createDistributionSet("two");
final Target target = targetManagement.create(entityFactory.target().create().controllerId(CONTROLLER_ID));
deploymentManagement.assignDistributionSet(set.getId(), Arrays.asList(target.getTargetWithActionType()));
deploymentManagement.assignDistributionSet(setTwo.getId(), Arrays.asList(target.getTargetWithActionType()));
assignDistributionSet(set.getId(), target.getControllerId());
assignDistributionSet(setTwo.getId(), target.getControllerId());
mockMvc.perform(get(DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}",
tenantAware.getCurrentTenant(), target.getControllerId()).accept(MediaTypes.HAL_JSON_VALUE))
@@ -137,8 +136,7 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
});
final Target target = targetManagement.create(entityFactory.target().create().controllerId(CONTROLLER_ID));
final Long actionId = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(set.getId(),
Arrays.asList(target.getTargetWithActionType())));
final Long actionId = getFirstAssignedActionId(assignDistributionSet(set.getId(), target.getControllerId()));
final Action cancelAction = deploymentManagement.cancelAction(actionId);
mockMvc.perform(
@@ -169,8 +167,7 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
final DistributionSet set = testdataFactory.createDistributionSet("one");
final Target target = targetManagement.create(entityFactory.target().create().controllerId(CONTROLLER_ID));
final Long actionId = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(set.getId(),
Arrays.asList(target.getTargetWithActionType())));
final Long actionId = getFirstAssignedActionId(assignDistributionSet(set.getId(), target.getControllerId()));
final Action cancelAction = deploymentManagement.cancelAction(actionId);
mockMvc.perform(post(
@@ -387,8 +384,7 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
final DistributionSet set = testdataFactory.createDistributionSet("one");
final Target target = targetManagement.create(entityFactory.target().create().controllerId(CONTROLLER_ID));
final Long actionId = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(set.getId(),
Arrays.asList(target.getTargetWithActionType())));
final Long actionId = getFirstAssignedActionId(assignDistributionSet(set.getId(), target.getControllerId()));
mockMvc.perform(post(DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/"
+ DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}/feedback", tenantAware.getCurrentTenant(),
@@ -434,7 +430,7 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio
.create(new ArtifactUpload(new ByteArrayInputStream(random), module.getId(), "binaryFile", false, 0));
final Target target = targetManagement.create(entityFactory.target().create().controllerId(CONTROLLER_ID));
deploymentManagement.assignDistributionSet(set.getId(), Arrays.asList(target.getTargetWithActionType()));
assignDistributionSet(set.getId(), target.getControllerId());
mockMvc.perform(
get(DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/softwaremodules/{moduleId}/artifacts",

View File

@@ -27,6 +27,7 @@ import org.eclipse.hawkbit.ddi.rest.resource.DdiApiConfiguration;
import org.eclipse.hawkbit.mgmt.rest.resource.MgmtApiConfiguration;
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.DistributionSet;
@@ -169,7 +170,8 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
private List<Target> assignWithoutMaintenanceWindow(final DistributionSet distributionSet, final Target savedTarget,
final boolean timeforced) {
final List<Action> actions = timeforced
? assignDistributionSetTimeForced(distributionSet, savedTarget).getAssignedEntity()
? assignDistributionSet(distributionSet.getId(), savedTarget.getControllerId(), ActionType.TIMEFORCED)
.getAssignedEntity()
: assignDistributionSet(distributionSet, savedTarget).getAssignedEntity();
return actions.stream().map(Action::getTarget).collect(Collectors.toList());
}
@@ -178,8 +180,8 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
final boolean timeforced, final String maintenanceWindowSchedule, final String maintenanceWindowDuration,
final String maintenanceWindowTimeZone) {
final List<Action> actions = timeforced
? assignDistributionSetWithMaintenanceWindowTimeForced(distributionSet.getId(),
savedTarget.getControllerId(), maintenanceWindowSchedule, maintenanceWindowDuration,
? assignDistributionSetWithMaintenanceWindow(distributionSet.getId(), savedTarget.getControllerId(),
ActionType.TIMEFORCED, maintenanceWindowSchedule, maintenanceWindowDuration,
maintenanceWindowTimeZone).getAssignedEntity()
: assignDistributionSetWithMaintenanceWindow(distributionSet.getId(), savedTarget.getControllerId(),
maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone)

View File

@@ -188,7 +188,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 FORCETIME_TYPE = "The type of the forcetime.";
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.";
public static final String TARGET_ASSIGNED_TOTAL = "The total number of targets that are part of this operation.";

View File

@@ -383,17 +383,20 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
parameterWithName("distributionSetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
requestParameters(parameterWithName("offline")
.description(MgmtApiModelProperties.OFFLINE_UPDATE).optional()),
requestFields(requestFieldWithPath("[]forcetime").description(MgmtApiModelProperties.FORCETIME),
requestFieldWithPath("[]id").description(ApiModelPropertiesGeneric.ITEM_ID),
requestFieldWithPath("[]maintenanceWindow")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW).optional(),
requestFieldWithPath("[]maintenanceWindow.schedule")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_SCHEDULE).optional(),
requestFieldWithPath("[]maintenanceWindow.duration")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_DURATION).optional(),
requestFieldWithPath("[]maintenanceWindow.timezone")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_TIMEZONE).optional(),
requestFieldWithPath("[]type").description(MgmtApiModelProperties.FORCETIME_TYPE)
requestFields(
requestFieldWithPath("[].id").description(ApiModelPropertiesGeneric.ITEM_ID),
optionalRequestFieldWithPath("[].forcetime")
.description(MgmtApiModelProperties.FORCETIME),
optionalRequestFieldWithPath("[].maintenanceWindow")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW),
optionalRequestFieldWithPath("[].maintenanceWindow.schedule")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_SCHEDULE),
optionalRequestFieldWithPath("[].maintenanceWindow.duration")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_DURATION),
optionalRequestFieldWithPath("[].maintenanceWindow.timezone")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_TIMEZONE),
optionalRequestFieldWithPath("[].type")
.description(MgmtApiModelProperties.ASSIGNMENT_TYPE)
.attributes(key("value").value("['soft', 'forced','timeforced', 'downloadonly']"))),
responseFields(
fieldWithPath("assigned").description(MgmtApiModelProperties.DS_NEW_ASSIGNED_TARGETS),

View File

@@ -48,6 +48,7 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.restdocs.payload.JsonFieldType;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -395,8 +396,8 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
public void switchActionToForced() throws Exception {
final Target target = testdataFactory.createTarget(targetId);
final DistributionSet set = testdataFactory.createDistributionSet();
final Long actionId = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(set.getId(),
ActionType.SOFT, 0, Collections.singletonList(target.getControllerId())));
final Long actionId = getFirstAssignedActionId(
assignDistributionSet(set.getId(), target.getControllerId(), ActionType.SOFT));
assertThat(deploymentManagement.findAction(actionId).get().getActionType()).isEqualTo(ActionType.SOFT);
final Map<String, Object> body = new HashMap<>();
@@ -485,7 +486,7 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
pathParameters(parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
getResponseFieldsDistributionSet(false)));
}
@Test
@Description("Handles the POST request for assigning a distribution set to a specific target. Required Permission: READ_REPOSITORY and UPDATE_TARGET.")
public void postAssignDistributionSetToTarget() throws Exception {
@@ -511,30 +512,80 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio
pathParameters(parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
requestParameters(parameterWithName("offline")
.description(MgmtApiModelProperties.OFFLINE_UPDATE).optional()),
requestFields(requestFieldWithPath("forcetime").description(MgmtApiModelProperties.FORCETIME),
requestFields(
requestFieldWithPath("id").description(ApiModelPropertiesGeneric.ITEM_ID),
requestFieldWithPath("maintenanceWindow")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW).optional(),
requestFieldWithPath("maintenanceWindow.schedule")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_SCHEDULE).optional(),
requestFieldWithPath("maintenanceWindow.duration")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_DURATION).optional(),
requestFieldWithPath("maintenanceWindow.timezone")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_TIMEZONE).optional(),
requestFieldWithPath("type").description(MgmtApiModelProperties.FORCETIME_TYPE)
optionalRequestFieldWithPath("forcetime").description(MgmtApiModelProperties.FORCETIME),
optionalRequestFieldWithPath("maintenanceWindow")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW),
optionalRequestFieldWithPath("maintenanceWindow.schedule")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_SCHEDULE),
optionalRequestFieldWithPath("maintenanceWindow.duration")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_DURATION),
optionalRequestFieldWithPath("maintenanceWindow.timezone")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_TIMEZONE),
optionalRequestFieldWithPath("type").description(MgmtApiModelProperties.ASSIGNMENT_TYPE)
.attributes(key("value").value("['soft', 'forced','timeforced', 'downloadonly']"))),
responseFields(
fieldWithPath("assigned").description(MgmtApiModelProperties.DS_NEW_ASSIGNED_TARGETS),
fieldWithPath("alreadyAssigned").type(JsonFieldType.NUMBER)
.description(MgmtApiModelProperties.DS_ALREADY_ASSIGNED_TARGETS),
fieldWithPath("assignedActions").type(JsonFieldType.ARRAY)
.description(MgmtApiModelProperties.DS_NEW_ASSIGNED_ACTIONS),
fieldWithPath("assignedActions.[].id").type(JsonFieldType.NUMBER)
.description(MgmtApiModelProperties.ACTION_ID),
fieldWithPath("assignedActions.[]._links.self").type(JsonFieldType.OBJECT)
.description(MgmtApiModelProperties.LINK_TO_ACTION),
fieldWithPath("total").type(JsonFieldType.NUMBER)
.description(MgmtApiModelProperties.DS_TOTAL_ASSIGNED_TARGETS))));
responseFields(getDsAssignmentResponseFieldDescriptors())));
}
@Test
@Description("Handles the POST request for assigning distribution sets to a specific target. Required Permission: READ_REPOSITORY and UPDATE_TARGET.")
public void postAssignDistributionSetsToTarget() throws Exception {
// create target and ds, and assign ds
final List<DistributionSet> sets = testdataFactory.createDistributionSets(2);
testdataFactory.createTarget(targetId);
final long forceTime = System.currentTimeMillis();
final JSONArray body = new JSONArray();
body.put(new JSONObject().put("id", sets.get(1).getId()).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"));
enableMultiAssignments();
mockMvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/"
+ MgmtRestConstants.TARGET_V1_ASSIGNED_DISTRIBUTION_SET, targetId).content(body.toString())
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andDo(this.document.document(
pathParameters(parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
requestParameters(parameterWithName("offline")
.description(MgmtApiModelProperties.OFFLINE_UPDATE).optional()),
requestFields(
requestFieldWithPath("[].id").description(ApiModelPropertiesGeneric.ITEM_ID),
optionalRequestFieldWithPath("[].forcetime")
.description(MgmtApiModelProperties.FORCETIME),
optionalRequestFieldWithPath("[].maintenanceWindow")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW),
optionalRequestFieldWithPath("[].maintenanceWindow.schedule")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_SCHEDULE),
optionalRequestFieldWithPath("[].maintenanceWindow.duration")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_DURATION),
optionalRequestFieldWithPath("[].maintenanceWindow.timezone")
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_TIMEZONE),
optionalRequestFieldWithPath("[].type")
.description(MgmtApiModelProperties.ASSIGNMENT_TYPE)
.attributes(key("[].value")
.value("['soft', 'forced','timeforced', 'downloadonly']"))),
responseFields(getDsAssignmentResponseFieldDescriptors())));
}
private static FieldDescriptor[] getDsAssignmentResponseFieldDescriptors() {
final FieldDescriptor[] descriptors = {
fieldWithPath("assigned").description(MgmtApiModelProperties.DS_NEW_ASSIGNED_TARGETS),
fieldWithPath("alreadyAssigned").type(JsonFieldType.NUMBER)
.description(MgmtApiModelProperties.DS_ALREADY_ASSIGNED_TARGETS),
fieldWithPath("assignedActions").type(JsonFieldType.ARRAY)
.description(MgmtApiModelProperties.DS_NEW_ASSIGNED_ACTIONS),
fieldWithPath("assignedActions.[].id").type(JsonFieldType.NUMBER)
.description(MgmtApiModelProperties.ACTION_ID),
fieldWithPath("assignedActions.[]._links.self").type(JsonFieldType.OBJECT)
.description(MgmtApiModelProperties.LINK_TO_ACTION),
fieldWithPath("total").type(JsonFieldType.NUMBER)
.description(MgmtApiModelProperties.DS_TOTAL_ASSIGNED_TARGETS) };
return descriptors;
}
@Test