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:
committed by
Stefan Behl
parent
dba972423b
commit
8687510131
10
MIGRATION.md
10
MIGRATION.md
@@ -13,8 +13,12 @@
|
||||
- ENTITYsrest classes have been removed; List<ENTITYrest> used instead (e.g. List<TargetRest> instead of TargetsRest)
|
||||
|
||||
### Renamed api annotations
|
||||
- Annotation `org.eclipse.hawkbit.rest.resource.EnableRestResources` have changed to `org.eclipse.hawkbit.mgmt.annotation.EnableMgmtApi`
|
||||
- Annotation `org.eclipse.hawkbit.ddi.resource.EnableDirectDeviceApi` have changed to `org.eclipse.hawkbit.ddi.annotation.EnableDdiApi`
|
||||
- Annotation `org.eclipse.hawkbit.rest.resource.EnableRestResources` has changed to `org.eclipse.hawkbit.mgmt.annotation.EnableMgmtApi`
|
||||
- Annotation `org.eclipse.hawkbit.ddi.resource.EnableDirectDeviceApi` has changed to `org.eclipse.hawkbit.ddi.annotation.EnableDdiApi`
|
||||
|
||||
### Renamed maven modules
|
||||
- Module hawkbit-mgmt-api-client have changed to hawkbit-example-mgmt-simulator
|
||||
- Module hawkbit-mgmt-api-client has changed to hawkbit-example-mgmt-simulator
|
||||
|
||||
## Milestone 0.3.0M6
|
||||
### Configuration Property changes
|
||||
- hawkbit.server.security.dos.maxTargetsPerManualAssignment has changed to hawkbit.server.security.dos.maxTargetDistributionSetAssignmentsPerManualAssignment
|
||||
|
||||
@@ -240,7 +240,13 @@ public enum SpServerError {
|
||||
* change is not allowed.
|
||||
*/
|
||||
SP_CONFIGURATION_VALUE_CHANGE_NOT_ALLOWED("hawkbit.server.error.repo.tenantConfigurationValueChangeNotAllowed",
|
||||
"The requested tenant configuration value modification is not allowed.");
|
||||
"The requested tenant configuration value modification is not allowed."),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_MULTIASSIGNMENT_NOT_ENABLED("hawkbit.server.error.multiassignmentNotEnabled",
|
||||
"The requested operation requires Multiassignments to be enabled.");
|
||||
|
||||
private final String key;
|
||||
private final String message;
|
||||
|
||||
@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.integration;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.amqp.AmqpProperties;
|
||||
@@ -23,7 +22,6 @@ import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
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.security.DmfTenantSecurityToken;
|
||||
import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
@@ -193,8 +191,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
|
||||
final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
|
||||
FileResource.createFileResourceBySha1(artifact.getSha1Hash()));
|
||||
|
||||
deploymentManagement.assignDistributionSet(distributionSet.getId(),
|
||||
Arrays.asList(new TargetWithActionType(TARGET)));
|
||||
assignDistributionSet(distributionSet.getId(), TARGET);
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyOkResult(returnMessage, artifact);
|
||||
@@ -212,8 +209,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
|
||||
final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, target.getId(), null,
|
||||
FileResource.createFileResourceBySha1(artifact.getSha1Hash()));
|
||||
|
||||
deploymentManagement.assignDistributionSet(distributionSet.getId(),
|
||||
Arrays.asList(new TargetWithActionType(TARGET)));
|
||||
assignDistributionSet(distributionSet.getId(), TARGET);
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyOkResult(returnMessage, artifact);
|
||||
@@ -246,8 +242,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
|
||||
final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
|
||||
FileResource.createFileResourceBySha1(artifact.getSha1Hash()));
|
||||
|
||||
deploymentManagement.assignDistributionSet(distributionSet.getId(),
|
||||
Arrays.asList(new TargetWithActionType(TARGET)));
|
||||
assignDistributionSet(distributionSet.getId(), TARGET);
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyOkResult(returnMessage, artifact);
|
||||
@@ -309,8 +304,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
|
||||
final FileResource fileResource = FileResource.createFileResourceByArtifactId(artifact.getId());
|
||||
final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource);
|
||||
|
||||
deploymentManagement.assignDistributionSet(distributionSet.getId(),
|
||||
Arrays.asList(new TargetWithActionType(TARGET)));
|
||||
assignDistributionSet(distributionSet.getId(), TARGET);
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyOkResult(returnMessage, artifact);
|
||||
@@ -365,8 +359,7 @@ public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmq
|
||||
softwareModule.getArtifact(artifact.getId()).get().getFilename());
|
||||
final DmfTenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource);
|
||||
|
||||
deploymentManagement.assignDistributionSet(distributionSet.getId(),
|
||||
Arrays.asList(new TargetWithActionType(TARGET)));
|
||||
assignDistributionSet(distributionSet.getId(), TARGET);
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyOkResult(returnMessage, artifact);
|
||||
|
||||
@@ -57,7 +57,6 @@ import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.amqp.core.Message;
|
||||
@@ -464,10 +463,6 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
||||
createConditionFactory().until(() -> securityRule.runAsPrivileged(callable));
|
||||
}
|
||||
|
||||
private void enableMultiAssignments() {
|
||||
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, true);
|
||||
}
|
||||
|
||||
private void assertLatestMultiActionMessageContainsInstallMessages(final String controllerId,
|
||||
final List<Set<Long>> smIdsOfActionsExpected) {
|
||||
final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION);
|
||||
|
||||
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -21,20 +22,21 @@ import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEv
|
||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
|
||||
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
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.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequestBuilder;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
@@ -48,71 +50,12 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||
public interface DeploymentManagement {
|
||||
|
||||
/**
|
||||
* Assigns the addressed {@link DistributionSet} to all {@link Target}s by
|
||||
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
||||
* Assigns {@link DistributionSet}s to {@link Target}s according to the
|
||||
* {@link DeploymentRequest}.
|
||||
*
|
||||
* @param dsID
|
||||
* the ID of the distribution set to assign
|
||||
* @param actionType
|
||||
* the type of the action to apply on the assignment
|
||||
* @param forcedTimestamp
|
||||
* the time when the action should be forced, only necessary for
|
||||
* {@link ActionType#TIMEFORCED}
|
||||
* @param controllerIDs
|
||||
* the IDs of the target to assign the distribution set
|
||||
* @return the assignment result
|
||||
*
|
||||
* @throws IncompleteDistributionSetException
|
||||
* if mandatory {@link SoftwareModuleType} are not assigned as
|
||||
* define by the {@link DistributionSetType}.
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if either provided {@link DistributionSet} or {@link Target}s
|
||||
* do not exist
|
||||
* @param deploymentRequests
|
||||
* information about all target-ds-assignments that shall be made
|
||||
*
|
||||
* @throws QuotaExceededException
|
||||
* if the maximum number of targets the distribution set can be
|
||||
* assigned to at once is exceeded
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||
DistributionSetAssignmentResult assignDistributionSet(long dsID, @NotNull ActionType actionType,
|
||||
long forcedTimestamp, @NotEmpty Collection<String> controllerIDs);
|
||||
|
||||
/**
|
||||
* Assigns the addressed {@link DistributionSet} to all {@link Target}s by
|
||||
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
||||
*
|
||||
* @param dsID
|
||||
* the ID of the distribution set to assign
|
||||
* @param targets
|
||||
* a list of all targets and their action type
|
||||
* @return the assignment result
|
||||
*
|
||||
* @throws IncompleteDistributionSetException
|
||||
* if mandatory {@link SoftwareModuleType} are not assigned as
|
||||
* define by the {@link DistributionSetType}.
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if either provided {@link DistributionSet} or {@link Target}s
|
||||
* do not exist
|
||||
*
|
||||
* @throws QuotaExceededException
|
||||
* if the maximum number of targets the distribution set can be
|
||||
* assigned to at once is exceeded
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||
DistributionSetAssignmentResult assignDistributionSet(long dsID,
|
||||
@NotEmpty Collection<TargetWithActionType> targets);
|
||||
|
||||
/**
|
||||
* Assigns the given set of {@link DistributionSet} entities to the given
|
||||
* {@link Target}s using the specified {@link ActionType} and
|
||||
* {@code forcetime}.
|
||||
*
|
||||
* @param dsIDs
|
||||
* the set of IDs of the distribution sets to assign
|
||||
* @param targets
|
||||
* a list of all targets and their action type
|
||||
* @return the list of assignment results
|
||||
*
|
||||
* @throws IncompleteDistributionSetException
|
||||
@@ -126,26 +69,29 @@ public interface DeploymentManagement {
|
||||
* @throws QuotaExceededException
|
||||
* if the maximum number of targets the distribution set can be
|
||||
* assigned to at once is exceeded
|
||||
* @throws MultiAssignmentIsNotEnabledException
|
||||
* if the request results in multiple assignments to the same
|
||||
* target and multiassignment is disabled
|
||||
*
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||
List<DistributionSetAssignmentResult> assignDistributionSets(@NotEmpty Set<Long> dsIDs,
|
||||
@NotEmpty Collection<TargetWithActionType> targets);
|
||||
List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
@NotEmpty List<DeploymentRequest> deploymentRequests);
|
||||
|
||||
/**
|
||||
* Assigns the addressed {@link DistributionSet} to all {@link Target}s by
|
||||
* their IDs with a specific {@link ActionType} and an action message.
|
||||
* Assigns {@link DistributionSet}s to {@link Target}s according to the
|
||||
* {@link DeploymentRequest}.
|
||||
*
|
||||
* @param dsID
|
||||
* the ID of the distribution set to assign
|
||||
* @param targets
|
||||
* a list of all targets and their action type
|
||||
* @param deploymentRequests
|
||||
* information about all target-ds-assignments that shall be made
|
||||
* @param actionMessage
|
||||
* an optional message for the action status
|
||||
* @return the assignment result
|
||||
*
|
||||
* @return the list of assignment results
|
||||
*
|
||||
* @throws IncompleteDistributionSetException
|
||||
* if mandatory {@link SoftwareModuleType} are not assigned as
|
||||
* define by the {@link DistributionSetType}.
|
||||
* defined by the {@link DistributionSetType}.
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if either provided {@link DistributionSet} or {@link Target}s
|
||||
@@ -154,14 +100,32 @@ public interface DeploymentManagement {
|
||||
* @throws QuotaExceededException
|
||||
* if the maximum number of targets the distribution set can be
|
||||
* assigned to at once is exceeded
|
||||
* @throws MultiAssignmentIsNotEnabledException
|
||||
* if the request results in multiple assignments to the same
|
||||
* target and multiassignment is disabled
|
||||
*
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||
DistributionSetAssignmentResult assignDistributionSet(long dsID, @NotEmpty Collection<TargetWithActionType> targets,
|
||||
String actionMessage);
|
||||
List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
@NotEmpty List<DeploymentRequest> deploymentRequests, String actionMessage);
|
||||
|
||||
/**
|
||||
* Registers an "offline" assignment, i.e. adds a completed action for the
|
||||
* given {@link DistributionSet} to the given {@link Target}s.
|
||||
* build a {@link DeploymentRequest} for a target distribution set
|
||||
* assignment
|
||||
*
|
||||
* @param controllerId
|
||||
* ID of target
|
||||
* @param distributionSetId
|
||||
* ID of distribution set
|
||||
* @return the builder
|
||||
*/
|
||||
static DeploymentRequestBuilder deploymentRequest(final String controllerId, final long distributionSetId) {
|
||||
return new DeploymentRequestBuilder(controllerId, distributionSetId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers "offline" assignments. "offline" assignment means adding a
|
||||
* completed action for a {@link DistributionSet} to a {@link Target}.
|
||||
*
|
||||
* The handling differs to hawkBit-managed updates by means that:<br/>
|
||||
*
|
||||
@@ -174,11 +138,10 @@ public interface DeploymentManagement {
|
||||
* <li>does not send a {@link TargetAssignDistributionSetEvent}.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param dsID
|
||||
* the ID of the distribution set that was assigned
|
||||
* @param controllerIDs
|
||||
* a list of IDs of the targets that where assigned
|
||||
* @return the assignment result
|
||||
* @param assignments
|
||||
* target IDs with the respective distribution set ID which they
|
||||
* are supposed to be assigned to
|
||||
* @return the assignment results
|
||||
*
|
||||
* @throws IncompleteDistributionSetException
|
||||
* if mandatory {@link SoftwareModuleType} are not assigned as
|
||||
@@ -191,9 +154,13 @@ public interface DeploymentManagement {
|
||||
* @throws QuotaExceededException
|
||||
* if the maximum number of targets the distribution set can be
|
||||
* assigned to at once is exceeded
|
||||
*
|
||||
* @throws MultiAssignmentIsNotEnabledException
|
||||
* if the request results in multiple assignments to the same
|
||||
* target and multiassignment is disabled
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||
DistributionSetAssignmentResult offlineAssignedDistributionSet(Long dsID, Collection<String> controllerIDs);
|
||||
List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(Collection<Entry<String, Long>> assignments);
|
||||
|
||||
/**
|
||||
* Cancels the {@link Action} with the given ID. The method will immediately
|
||||
|
||||
@@ -80,10 +80,10 @@ public interface QuotaManagement {
|
||||
int getMaxTargetsPerRolloutGroup();
|
||||
|
||||
/**
|
||||
* @return the maximum number of targets which for a manual distribution set
|
||||
* assignment
|
||||
* @return the maximum number of target distribution set assignments
|
||||
* resulting from a manual assignment
|
||||
*/
|
||||
int getMaxTargetsPerManualAssignment();
|
||||
int getMaxTargetDistributionSetAssignmentsPerManualAssignment();
|
||||
|
||||
/**
|
||||
* @return the maximum number of targets for an automatic distribution set
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 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.repository.exception;
|
||||
|
||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
|
||||
/**
|
||||
* This exception is thrown if an operation requires multiassignments, but the
|
||||
* feature is not enabled.
|
||||
*
|
||||
*/
|
||||
public class MultiAssignmentIsNotEnabledException extends AbstractServerRtException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final SpServerError THIS_ERROR = SpServerError.SP_MULTIASSIGNMENT_NOT_ENABLED;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public MultiAssignmentIsNotEnabledException() {
|
||||
super(THIS_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameterized constructor.
|
||||
*
|
||||
* @param cause
|
||||
* of the exception
|
||||
*/
|
||||
public MultiAssignmentIsNotEnabledException(final Throwable cause) {
|
||||
super(THIS_ERROR, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameterized constructor.
|
||||
*
|
||||
* @param message
|
||||
* of the exception
|
||||
* @param cause
|
||||
* of the exception
|
||||
*/
|
||||
public MultiAssignmentIsNotEnabledException(final String message, final Throwable cause) {
|
||||
super(message, THIS_ERROR, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameterized constructor.
|
||||
*
|
||||
* @param message
|
||||
* of the exception
|
||||
*/
|
||||
public MultiAssignmentIsNotEnabledException(final String message) {
|
||||
super(message, THIS_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,8 @@ public final class QuotaExceededException extends AbstractServerRtException {
|
||||
* The maximum number of entities that can be assigned to the
|
||||
* parent entity.
|
||||
*/
|
||||
public QuotaExceededException(final String type, final String parentType, final Long parentId, final long requested,
|
||||
public QuotaExceededException(final String type, final String parentType, final Object parentId,
|
||||
final long requested,
|
||||
final long quota) {
|
||||
super(String.format(ASSIGNMENT_QUOTA_EXCEEDED_MESSAGE, requested, type, parentType,
|
||||
parentId != null ? String.valueOf(parentId) : "<new>", quota), SpServerError.SP_QUOTA_EXCEEDED);
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 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.repository.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
|
||||
/**
|
||||
* A custom view on assigning a {@link DistributionSet} to a {@link Target}.
|
||||
*
|
||||
*/
|
||||
public class DeploymentRequest {
|
||||
private final Long distributionSetId;
|
||||
private final TargetWithActionType targetWithActionType;
|
||||
|
||||
/**
|
||||
* Constructor that also accepts maintenance schedule parameters and checks
|
||||
* for validity of the specified maintenance schedule.
|
||||
*
|
||||
* @param controllerId
|
||||
* for which the action is created.
|
||||
* @param distributionSetId
|
||||
* of the distribution set that that should be assigned to the
|
||||
* controller.
|
||||
* @param actionType
|
||||
* specified for the action.
|
||||
* @param forceTime
|
||||
* at what time the type soft turns into forced.
|
||||
* @param maintenanceSchedule
|
||||
* is the cron expression to be used for scheduling maintenance
|
||||
* windows. Expression has 6 mandatory fields and 1 last optional
|
||||
* field: "second minute hour dayofmonth month weekday year"
|
||||
* @param maintenanceWindowDuration
|
||||
* in HH:mm:ss format specifying the duration of a maintenance
|
||||
* window, for example 00:30:00 for 30 minutes
|
||||
* @param maintenanceWindowTimeZone
|
||||
* is the time zone specified as +/-hh:mm offset from UTC, for
|
||||
* example +02:00 for CET summer time and +00:00 for UTC. The
|
||||
* start time of a maintenance window calculated based on the
|
||||
* cron expression is relative to this time zone.
|
||||
*
|
||||
* @throws InvalidMaintenanceScheduleException
|
||||
* if the parameters do not define a valid maintenance schedule.
|
||||
*/
|
||||
public DeploymentRequest(final String controllerId, final Long distributionSetId, final ActionType actionType,
|
||||
final long forceTime, final String maintenanceSchedule, final String maintenanceWindowDuration,
|
||||
final String maintenanceWindowTimeZone) {
|
||||
this.targetWithActionType = new TargetWithActionType(controllerId, actionType, forceTime, maintenanceSchedule,
|
||||
maintenanceWindowDuration,
|
||||
maintenanceWindowTimeZone);
|
||||
this.distributionSetId = distributionSetId;
|
||||
}
|
||||
|
||||
public Long getDistributionSetId() {
|
||||
return distributionSetId;
|
||||
}
|
||||
|
||||
public String getControllerId() {
|
||||
return targetWithActionType.getControllerId();
|
||||
}
|
||||
|
||||
public TargetWithActionType getTargetWithActionType() {
|
||||
return targetWithActionType;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"DeploymentRequest [controllerId=%s, distributionSetId=%d, actionType=%s, forceTime=%d, maintenanceSchedule=%s, maintenanceWindowDuration=%s, maintenanceWindowTimeZone=%s]",
|
||||
targetWithActionType.getControllerId(), getDistributionSetId(), targetWithActionType.getActionType(),
|
||||
targetWithActionType.getForceTime(), targetWithActionType.getMaintenanceSchedule(),
|
||||
targetWithActionType.getMaintenanceWindowDuration(),
|
||||
targetWithActionType.getMaintenanceWindowTimeZone());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(distributionSetId, targetWithActionType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final DeploymentRequest other = (DeploymentRequest) obj;
|
||||
return Objects.equals(distributionSetId, other.distributionSetId)
|
||||
&& Objects.equals(targetWithActionType, other.targetWithActionType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 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.repository.model;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
|
||||
/**
|
||||
* Builder for {@link DeploymentRequest}
|
||||
*
|
||||
*/
|
||||
public class DeploymentRequestBuilder {
|
||||
|
||||
private final String controllerId;
|
||||
private final Long distributionSetId;
|
||||
|
||||
private long forceTime = RepositoryModelConstants.NO_FORCE_TIME;
|
||||
private ActionType actionType = ActionType.FORCED;
|
||||
private String maintenanceSchedule;
|
||||
private String maintenanceWindowDuration;
|
||||
private String maintenanceWindowTimeZone;
|
||||
|
||||
/**
|
||||
* Create a builder for a target distribution set assignment with the
|
||||
* mandatory fields
|
||||
*
|
||||
* @param controllerId
|
||||
* ID of the target
|
||||
* @param distributionSetId
|
||||
* ID of the distribution set
|
||||
*/
|
||||
public DeploymentRequestBuilder(final String controllerId, final Long distributionSetId) {
|
||||
this.controllerId = controllerId;
|
||||
this.distributionSetId = distributionSetId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an other {@link ActionType} than {@link ActionType#FORCED}
|
||||
*
|
||||
* @param actionType
|
||||
* type to used
|
||||
* @return builder
|
||||
*/
|
||||
public DeploymentRequestBuilder setActionType(final ActionType actionType) {
|
||||
this.actionType = actionType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a forceTime other than the default one.
|
||||
*
|
||||
* @param forceTime
|
||||
* at what time the type soft turns into forced.
|
||||
* @return builder
|
||||
*/
|
||||
public DeploymentRequestBuilder setForceTime(final long forceTime) {
|
||||
this.forceTime = forceTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a maintenanceWindow
|
||||
*
|
||||
* @param maintenanceSchedule
|
||||
* is the cron expression to be used for scheduling maintenance
|
||||
* windows. Expression has 6 mandatory fields and 1 last optional
|
||||
* field: "second minute hour dayofmonth month weekday year"
|
||||
* @param maintenanceWindowDuration
|
||||
* in HH:mm:ss format specifying the duration of a maintenance
|
||||
* window, for example 00:30:00 for 30 minutes
|
||||
* @param maintenanceWindowTimeZone
|
||||
* is the time zone specified as +/-hh:mm offset from UTC, for
|
||||
* example +02:00 for CET summer time and +00:00 for UTC. The
|
||||
* start time of a maintenance window calculated based on the
|
||||
* cron expression is relative to this time zone.
|
||||
* @return builder
|
||||
*/
|
||||
public DeploymentRequestBuilder setMaintenance(final String maintenanceSchedule,
|
||||
final String maintenanceWindowDuration, final String maintenanceWindowTimeZone) {
|
||||
this.maintenanceSchedule = maintenanceSchedule;
|
||||
this.maintenanceWindowDuration = maintenanceWindowDuration;
|
||||
this.maintenanceWindowTimeZone = maintenanceWindowTimeZone;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* build the request
|
||||
*
|
||||
* @return the request object
|
||||
*/
|
||||
public DeploymentRequest build() {
|
||||
return new DeploymentRequest(controllerId, distributionSetId, actionType, forceTime, maintenanceSchedule,
|
||||
maintenanceWindowDuration, maintenanceWindowTimeZone);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
|
||||
@@ -16,7 +18,6 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
*
|
||||
*/
|
||||
public class TargetWithActionType {
|
||||
|
||||
private final String controllerId;
|
||||
private final ActionType actionType;
|
||||
private final long forceTime;
|
||||
@@ -24,10 +25,27 @@ public class TargetWithActionType {
|
||||
private String maintenanceWindowDuration;
|
||||
private String maintenanceWindowTimeZone;
|
||||
|
||||
/**
|
||||
* Constructor that uses {@link ActionType#FORCED}
|
||||
*
|
||||
* @param controllerId
|
||||
* ID if the controller
|
||||
*/
|
||||
public TargetWithActionType(final String controllerId) {
|
||||
this(controllerId, ActionType.FORCED, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that leaves the maintenance info empty
|
||||
*
|
||||
* @param controllerId
|
||||
* for which the action is created.
|
||||
* @param actionType
|
||||
* specified for the action.
|
||||
* @param forceTime
|
||||
* after that point in time the action is exposed as forcen in
|
||||
* case the type is {@link ActionType#TIMEFORCED}
|
||||
*/
|
||||
public TargetWithActionType(final String controllerId, final ActionType actionType, final long forceTime) {
|
||||
this.controllerId = controllerId;
|
||||
this.actionType = actionType != null ? actionType : ActionType.FORCED;
|
||||
@@ -42,6 +60,9 @@ public class TargetWithActionType {
|
||||
* for which the action is created.
|
||||
* @param actionType
|
||||
* specified for the action.
|
||||
* @param forceTime
|
||||
* after that point in time the action is exposed as forcen in
|
||||
* case the type is {@link ActionType#TIMEFORCED}
|
||||
* @param maintenanceSchedule
|
||||
* is the cron expression to be used for scheduling maintenance
|
||||
* windows. Expression has 6 mandatory fields and 1 last optional
|
||||
@@ -122,4 +143,30 @@ public class TargetWithActionType {
|
||||
+ "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(actionType, controllerId, forceTime, maintenanceSchedule, maintenanceWindowDuration,
|
||||
maintenanceWindowTimeZone);
|
||||
}
|
||||
|
||||
@SuppressWarnings("squid:S1067")
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final TargetWithActionType other = (TargetWithActionType) obj;
|
||||
return Objects.equals(actionType, other.actionType) && Objects.equals(controllerId, other.controllerId)
|
||||
&& Objects.equals(forceTime, other.forceTime)
|
||||
&& Objects.equals(maintenanceSchedule, other.maintenanceSchedule)
|
||||
&& Objects.equals(maintenanceWindowDuration, other.maintenanceWindowDuration)
|
||||
&& Objects.equals(maintenanceWindowTimeZone, other.maintenanceWindowTimeZone);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -89,8 +89,8 @@ public class PropertiesQuotaManagement implements QuotaManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxTargetsPerManualAssignment() {
|
||||
return securityProperties.getDos().getMaxTargetsPerManualAssignment();
|
||||
public int getMaxTargetDistributionSetAssignmentsPerManualAssignment() {
|
||||
return securityProperties.getDos().getMaxTargetDistributionSetAssignmentsPerManualAssignment();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,9 +10,9 @@ package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
@@ -51,17 +51,19 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
protected final ActionRepository actionRepository;
|
||||
private final ActionStatusRepository actionStatusRepository;
|
||||
private final QuotaManagement quotaManagement;
|
||||
private final BooleanSupplier multiAssignmentsConfig;
|
||||
|
||||
AbstractDsAssignmentStrategy(final TargetRepository targetRepository,
|
||||
final AfterTransactionCommitExecutor afterCommit, final EventPublisherHolder eventPublisherHolder,
|
||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final QuotaManagement quotaManagement) {
|
||||
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig) {
|
||||
this.targetRepository = targetRepository;
|
||||
this.afterCommit = afterCommit;
|
||||
this.eventPublisherHolder = eventPublisherHolder;
|
||||
this.actionRepository = actionRepository;
|
||||
this.actionStatusRepository = actionStatusRepository;
|
||||
this.quotaManagement = quotaManagement;
|
||||
this.multiAssignmentsConfig = multiAssignmentsConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,14 +201,14 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
new CancelTargetAssignmentEvent(target, actionId, eventPublisherHolder.getApplicationId())));
|
||||
}
|
||||
|
||||
JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap, final JpaTarget target,
|
||||
JpaAction createTargetAction(final TargetWithActionType targetWithActionType, final List<JpaTarget> targets,
|
||||
final JpaDistributionSet set) {
|
||||
|
||||
// enforce the 'max actions per target' quota
|
||||
assertActionsPerTargetQuota(target, 1);
|
||||
final Optional<JpaTarget> optTarget = targets.stream()
|
||||
.filter(t -> t.getControllerId().equals(targetWithActionType.getControllerId())).findFirst();
|
||||
|
||||
// create the action
|
||||
return getTargetWithActionType(targetsWithActionMap, target.getControllerId()).map(targetWithActionType -> {
|
||||
return optTarget.map(target -> {
|
||||
assertActionsPerTargetQuota(target, 1);
|
||||
final JpaAction actionForTarget = new JpaAction();
|
||||
actionForTarget.setActionType(targetWithActionType.getActionType());
|
||||
actionForTarget.setForcedTime(targetWithActionType.getForceTime());
|
||||
@@ -218,7 +220,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
actionForTarget.setMaintenanceWindowTimeZone(targetWithActionType.getMaintenanceWindowTimeZone());
|
||||
return actionForTarget;
|
||||
}).orElseGet(() -> {
|
||||
LOG.warn("Cannot find targetWithActionType for target '{}'.", target.getControllerId());
|
||||
LOG.warn("Cannot find target for targetWithActionType '{}'.", targetWithActionType.getControllerId());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
@@ -241,14 +243,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
actionRepository::countByTargetId);
|
||||
}
|
||||
|
||||
private static Optional<TargetWithActionType> getTargetWithActionType(
|
||||
final Map<String, TargetWithActionType> targetsWithActionMap, final String controllerId) {
|
||||
if (targetsWithActionMap.containsKey(controllerId)) {
|
||||
return Optional.of(targetsWithActionMap.get(controllerId));
|
||||
} else {
|
||||
return targetsWithActionMap.values().stream()
|
||||
.filter(t -> controllerId.equalsIgnoreCase(t.getControllerId())).findFirst();
|
||||
}
|
||||
protected boolean isMultiAssignmentsEnabled() {
|
||||
return multiAssignmentsConfig.getAsBoolean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -43,6 +44,7 @@ import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
@@ -59,6 +61,7 @@ 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.ActionStatus;
|
||||
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.DistributionSetType;
|
||||
@@ -81,8 +84,12 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.retry.backoff.FixedBackOffPolicy;
|
||||
import org.springframework.retry.policy.SimpleRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -131,6 +138,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final TenantAware tenantAware;
|
||||
private final Database database;
|
||||
private final RetryTemplate retryTemplate;
|
||||
|
||||
protected JpaDeploymentManagement(final EntityManager entityManager, final ActionRepository actionRepository,
|
||||
final DistributionSetRepository distributionSetRepository, final TargetRepository targetRepository,
|
||||
@@ -150,80 +158,88 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisherHolder,
|
||||
actionRepository, actionStatusRepository, quotaManagement, this::isMultiAssignmentsEnabled);
|
||||
offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, afterCommit,
|
||||
eventPublisherHolder, actionRepository, actionStatusRepository, quotaManagement);
|
||||
eventPublisherHolder, actionRepository, actionStatusRepository, quotaManagement,
|
||||
this::isMultiAssignmentsEnabled);
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.quotaManagement = quotaManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.tenantAware = tenantAware;
|
||||
this.database = database;
|
||||
retryTemplate = createRetryTemplate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetAssignmentResult offlineAssignedDistributionSet(final Long dsID,
|
||||
final Collection<String> controllerIDs) {
|
||||
final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID,
|
||||
controllerIDs.stream()
|
||||
.map(controllerId -> new TargetWithActionType(controllerId, ActionType.FORCED, -1))
|
||||
.collect(Collectors.toList()),
|
||||
null, offlineDsAssignmentStrategy);
|
||||
offlineDsAssignmentStrategy.sendDeploymentEvents(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetAssignmentResult assignDistributionSet(final long dsID, final ActionType actionType,
|
||||
final long forcedTimestamp, final Collection<String> controllerIDs) {
|
||||
|
||||
final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID,
|
||||
controllerIDs.stream()
|
||||
.map(controllerId -> new TargetWithActionType(controllerId, actionType, forcedTimestamp))
|
||||
.collect(Collectors.toList()),
|
||||
null, onlineDsAssignmentStrategy);
|
||||
onlineDsAssignmentStrategy.sendDeploymentEvents(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetAssignmentResult assignDistributionSet(final long dsID,
|
||||
final Collection<TargetWithActionType> targets) {
|
||||
|
||||
final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID, targets, null,
|
||||
onlineDsAssignmentStrategy);
|
||||
onlineDsAssignmentStrategy.sendDeploymentEvents(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DistributionSetAssignmentResult> assignDistributionSets(final Set<Long> dsIDs,
|
||||
final Collection<TargetWithActionType> targets) {
|
||||
|
||||
final List<DistributionSetAssignmentResult> results = dsIDs.stream()
|
||||
.map(dsID -> assignDistributionSetToTargets(dsID, targets, null, onlineDsAssignmentStrategy))
|
||||
public List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(
|
||||
final Collection<Entry<String, Long>> assignments) {
|
||||
final Collection<Entry<String, Long>> distinctAssignments = assignments.stream().distinct()
|
||||
.collect(Collectors.toList());
|
||||
onlineDsAssignmentStrategy.sendDeploymentEvents(results);
|
||||
|
||||
enforceMaxAssignmentsPerRequest(distinctAssignments.size());
|
||||
final List<DeploymentRequest> deploymentRequests = distinctAssignments.stream()
|
||||
.map(entry -> DeploymentManagement.deploymentRequest(entry.getKey(), entry.getValue()).build())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return assignDistributionSets(deploymentRequests, null, offlineDsAssignmentStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
public List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
final List<DeploymentRequest> deploymentRequests) {
|
||||
return assignDistributionSets(deploymentRequests, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
public List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
final List<DeploymentRequest> deploymentRequests, final String actionMessage) {
|
||||
return assignDistributionSets(deploymentRequests, actionMessage, onlineDsAssignmentStrategy);
|
||||
}
|
||||
|
||||
private List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
final List<DeploymentRequest> deploymentRequests, final String actionMessage,
|
||||
final AbstractDsAssignmentStrategy strategy) {
|
||||
final List<DeploymentRequest> validatedRequests = validateRequestForAssignments(deploymentRequests);
|
||||
final Map<Long, List<TargetWithActionType>> assignmentsByDsIds = convertRequest(validatedRequests);
|
||||
|
||||
final List<DistributionSetAssignmentResult> results = assignmentsByDsIds.entrySet().stream()
|
||||
.map(entry -> assignDistributionSetToTargetsWithRetry(entry.getKey(), entry.getValue(), actionMessage,
|
||||
strategy))
|
||||
.collect(Collectors.toList());
|
||||
strategy.sendDeploymentEvents(results);
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetAssignmentResult assignDistributionSet(final long dsID,
|
||||
final Collection<TargetWithActionType> targets, final String actionMessage) {
|
||||
private List<DeploymentRequest> validateRequestForAssignments(List<DeploymentRequest> deploymentRequests) {
|
||||
if (!isMultiAssignmentsEnabled()) {
|
||||
deploymentRequests = deploymentRequests.stream().distinct().collect(Collectors.toList());
|
||||
checkIfRequiresMultiAssignment(deploymentRequests);
|
||||
}
|
||||
checkQuotaForAssignment(deploymentRequests);
|
||||
return deploymentRequests;
|
||||
}
|
||||
|
||||
final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID, targets, actionMessage,
|
||||
onlineDsAssignmentStrategy);
|
||||
onlineDsAssignmentStrategy.sendDeploymentEvents(result);
|
||||
return result;
|
||||
private static Map<Long, List<TargetWithActionType>> convertRequest(
|
||||
final Collection<DeploymentRequest> deploymentRequests) {
|
||||
return deploymentRequests.stream().collect(Collectors.groupingBy(DeploymentRequest::getDistributionSetId,
|
||||
Collectors.mapping(DeploymentRequest::getTargetWithActionType, Collectors.toList())));
|
||||
}
|
||||
|
||||
private static void checkIfRequiresMultiAssignment(final Collection<DeploymentRequest> deploymentRequests) {
|
||||
final long distinctTargetsInRequest = deploymentRequests.stream()
|
||||
.map(request -> request.getTargetWithActionType().getControllerId()).distinct().count();
|
||||
if (distinctTargetsInRequest < deploymentRequests.size()) {
|
||||
throw new MultiAssignmentIsNotEnabledException();
|
||||
}
|
||||
}
|
||||
|
||||
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final Long dsID,
|
||||
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy) {
|
||||
final RetryCallback<DistributionSetAssignmentResult, ConcurrencyFailureException> retryCallback = retryContext -> assignDistributionSetToTargets(
|
||||
dsID, targetsWithActionType, actionMessage, assignmentStrategy);
|
||||
return retryTemplate.execute(retryCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,10 +275,10 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy) {
|
||||
|
||||
final JpaDistributionSet distributionSetEntity = getAndValidateDsById(dsID);
|
||||
checkQuotaForAssignment(targetsWithActionType, distributionSetEntity);
|
||||
final List<String> targetIds = targetsWithActionType.stream().map(TargetWithActionType::getControllerId).distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
final List<JpaTarget> targetEntities = assignmentStrategy.findTargetsForAssignment(
|
||||
targetsWithActionType.stream().map(TargetWithActionType::getControllerId).collect(Collectors.toList()),
|
||||
final List<JpaTarget> targetEntities = assignmentStrategy.findTargetsForAssignment(targetIds,
|
||||
distributionSetEntity.getId());
|
||||
|
||||
if (targetEntities.isEmpty()) {
|
||||
@@ -288,7 +304,10 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy, final JpaDistributionSet distributionSetEntity,
|
||||
final List<JpaTarget> targetEntities) {
|
||||
final List<List<Long>> targetEntitiesIdsChunks = getTargetEntitiesAsChunks(targetEntities);
|
||||
closeOrCancelActiveActions(assignmentStrategy, targetEntitiesIdsChunks);
|
||||
|
||||
if (!isMultiAssignmentsEnabled()) {
|
||||
closeOrCancelActiveActions(assignmentStrategy, targetEntitiesIdsChunks);
|
||||
}
|
||||
// cancel all scheduled actions which are in-active, these actions were
|
||||
// not active before and the manual assignment which has been done
|
||||
// cancels them
|
||||
@@ -320,7 +339,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
|
||||
private static DistributionSetAssignmentResult buildAssignmentResult(final JpaDistributionSet distributionSet,
|
||||
final List<JpaAction> assignedActions, final int totalTargetsForAssignment) {
|
||||
int alreadyAssignedTargetsCount = totalTargetsForAssignment - assignedActions.size();
|
||||
final int alreadyAssignedTargetsCount = totalTargetsForAssignment - assignedActions.size();
|
||||
|
||||
return new DistributionSetAssignmentResult(distributionSet, alreadyAssignedTargetsCount, assignedActions);
|
||||
}
|
||||
@@ -337,37 +356,31 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
return distributionSet;
|
||||
}
|
||||
|
||||
private void checkQuotaForAssignment(final Collection<TargetWithActionType> targetsWithActionType,
|
||||
final JpaDistributionSet distributionSet) {
|
||||
// enforce the 'max targets per manual assignment' quota
|
||||
if (!targetsWithActionType.isEmpty()) {
|
||||
assertMaxTargetsPerManualAssignmentQuota(distributionSet.getId(), targetsWithActionType.size());
|
||||
private void checkQuotaForAssignment(final Collection<DeploymentRequest> deploymentRequests) {
|
||||
if (!deploymentRequests.isEmpty()) {
|
||||
enforceMaxAssignmentsPerRequest(deploymentRequests.size());
|
||||
enforceMaxActionsPerTarget(deploymentRequests);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces the quota defining the maximum number of {@link Target}s per
|
||||
* manual {@link DistributionSet} assignment.
|
||||
*
|
||||
* @param id
|
||||
* of the distribution set
|
||||
* @param requested
|
||||
* number of targets to check
|
||||
*/
|
||||
private void assertMaxTargetsPerManualAssignmentQuota(final Long distributionSetId,
|
||||
final int requestedTargetsCount) {
|
||||
QuotaHelper.assertAssignmentQuota(distributionSetId, requestedTargetsCount,
|
||||
quotaManagement.getMaxTargetsPerManualAssignment(), Target.class, DistributionSet.class, null);
|
||||
private void enforceMaxAssignmentsPerRequest(final int requestedActions) {
|
||||
QuotaHelper.assertAssignmentRequestSizeQuota(requestedActions,
|
||||
quotaManagement.getMaxTargetDistributionSetAssignmentsPerManualAssignment());
|
||||
}
|
||||
|
||||
private void enforceMaxActionsPerTarget(final Collection<DeploymentRequest> deploymentRequests) {
|
||||
final int quota = quotaManagement.getMaxActionsPerTarget();
|
||||
|
||||
final Map<String, Long> countOfTargtInRequest = deploymentRequests.stream()
|
||||
.map(DeploymentRequest::getControllerId)
|
||||
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
|
||||
|
||||
countOfTargtInRequest.forEach((controllerId, count) -> QuotaHelper.assertAssignmentQuota(controllerId, count,
|
||||
quota, Action.class, Target.class, actionRepository::countByTargetControllerId));
|
||||
}
|
||||
|
||||
private void closeOrCancelActiveActions(final AbstractDsAssignmentStrategy assignmentStrategy,
|
||||
final List<List<Long>> targetIdsChunks) {
|
||||
|
||||
if (isMultiAssignmentsEnabled()) {
|
||||
LOG.debug("Multi Assignments feature is enabled: No need to close /cancel active actions.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isActionsAutocloseEnabled()) {
|
||||
assignmentStrategy.closeActiveActions(targetIdsChunks);
|
||||
} else {
|
||||
@@ -396,10 +409,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
private List<JpaAction> createActions(final Collection<TargetWithActionType> targetsWithActionType,
|
||||
final List<JpaTarget> targets, final AbstractDsAssignmentStrategy assignmentStrategy,
|
||||
final JpaDistributionSet set) {
|
||||
final Map<String, TargetWithActionType> targetsWithActionMap = targetsWithActionType.stream()
|
||||
.collect(Collectors.toMap(TargetWithActionType::getControllerId, Function.identity()));
|
||||
|
||||
return targets.stream().map(trg -> assignmentStrategy.createTargetAction(targetsWithActionMap, trg, set))
|
||||
return targetsWithActionType.stream().map(twt -> assignmentStrategy.createTargetAction(twt, targets, set))
|
||||
.filter(Objects::nonNull).map(actionRepository::save).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -820,4 +831,17 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
.runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType).getValue());
|
||||
}
|
||||
|
||||
private static RetryTemplate createRetryTemplate() {
|
||||
final RetryTemplate template = new RetryTemplate();
|
||||
|
||||
final FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
|
||||
backOffPolicy.setBackOffPeriod(Constants.TX_RT_DELAY);
|
||||
template.setBackOffPolicy(backOffPolicy);
|
||||
|
||||
final SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(Constants.TX_RT_MAX,
|
||||
Collections.singletonMap(ConcurrencyFailureException.class, true));
|
||||
template.setRetryPolicy(retryPolicy);
|
||||
|
||||
return template;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@ package org.eclipse.hawkbit.repository.jpa;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
@@ -44,9 +45,9 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
OfflineDsAssignmentStrategy(final TargetRepository targetRepository,
|
||||
final AfterTransactionCommitExecutor afterCommit, final EventPublisherHolder eventPublisherHolder,
|
||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final QuotaManagement quotaManagement) {
|
||||
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig) {
|
||||
super(targetRepository, afterCommit, eventPublisherHolder, actionRepository, actionStatusRepository,
|
||||
quotaManagement);
|
||||
quotaManagement, multiAssignmentsConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,10 +60,15 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
|
||||
@Override
|
||||
public List<JpaTarget> findTargetsForAssignment(final List<String> controllerIDs, final long setId) {
|
||||
return Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream()
|
||||
.map(ids -> targetRepository.findAll(SpecificationsBuilder.combineWithAnd(
|
||||
Arrays.asList(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, setId),
|
||||
TargetSpecifications.notEqualToTargetUpdateStatus(TargetUpdateStatus.PENDING)))))
|
||||
final Function<List<String>, List<JpaTarget>> mapper;
|
||||
if (isMultiAssignmentsEnabled()) {
|
||||
mapper = targetRepository::findAllByControllerId;
|
||||
} else {
|
||||
mapper = ids -> targetRepository.findAll(SpecificationsBuilder.combineWithAnd(
|
||||
Arrays.asList(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, setId),
|
||||
TargetSpecifications.notEqualToTargetUpdateStatus(TargetUpdateStatus.PENDING))));
|
||||
}
|
||||
return Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream().map(mapper)
|
||||
.flatMap(List::stream).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -84,9 +90,9 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap,
|
||||
final JpaTarget target, final JpaDistributionSet set) {
|
||||
final JpaAction result = super.createTargetAction(targetsWithActionMap, target, set);
|
||||
protected JpaAction createTargetAction(final TargetWithActionType targetWithActionType,
|
||||
final List<JpaTarget> targets, final JpaDistributionSet set) {
|
||||
final JpaAction result = super.createTargetAction(targetWithActionType, targets, set);
|
||||
if (result != null) {
|
||||
result.setStatus(Status.FINISHED);
|
||||
result.setActive(Boolean.FALSE);
|
||||
|
||||
@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Function;
|
||||
@@ -46,15 +45,12 @@ import com.google.common.collect.Lists;
|
||||
*/
|
||||
public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
|
||||
private final BooleanSupplier multiAssignmentsConfig;
|
||||
|
||||
OnlineDsAssignmentStrategy(final TargetRepository targetRepository,
|
||||
final AfterTransactionCommitExecutor afterCommit, final EventPublisherHolder eventPublisherHolder,
|
||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig) {
|
||||
super(targetRepository, afterCommit, eventPublisherHolder, actionRepository, actionStatusRepository,
|
||||
quotaManagement);
|
||||
this.multiAssignmentsConfig = multiAssignmentsConfig;
|
||||
quotaManagement, multiAssignmentsConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,9 +126,9 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap, final JpaTarget target,
|
||||
JpaAction createTargetAction(final TargetWithActionType targetWithActionType, final List<JpaTarget> targets,
|
||||
final JpaDistributionSet set) {
|
||||
final JpaAction result = super.createTargetAction(targetsWithActionMap, target, set);
|
||||
final JpaAction result = super.createTargetAction(targetWithActionType, targets, set);
|
||||
if (result != null) {
|
||||
result.setStatus(Status.RUNNING);
|
||||
}
|
||||
@@ -207,10 +203,6 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
.publishEvent(new MultiActionEvent(tenant, eventPublisherHolder.getApplicationId(), controllerIds)));
|
||||
}
|
||||
|
||||
private boolean isMultiAssignmentsEnabled() {
|
||||
return multiAssignmentsConfig.getAsBoolean();
|
||||
}
|
||||
|
||||
private static Stream<Action> filterCancellations(final List<Action> actions) {
|
||||
return actions.stream().filter(action -> {
|
||||
final Status actionStatus = action.getStatus();
|
||||
|
||||
@@ -19,11 +19,10 @@ import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
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.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -143,11 +142,11 @@ public class AutoAssignChecker {
|
||||
|
||||
return DeploymentHelper.runInNewTransaction(transactionManager, "autoAssignDSToTargets",
|
||||
Isolation.READ_COMMITTED.value(), status -> {
|
||||
final List<TargetWithActionType> targets = getTargetsWithActionType(targetFilterQuery.getQuery(),
|
||||
dsId, targetFilterQuery.getAutoAssignActionType(), PAGE_SIZE);
|
||||
final int count = targets.size();
|
||||
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(
|
||||
targetFilterQuery.getQuery(), dsId, targetFilterQuery.getAutoAssignActionType(), PAGE_SIZE);
|
||||
final int count = deploymentRequests.size();
|
||||
if (count > 0) {
|
||||
deploymentManagement.assignDistributionSet(dsId, targets, actionMessage);
|
||||
deploymentManagement.assignDistributionSets(deploymentRequests, actionMessage);
|
||||
}
|
||||
return count;
|
||||
});
|
||||
@@ -168,7 +167,7 @@ public class AutoAssignChecker {
|
||||
* maximum amount of targets to retrieve
|
||||
* @return list of targets with action type
|
||||
*/
|
||||
private List<TargetWithActionType> getTargetsWithActionType(final String targetFilterQuery, final Long dsId,
|
||||
private List<DeploymentRequest> createAssignmentRequests(final String targetFilterQuery, final Long dsId,
|
||||
final ActionType type, final int count) {
|
||||
final Page<Target> targets = targetManagement.findByTargetFilterQueryAndNonDS(PageRequest.of(0, count), dsId,
|
||||
targetFilterQuery);
|
||||
@@ -176,8 +175,10 @@ public class AutoAssignChecker {
|
||||
// specified)
|
||||
final ActionType autoAssignActionType = type == null ? ActionType.FORCED : type;
|
||||
|
||||
return targets.getContent().stream().map(t -> new TargetWithActionType(t.getControllerId(),
|
||||
autoAssignActionType, RepositoryModelConstants.NO_FORCE_TIME)).collect(Collectors.toList());
|
||||
return targets.getContent().stream()
|
||||
.map(t -> DeploymentManagement.deploymentRequest(t.getControllerId(), dsId)
|
||||
.setActionType(autoAssignActionType).build())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.utils;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.ToLongFunction;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@@ -76,8 +76,8 @@ public final class QuotaHelper {
|
||||
* if the assignment operation would cause the quota to be
|
||||
* exceeded
|
||||
*/
|
||||
public static void assertAssignmentQuota(final Long parentId, final long requested, final long limit,
|
||||
@NotNull final Class<?> type, @NotNull final Class<?> parentType, final Function<Long, Long> countFct) {
|
||||
public static <T> void assertAssignmentQuota(final T parentId, final long requested, final long limit,
|
||||
@NotNull final Class<?> type, @NotNull final Class<?> parentType, final ToLongFunction<T> countFct) {
|
||||
assertAssignmentQuota(parentId, requested, limit, type.getSimpleName(), parentType.getSimpleName(), countFct);
|
||||
}
|
||||
|
||||
@@ -104,8 +104,8 @@ public final class QuotaHelper {
|
||||
* if the assignment operation would cause the quota to be
|
||||
* exceeded
|
||||
*/
|
||||
public static void assertAssignmentQuota(final Long parentId, final long requested, final long limit,
|
||||
@NotNull final String type, @NotNull final String parentType, final Function<Long, Long> countFct) {
|
||||
public static <T> void assertAssignmentQuota(final T parentId, final long requested, final long limit,
|
||||
@NotNull final String type, @NotNull final String parentType, final ToLongFunction<T> countFct) {
|
||||
|
||||
// check if the quota is unlimited
|
||||
if (limit <= 0) {
|
||||
@@ -121,7 +121,7 @@ public final class QuotaHelper {
|
||||
}
|
||||
|
||||
if (parentId != null && countFct != null) {
|
||||
final long currentCount = countFct.apply(parentId);
|
||||
final long currentCount = countFct.applyAsLong(parentId);
|
||||
if (currentCount + requested > limit) {
|
||||
LOG.warn(
|
||||
"Cannot assign {} {} entities to {} '{}' because of the configured quota limit {}. Currently, there are {} {} entities assigned.",
|
||||
@@ -130,4 +130,22 @@ public final class QuotaHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the number of assignments in a request does not exceed the
|
||||
* limit.
|
||||
*
|
||||
* @param requested
|
||||
* the number of assignments that are to be made
|
||||
* @param limit
|
||||
* the maximum number of assignments per request
|
||||
*/
|
||||
public static void assertAssignmentRequestSizeQuota(final long requested, final long limit) {
|
||||
if (requested > limit) {
|
||||
final String message = String.format(
|
||||
"Quota exceeded: Cannot assign %s entities at once. The maximum is %s.", requested, limit);
|
||||
LOG.warn(message);
|
||||
throw new QuotaExceededException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@ import org.eclipse.hawkbit.repository.exception.InvalidTargetAttributeException;
|
||||
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
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.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
@@ -1328,8 +1327,8 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final String knownExternalref = "externalRefId" + i;
|
||||
|
||||
testdataFactory.createTarget(knownControllerId);
|
||||
final DistributionSetAssignmentResult assignmentResult = deploymentManagement.assignDistributionSet(
|
||||
knownDistributionSet.getId(), ActionType.FORCED, 0, Collections.singleton(knownControllerId));
|
||||
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(knownDistributionSet.getId(),
|
||||
knownControllerId);
|
||||
final Long actionId = getFirstAssignedActionId(assignmentResult);
|
||||
controllerManagement.updateActionExternalRef(actionId, knownExternalref);
|
||||
|
||||
|
||||
@@ -13,15 +13,18 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.eclipse.hawkbit.repository.ActionStatusFields;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.event.remote.MultiActionEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
|
||||
@@ -35,6 +38,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
|
||||
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
@@ -47,13 +51,13 @@ 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.ActionStatus;
|
||||
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.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
@@ -100,12 +104,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget();
|
||||
final String dsName = "DistributionSet";
|
||||
|
||||
verifyThrownExceptionBy(() -> deploymentManagement.assignDistributionSet(NOT_EXIST_IDL,
|
||||
Collections.singletonList(new TargetWithActionType(target.getControllerId()))), dsName);
|
||||
verifyThrownExceptionBy(() -> deploymentManagement.assignDistributionSet(NOT_EXIST_IDL,
|
||||
Collections.singletonList(new TargetWithActionType(target.getControllerId())), "xxx"), dsName);
|
||||
verifyThrownExceptionBy(() -> deploymentManagement.assignDistributionSet(NOT_EXIST_IDL, ActionType.FORCED,
|
||||
System.currentTimeMillis(), Collections.singletonList(target.getControllerId())), dsName);
|
||||
verifyThrownExceptionBy(() -> assignDistributionSet(NOT_EXIST_IDL, target.getControllerId()), dsName);
|
||||
|
||||
verifyThrownExceptionBy(() -> deploymentManagement.cancelAction(NOT_EXIST_IDL), "Action");
|
||||
verifyThrownExceptionBy(() -> deploymentManagement.countActionsByTarget(NOT_EXIST_ID), "Target");
|
||||
@@ -158,34 +157,35 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test verifies that the 'max actions per target' quota is enforced if the assigned distribution set is changed permanently.")
|
||||
public void changeDistributionSetAssignmentUntilMaxActionsPerTargetQuotaIsExceeded() {
|
||||
@Description("Test verifies that the 'max actions per target' quota is enforced.")
|
||||
public void assertMaxActionsPerTargetQuotaIsEnforced() {
|
||||
|
||||
final int maxActions = quotaManagement.getMaxActionsPerTarget();
|
||||
final List<Target> testTargets = testdataFactory.createTargets(1);
|
||||
final Target testTarget = testdataFactory.createTarget();
|
||||
final DistributionSet ds1 = testdataFactory.createDistributionSet("ds1");
|
||||
final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2");
|
||||
final DistributionSet ds3 = testdataFactory.createDistributionSet("ds3");
|
||||
|
||||
IntStream.range(0, maxActions).forEach(i -> {
|
||||
assignDistributionSet(i % 2 == 0 ? ds1 : ds2, testTargets);
|
||||
});
|
||||
enableMultiAssignments();
|
||||
for (int i = 0; i < maxActions; i++) {
|
||||
deploymentManagement.offlineAssignedDistributionSets(Collections
|
||||
.singletonList(new SimpleEntry<String, Long>(testTarget.getControllerId(), ds1.getId())));
|
||||
}
|
||||
|
||||
// change the distribution set one last time to trigger a quota hit
|
||||
assertThatExceptionOfType(QuotaExceededException.class)
|
||||
.isThrownBy(() -> assignDistributionSet(ds3, testTargets));
|
||||
.isThrownBy(() -> assignDistributionSet(ds1, Collections.singletonList(testTarget)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Assigns the same distribution set to many targets until the 'max targets per manual assignment' quota is exceeded.")
|
||||
public void assignDistributionSetUntilQuotaIsExceeded() {
|
||||
@Description("An assignment request with more assignments than allowed by 'maxTargetDistributionSetAssignmentsPerManualAssignment' quota throws an exception.")
|
||||
public void assignmentRequestThatIsTooLarge() {
|
||||
final int maxActions = quotaManagement.getMaxTargetDistributionSetAssignmentsPerManualAssignment();
|
||||
final DistributionSet ds1 = testdataFactory.createDistributionSet("1");
|
||||
final DistributionSet ds2 = testdataFactory.createDistributionSet("2");
|
||||
|
||||
final int maxTargets = quotaManagement.getMaxTargetsPerManualAssignment();
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
final List<Target> targets = testdataFactory.createTargets(maxActions, "assignmentTest1");
|
||||
assignDistributionSet(ds1, targets);
|
||||
|
||||
assignDistributionSet(ds, testdataFactory.createTargets(maxTargets, "ok"));
|
||||
assertThatExceptionOfType(QuotaExceededException.class)
|
||||
.isThrownBy(() -> assignDistributionSet(ds, testdataFactory.createTargets(maxTargets + 1, "fail")));
|
||||
targets.add(testdataFactory.createTarget("assignmentTest2"));
|
||||
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> assignDistributionSet(ds2, targets));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -455,6 +455,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1) })
|
||||
public void assignedDistributionSet() {
|
||||
|
||||
final List<String> controllerIds = testdataFactory.createTargets(10).stream().map(Target::getControllerId)
|
||||
.collect(Collectors.toList());
|
||||
final List<Target> onlineAssignedTargets = testdataFactory.createTargets(10, "2");
|
||||
@@ -464,8 +465,15 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
assignDistributionSet(testdataFactory.createDistributionSet("2"), onlineAssignedTargets);
|
||||
|
||||
final long current = System.currentTimeMillis();
|
||||
final List<Target> targets = deploymentManagement.offlineAssignedDistributionSet(ds.getId(), controllerIds)
|
||||
.getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList());;
|
||||
|
||||
final List<Entry<String, Long>> offlineAssignments = controllerIds.stream()
|
||||
.map(targetId -> new SimpleEntry<String, Long>(targetId, ds.getId())).collect(Collectors.toList());
|
||||
final List<DistributionSetAssignmentResult> assignmentResults = deploymentManagement
|
||||
.offlineAssignedDistributionSets(offlineAssignments);
|
||||
assertThat(assignmentResults).hasSize(1);
|
||||
final List<Target> targets = assignmentResults.get(0).getAssignedEntity().stream().map(Action::getTarget)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertThat(actionRepository.count()).isEqualTo(20);
|
||||
assertThat(actionRepository.findByDistributionSetId(PAGE, ds.getId())).as("Offline actions are not active")
|
||||
.allMatch(action -> !action.isActive());
|
||||
@@ -480,6 +488,33 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
.allMatch(target -> target.getLastModifiedAt() == target.getInstallationDate());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Offline assign multiple DSs to multiple Targets in multiassignment mode.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 2),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 4), @Expect(type = ActionCreatedEvent.class, count = 4),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6) })
|
||||
public void multiOfflineAssignment() {
|
||||
final List<String> targetIds = testdataFactory.createTargets(2).stream().map(Target::getControllerId)
|
||||
.collect(Collectors.toList());
|
||||
final List<Long> dsIds = testdataFactory.createDistributionSets(2).stream().map(DistributionSet::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
enableMultiAssignments();
|
||||
final List<Entry<String, Long>> offlineAssignments = new ArrayList<>();
|
||||
targetIds.forEach(targetId -> dsIds
|
||||
.forEach(dsId -> offlineAssignments.add(new SimpleEntry<String, Long>(targetId, dsId))));
|
||||
final List<DistributionSetAssignmentResult> assignmentResults = deploymentManagement
|
||||
.offlineAssignedDistributionSets(offlineAssignments);
|
||||
|
||||
assertThat(getResultingActionCount(assignmentResults)).isEqualTo(4);
|
||||
targetIds.forEach(controllerId -> {
|
||||
final List<Long> assignedDsIds = actionRepository.findByTargetControllerId(PAGE, controllerId).stream()
|
||||
.map(action -> action.getDistributionSet().getId()).collect(Collectors.toList());
|
||||
assertThat(assignedDsIds).containsExactlyInAnyOrderElementsOf(dsIds);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that if an account is set to action autoclose running actions in case of a new assigned set get closed and set to CANCELED.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 10),
|
||||
@@ -555,6 +590,92 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
assignment.stream().mapToLong(action -> action.getTarget().getId()).toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Assign multiple DSs to multiple Targets in one request in multiassignment mode.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 2),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 4), @Expect(type = ActionCreatedEvent.class, count = 4),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
|
||||
@Expect(type = MultiActionEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 0) })
|
||||
public void multiassignmentInOneRequest() {
|
||||
final List<Target> targets = testdataFactory.createTargets(2);
|
||||
final List<DistributionSet> distributionSets = testdataFactory.createDistributionSets(2);
|
||||
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(distributionSets, targets);
|
||||
|
||||
enableMultiAssignments();
|
||||
final List<DistributionSetAssignmentResult> results = deploymentManagement
|
||||
.assignDistributionSets(deploymentRequests);
|
||||
|
||||
assertThat(getResultingActionCount(results)).isEqualTo(deploymentRequests.size());
|
||||
final List<Long> dsIds = distributionSets.stream().map(DistributionSet::getId).collect(Collectors.toList());
|
||||
targets.forEach(target -> {
|
||||
final List<Long> assignedDsIds = actionRepository.findByTargetControllerId(PAGE, target.getControllerId())
|
||||
.stream().map(action -> action.getDistributionSet().getId()).collect(Collectors.toList());
|
||||
assertThat(assignedDsIds).containsExactlyInAnyOrderElementsOf(dsIds);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("A Request resulting in multiple assignments to a single target is only allowed when multiassignment is enabled.")
|
||||
public void multipleAssignmentsToTargetOnlyAllowedInMultiAssignMode() {
|
||||
final Target target = testdataFactory.createTarget();
|
||||
final List<DistributionSet> distributionSets = testdataFactory.createDistributionSets(2);
|
||||
|
||||
final DeploymentRequest targetToDS0 = DeploymentManagement
|
||||
.deploymentRequest(target.getControllerId(), distributionSets.get(0).getId()).build();
|
||||
final DeploymentRequest targetToDS1 = DeploymentManagement
|
||||
.deploymentRequest(target.getControllerId(), distributionSets.get(1).getId()).build();
|
||||
|
||||
Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class)
|
||||
.isThrownBy(() -> deploymentManagement.assignDistributionSets(Arrays.asList(targetToDS0, targetToDS1)));
|
||||
|
||||
enableMultiAssignments();
|
||||
assertThat(getResultingActionCount(
|
||||
deploymentManagement.assignDistributionSets(Arrays.asList(targetToDS0, targetToDS1)))).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Duplicate Assignments are removed from a request when multiassignment is disabled, otherwise not")
|
||||
public void duplicateAssignmentsInRequestAreOnlyRemovedIfMultiassignmentDisabled() {
|
||||
final Target target = testdataFactory.createTarget();
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
final List<DeploymentRequest> twoEqualAssignments = Collections.nCopies(2,
|
||||
DeploymentManagement.deploymentRequest(target.getControllerId(), ds.getId()).build());
|
||||
|
||||
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignments)))
|
||||
.isEqualTo(1);
|
||||
|
||||
enableMultiAssignments();
|
||||
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignments)))
|
||||
.isEqualTo(2);
|
||||
}
|
||||
|
||||
private int getResultingActionCount(final List<DistributionSetAssignmentResult> results) {
|
||||
return results.stream().map(DistributionSetAssignmentResult::getTotal).reduce(0, Integer::sum);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("An assignment request is not accepted if it would lead to a target exceeding the max actions per target quota.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 0) })
|
||||
public void maxActionsPerTargetIsCheckedBeforeAssignmentExecution() {
|
||||
final int maxActions = quotaManagement.getMaxActionsPerTarget();
|
||||
final String controllerId = testdataFactory.createTarget().getControllerId();
|
||||
final Long dsId = testdataFactory.createDistributionSet().getId();
|
||||
|
||||
final List<DeploymentRequest> deploymentRequests = Collections.nCopies(maxActions + 1,
|
||||
DeploymentManagement.deploymentRequest(controllerId, dsId).build());
|
||||
|
||||
enableMultiAssignments();
|
||||
Assertions.assertThatExceptionOfType(QuotaExceededException.class)
|
||||
.isThrownBy(() -> deploymentManagement.assignDistributionSets(deploymentRequests));
|
||||
assertThat(actionRepository.countByTargetControllerId(controllerId)).isEqualTo(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* test a simple deployment by calling the
|
||||
* {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)}
|
||||
@@ -563,11 +684,11 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
*/
|
||||
@Test
|
||||
@Description("Simple deployment or distribution set to target assignment test.")
|
||||
@ExpectEvents({@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@ExpectEvents({ @Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetCreatedEvent.class, count = 30), @Expect(type = ActionCreatedEvent.class, count = 20),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 20),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3)})
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
|
||||
public void assignDistributionSet2Targets() {
|
||||
|
||||
final String myCtrlIDPref = "myCtrlID";
|
||||
@@ -617,7 +738,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Test that it is not possible to assign a distribution set that is not complete.")
|
||||
@ExpectEvents({@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@ExpectEvents({ @Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetCreatedEvent.class, count = 10), @Expect(type = ActionCreatedEvent.class, count = 10),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 10),
|
||||
@@ -650,14 +771,14 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
@Test
|
||||
@Description("Multiple deployments or distribution set to target assignment test. Expected behaviour is that a new deployment "
|
||||
+ "overides unfinished old one which are canceled as part of the operation.")
|
||||
@ExpectEvents({@Expect(type = TargetCreatedEvent.class, count = 5 + 4),
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 5 + 4),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 3 * 4),
|
||||
@Expect(type = ActionCreatedEvent.class, count = 3 * 4),
|
||||
@Expect(type = ActionUpdatedEvent.class, count = 4 * 2),
|
||||
@Expect(type = CancelTargetAssignmentEvent.class, count = 4 * 2),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 3),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 9),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 2)})
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 2) })
|
||||
public void mutipleDeployments() throws InterruptedException {
|
||||
final String undeployedTargetPrefix = "undep-T";
|
||||
final int noOfUndeployedTargets = 5;
|
||||
@@ -982,10 +1103,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget("knownControllerId");
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet("a");
|
||||
// assign ds to create an action
|
||||
final DistributionSetAssignmentResult assignDistributionSet = deploymentManagement.assignDistributionSet(
|
||||
ds.getId(), ActionType.SOFT,
|
||||
org.eclipse.hawkbit.repository.model.RepositoryModelConstants.NO_FORCE_TIME,
|
||||
Collections.singletonList(target.getControllerId()));
|
||||
final DistributionSetAssignmentResult assignDistributionSet = assignDistributionSet(ds.getId(),
|
||||
target.getControllerId(), ActionType.SOFT);
|
||||
final Long actionId = getFirstAssignedActionId(assignDistributionSet);
|
||||
// verify preparation
|
||||
Action findAction = deploymentManagement.findAction(actionId).get();
|
||||
@@ -1006,10 +1125,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget("knownControllerId");
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet("a");
|
||||
// assign ds to create an action
|
||||
final DistributionSetAssignmentResult assignDistributionSet = deploymentManagement.assignDistributionSet(
|
||||
ds.getId(), ActionType.FORCED,
|
||||
org.eclipse.hawkbit.repository.model.RepositoryModelConstants.NO_FORCE_TIME,
|
||||
Collections.singletonList(target.getControllerId()));
|
||||
final DistributionSetAssignmentResult assignDistributionSet = assignDistributionSet(ds.getId(),
|
||||
target.getControllerId(), ActionType.FORCED);
|
||||
final Long actionId = getFirstAssignedActionId(assignDistributionSet);
|
||||
// verify perparation
|
||||
Action findAction = deploymentManagement.findAction(actionId).get();
|
||||
@@ -1023,23 +1140,22 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
findAction = deploymentManagement.findAction(actionId).get();
|
||||
assertThat(findAction.getActionType()).as("action type is wrong").isEqualTo(ActionType.FORCED);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Description("Tests the computation of already assigned entities returned as a result of an assignment")
|
||||
public void testAlreadyAssignedAndAssignedActionsInAssignmentResult(){
|
||||
// create target1, distributionSet, assign ds to target1 and finish update (close all actions)
|
||||
final Action action = prepareFinishedUpdate("target1", "ds", false);
|
||||
final Target target2 = testdataFactory.createTarget("target2");
|
||||
final Target target2 = testdataFactory.createTarget("target2");
|
||||
final Target target3 = testdataFactory.createTarget("target3");
|
||||
|
||||
// assign ds to target2, but don't finish update (actions should be still open)
|
||||
assignDistributionSet(action.getDistributionSet().getId(), target2.getControllerId());
|
||||
|
||||
final DistributionSetAssignmentResult assignmentResult = deploymentManagement.assignDistributionSet(
|
||||
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(
|
||||
action.getDistributionSet().getId(),
|
||||
Arrays.asList(new TargetWithActionType(action.getTarget().getControllerId()),
|
||||
new TargetWithActionType(target3.getControllerId())));
|
||||
|
||||
Arrays.asList(action.getTarget().getControllerId(), target3.getControllerId()), ActionType.FORCED);
|
||||
|
||||
assertThat(assignmentResult).isNotNull();
|
||||
assertThat(assignmentResult.getTotal()).as("Total count of assigned and already assigned targets").isEqualTo(2);
|
||||
assertThat(assignmentResult.getAssigned()).as("Total count of assigned targets").isEqualTo(1);
|
||||
@@ -1170,8 +1286,4 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
}
|
||||
|
||||
private void enableMultiAssignments() {
|
||||
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -102,8 +101,8 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
final String knownControllerId = "controller12345";
|
||||
final DistributionSet knownDistributionSet = testdataFactory.createDistributionSet();
|
||||
testdataFactory.createTarget(knownControllerId);
|
||||
final DistributionSetAssignmentResult assignmentResult = deploymentManagement.assignDistributionSet(
|
||||
knownDistributionSet.getId(), ActionType.FORCED, 0, Collections.singleton(knownControllerId));
|
||||
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(knownDistributionSet.getId(),
|
||||
knownControllerId);
|
||||
final Long manuallyAssignedActionId = getFirstAssignedActionId(assignmentResult);
|
||||
|
||||
// create rollout with the same distribution set already assigned
|
||||
@@ -142,8 +141,8 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet firstDistributionSet = testdataFactory.createDistributionSet();
|
||||
final DistributionSet secondDistributionSet = testdataFactory.createDistributionSet("second");
|
||||
testdataFactory.createTarget(knownControllerId);
|
||||
final DistributionSetAssignmentResult assignmentResult = deploymentManagement.assignDistributionSet(
|
||||
firstDistributionSet.getId(), ActionType.FORCED, 0, Collections.singleton(knownControllerId));
|
||||
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(firstDistributionSet.getId(),
|
||||
knownControllerId);
|
||||
final Long manuallyAssignedActionId = getFirstAssignedActionId(assignmentResult);
|
||||
|
||||
// create rollout with the same distribution set already assigned
|
||||
|
||||
@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa.autoassign;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -57,8 +56,8 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet firstDistributionSet = testdataFactory.createDistributionSet();
|
||||
final DistributionSet secondDistributionSet = testdataFactory.createDistributionSet("second");
|
||||
testdataFactory.createTarget(knownControllerId);
|
||||
final DistributionSetAssignmentResult assignmentResult = deploymentManagement.assignDistributionSet(
|
||||
firstDistributionSet.getId(), ActionType.FORCED, 0, Collections.singleton(knownControllerId));
|
||||
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(firstDistributionSet.getId(),
|
||||
knownControllerId);
|
||||
final Long manuallyAssignedActionId = getFirstAssignedActionId(assignmentResult);
|
||||
|
||||
// target filter query that matches all targets
|
||||
@@ -218,11 +217,11 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
|
||||
final String targetDsBIdPref = "B";
|
||||
final String targetDsCIdPref = "C";
|
||||
|
||||
List<Target> targetsA = createTargetsAndAutoAssignDistSet(targetDsAIdPref, 5, distributionSet,
|
||||
final List<Target> targetsA = createTargetsAndAutoAssignDistSet(targetDsAIdPref, 5, distributionSet,
|
||||
ActionType.FORCED);
|
||||
List<Target> targetsB = createTargetsAndAutoAssignDistSet(targetDsBIdPref, 10, distributionSet,
|
||||
final List<Target> targetsB = createTargetsAndAutoAssignDistSet(targetDsBIdPref, 10, distributionSet,
|
||||
ActionType.SOFT);
|
||||
List<Target> targetsC = createTargetsAndAutoAssignDistSet(targetDsCIdPref, 10, distributionSet,
|
||||
final List<Target> targetsC = createTargetsAndAutoAssignDistSet(targetDsCIdPref, 10, distributionSet,
|
||||
ActionType.DOWNLOAD_ONLY);
|
||||
|
||||
final int targetsCount = targetsA.size() + targetsB.size() + targetsC.size();
|
||||
|
||||
@@ -14,12 +14,10 @@ import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationPrope
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.ACTION_CLEANUP_ENABLED;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
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.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -54,10 +52,8 @@ public class AutoActionCleanupTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet ds1 = testdataFactory.createDistributionSet("ds1");
|
||||
final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2");
|
||||
|
||||
deploymentManagement.assignDistributionSet(ds1.getId(), ActionType.FORCED, 0,
|
||||
Collections.singletonList(trg1.getControllerId()));
|
||||
deploymentManagement.assignDistributionSet(ds2.getId(), ActionType.FORCED, 0,
|
||||
Collections.singletonList(trg2.getControllerId()));
|
||||
assignDistributionSet(ds1.getId(), trg1.getControllerId());
|
||||
assignDistributionSet(ds2.getId(), trg2.getControllerId());
|
||||
|
||||
assertThat(actionRepository.count()).isEqualTo(2);
|
||||
|
||||
@@ -80,10 +76,8 @@ public class AutoActionCleanupTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet ds1 = testdataFactory.createDistributionSet("ds1");
|
||||
final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2");
|
||||
|
||||
final Long action1 = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(ds1.getId(),
|
||||
ActionType.FORCED, 0, Collections.singletonList(trg1.getControllerId())));
|
||||
deploymentManagement.assignDistributionSet(ds2.getId(), ActionType.FORCED, 0,
|
||||
Collections.singletonList(trg2.getControllerId()));
|
||||
final Long action1 = getFirstAssignedActionId(assignDistributionSet(ds1.getId(), trg1.getControllerId()));
|
||||
assignDistributionSet(ds2.getId(), trg2.getControllerId());
|
||||
|
||||
setActionToCanceled(action1);
|
||||
|
||||
@@ -109,12 +103,9 @@ public class AutoActionCleanupTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet ds1 = testdataFactory.createDistributionSet("ds1");
|
||||
final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2");
|
||||
|
||||
final Long action1 = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(ds1.getId(),
|
||||
ActionType.FORCED, 0, Collections.singletonList(trg1.getControllerId())));
|
||||
final Long action2 = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(ds2.getId(),
|
||||
ActionType.FORCED, 0, Collections.singletonList(trg2.getControllerId())));
|
||||
final Long action3 = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(ds2.getId(),
|
||||
ActionType.FORCED, 0, Collections.singletonList(trg3.getControllerId())));
|
||||
final Long action1 = getFirstAssignedActionId(assignDistributionSet(ds1.getId(), trg1.getControllerId()));
|
||||
final Long action2 = getFirstAssignedActionId(assignDistributionSet(ds2.getId(), trg2.getControllerId()));
|
||||
final Long action3 = getFirstAssignedActionId(assignDistributionSet(ds2.getId(), trg3.getControllerId()));
|
||||
|
||||
assertThat(actionRepository.count()).isEqualTo(3);
|
||||
|
||||
@@ -144,12 +135,9 @@ public class AutoActionCleanupTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet ds1 = testdataFactory.createDistributionSet("ds1");
|
||||
final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2");
|
||||
|
||||
final Long action1 = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(ds1.getId(),
|
||||
ActionType.FORCED, 0, Collections.singletonList(trg1.getControllerId())));
|
||||
final Long action2 = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(ds2.getId(),
|
||||
ActionType.FORCED, 0, Collections.singletonList(trg2.getControllerId())));
|
||||
final Long action3 = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(ds2.getId(),
|
||||
ActionType.FORCED, 0, Collections.singletonList(trg3.getControllerId())));
|
||||
final Long action1 = getFirstAssignedActionId(assignDistributionSet(ds1.getId(), trg1.getControllerId()));
|
||||
final Long action2 = getFirstAssignedActionId(assignDistributionSet(ds2.getId(), trg2.getControllerId()));
|
||||
final Long action3 = getFirstAssignedActionId(assignDistributionSet(ds2.getId(), trg3.getControllerId()));
|
||||
|
||||
assertThat(actionRepository.count()).isEqualTo(3);
|
||||
|
||||
@@ -181,12 +169,9 @@ public class AutoActionCleanupTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet ds1 = testdataFactory.createDistributionSet("ds1");
|
||||
final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2");
|
||||
|
||||
final Long action1 = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(ds1.getId(),
|
||||
ActionType.FORCED, 0, Collections.singletonList(trg1.getControllerId())));
|
||||
final Long action2 = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(ds2.getId(),
|
||||
ActionType.FORCED, 0, Collections.singletonList(trg2.getControllerId())));
|
||||
final Long action3 = getFirstAssignedActionId(deploymentManagement.assignDistributionSet(ds2.getId(),
|
||||
ActionType.FORCED, 0, Collections.singletonList(trg3.getControllerId())));
|
||||
final Long action1 = getFirstAssignedActionId(assignDistributionSet(ds1.getId(), trg1.getControllerId()));
|
||||
final Long action2 = getFirstAssignedActionId(assignDistributionSet(ds2.getId(), trg2.getControllerId()));
|
||||
final Long action3 = getFirstAssignedActionId(assignDistributionSet(ds2.getId(), trg3.getControllerId()));
|
||||
|
||||
assertThat(actionRepository.count()).isEqualTo(3);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.test.util;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.CONTROLLER_ROLE;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.SYSTEM_ROLE;
|
||||
|
||||
@@ -15,7 +16,9 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
@@ -46,6 +49,7 @@ import org.eclipse.hawkbit.repository.TargetTagManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
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;
|
||||
@@ -55,11 +59,11 @@ import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.eclipse.hawkbit.repository.test.TestConfiguration;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.EventVerifier;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
@@ -231,8 +235,42 @@ public abstract class AbstractIntegrationTest {
|
||||
|
||||
protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final String controllerId,
|
||||
final ActionType actionType) {
|
||||
return deploymentManagement.assignDistributionSet(dsID, Collections.singletonList(
|
||||
new TargetWithActionType(controllerId, actionType, RepositoryModelConstants.NO_FORCE_TIME)));
|
||||
return assignDistributionSet(dsID, Collections.singletonList(controllerId), actionType);
|
||||
}
|
||||
|
||||
protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final String controllerId,
|
||||
final ActionType actionType, final long forcedTime) {
|
||||
return assignDistributionSet(dsID, Collections.singletonList(controllerId), actionType, forcedTime);
|
||||
}
|
||||
|
||||
protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final List<String> controllerIds,
|
||||
final ActionType actionType) {
|
||||
return assignDistributionSet(dsID, controllerIds, actionType, RepositoryModelConstants.NO_FORCE_TIME);
|
||||
}
|
||||
|
||||
protected DistributionSetAssignmentResult assignDistributionSet(final long dsID, final List<String> controllerIds,
|
||||
final ActionType actionType, final long forcedTime) {
|
||||
final List<DeploymentRequest> deploymentRequests = controllerIds.stream()
|
||||
.map(id -> DeploymentManagement.deploymentRequest(id, dsID).setActionType(actionType)
|
||||
.setForceTime(forcedTime).build())
|
||||
.collect(Collectors.toList());
|
||||
final List<DistributionSetAssignmentResult> results = deploymentManagement
|
||||
.assignDistributionSets(deploymentRequests);
|
||||
assertThat(results).hasSize(1);
|
||||
return results.get(0);
|
||||
}
|
||||
|
||||
protected DistributionSetAssignmentResult assignDistributionSet(final DistributionSet ds,
|
||||
final List<Target> targets) {
|
||||
final List<String> targetIds = targets.stream().map(Target::getControllerId).collect(Collectors.toList());
|
||||
return assignDistributionSet(ds.getId(), targetIds, ActionType.FORCED);
|
||||
}
|
||||
|
||||
private DistributionSetAssignmentResult makeAssignment(final DeploymentRequest request) {
|
||||
final List<DistributionSetAssignmentResult> results = deploymentManagement
|
||||
.assignDistributionSets(Collections.singletonList(request));
|
||||
assertThat(results).hasSize(1);
|
||||
return results.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,34 +302,37 @@ public abstract class AbstractIntegrationTest {
|
||||
protected DistributionSetAssignmentResult assignDistributionSetWithMaintenanceWindow(final long dsID,
|
||||
final String controllerId, final String maintenanceWindowSchedule, final String maintenanceWindowDuration,
|
||||
final String maintenanceWindowTimeZone) {
|
||||
return deploymentManagement.assignDistributionSet(dsID,
|
||||
Arrays.asList(new TargetWithActionType(controllerId, ActionType.FORCED,
|
||||
RepositoryModelConstants.NO_FORCE_TIME, maintenanceWindowSchedule, maintenanceWindowDuration,
|
||||
maintenanceWindowTimeZone)));
|
||||
|
||||
return makeAssignment(DeploymentManagement.deploymentRequest(controllerId, dsID)
|
||||
.setMaintenance(maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone)
|
||||
.build());
|
||||
}
|
||||
|
||||
protected DistributionSetAssignmentResult assignDistributionSetWithMaintenanceWindowTimeForced(final long dsID,
|
||||
final String controllerId, final String maintenanceWindowSchedule, final String maintenanceWindowDuration,
|
||||
protected DistributionSetAssignmentResult assignDistributionSetWithMaintenanceWindow(final long dsID,
|
||||
final String controllerId, final ActionType type, final String maintenanceWindowSchedule,
|
||||
final String maintenanceWindowDuration,
|
||||
final String maintenanceWindowTimeZone) {
|
||||
return deploymentManagement.assignDistributionSet(dsID,
|
||||
Arrays.asList(new TargetWithActionType(controllerId, ActionType.TIMEFORCED, System.currentTimeMillis(),
|
||||
maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone)));
|
||||
return makeAssignment(DeploymentManagement.deploymentRequest(controllerId, dsID).setActionType(type)
|
||||
.setMaintenance(maintenanceWindowSchedule, maintenanceWindowDuration, maintenanceWindowTimeZone)
|
||||
.build());
|
||||
}
|
||||
|
||||
protected DistributionSetAssignmentResult assignDistributionSet(final DistributionSet pset,
|
||||
final List<Target> targets) {
|
||||
return deploymentManagement.assignDistributionSet(pset.getId(),
|
||||
targets.stream().map(Target::getTargetWithActionType).collect(Collectors.toList()));
|
||||
protected List<DeploymentRequest> createAssignmentRequests(final Collection<DistributionSet> distributionSets,
|
||||
final Collection<Target> targets) {
|
||||
final List<DeploymentRequest> deploymentRequests = new ArrayList<>();
|
||||
distributionSets.forEach(ds -> targets.forEach(
|
||||
target -> deploymentRequests
|
||||
.add(DeploymentManagement.deploymentRequest(target.getControllerId(), ds.getId()).build()))
|
||||
);
|
||||
return deploymentRequests;
|
||||
}
|
||||
|
||||
protected DistributionSetAssignmentResult assignDistributionSet(final DistributionSet pset, final Target target) {
|
||||
return assignDistributionSet(pset, Arrays.asList(target));
|
||||
}
|
||||
|
||||
protected DistributionSetAssignmentResult assignDistributionSetTimeForced(final DistributionSet pset,
|
||||
final Target target) {
|
||||
return deploymentManagement.assignDistributionSet(pset.getId(), Arrays.asList(
|
||||
new TargetWithActionType(target.getControllerId(), ActionType.TIMEFORCED, System.currentTimeMillis())));
|
||||
protected void enableMultiAssignments() {
|
||||
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, true);
|
||||
}
|
||||
|
||||
protected DistributionSetMetadata createDistributionSetMetadata(final long dsId, final MetaData md) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -18,5 +18,4 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class MgmtSoftwareModuleTypeAssigment extends MgmtId {
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
| `400 Bad Request`
|
||||
| Bad Request - e.g. invalid parameters or 'multi assignments' is disabled
|
||||
|
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.";
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -245,15 +245,15 @@ public class HawkbitSecurityProperties {
|
||||
private int maxTargetsPerRolloutGroup = 20000;
|
||||
|
||||
/**
|
||||
* Maximum number of targets per rollout group
|
||||
* Maximum number of overall actions targets per target
|
||||
*/
|
||||
private int maxActionsPerTarget = 2000;
|
||||
|
||||
/**
|
||||
* Maximum number of targets for a manual distribution set assignment.
|
||||
* Must be greater than 1000.
|
||||
* Maximum number of actions resulting from a manual assignment of
|
||||
* distribution sets and targets. Must be greater than 1000.
|
||||
*/
|
||||
private int maxTargetsPerManualAssignment = 5000;
|
||||
private int maxTargetDistributionSetAssignmentsPerManualAssignment = 5000;
|
||||
|
||||
/**
|
||||
* Maximum number of targets for an automatic distribution set
|
||||
@@ -379,14 +379,15 @@ public class HawkbitSecurityProperties {
|
||||
this.maxActionsPerTarget = maxActionsPerTarget;
|
||||
}
|
||||
|
||||
public int getMaxTargetsPerManualAssignment() {
|
||||
return maxTargetsPerManualAssignment;
|
||||
public int getMaxTargetDistributionSetAssignmentsPerManualAssignment() {
|
||||
return maxTargetDistributionSetAssignmentsPerManualAssignment;
|
||||
}
|
||||
|
||||
public void setMaxTargetsPerManualAssignment(final int maxTargetsPerManualAssignment) {
|
||||
this.maxTargetsPerManualAssignment = maxTargetsPerManualAssignment;
|
||||
|
||||
public void setMaxTargetDistributionSetAssignmentsPerManualAssignment(
|
||||
final int maxTargetDistributionSetAssignmentsPerManualAssignment) {
|
||||
this.maxTargetDistributionSetAssignmentsPerManualAssignment = maxTargetDistributionSetAssignmentsPerManualAssignment;
|
||||
}
|
||||
|
||||
|
||||
public int getMaxTargetsPerAutoAssignment() {
|
||||
return maxTargetsPerAutoAssignment;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.management;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -17,12 +18,14 @@ import java.util.stream.Collectors;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
|
||||
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequestBuilder;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.eclipse.hawkbit.ui.UiProperties;
|
||||
import org.eclipse.hawkbit.ui.common.confirmwindow.layout.ConfirmationTab;
|
||||
import org.eclipse.hawkbit.ui.common.entity.TargetIdName;
|
||||
@@ -80,6 +83,7 @@ public final class TargetAssignmentOperations {
|
||||
* the Vaadin Message Source for multi language
|
||||
* @param eventSource
|
||||
* the source object for sending potential events
|
||||
* @throws MultiAssignmentIsNotEnabledException
|
||||
*/
|
||||
public static void saveAllAssignments(final List<Target> targets, final List<DistributionSet> distributionSets,
|
||||
final ManagementUIState managementUIState,
|
||||
@@ -101,31 +105,39 @@ public final class TargetAssignmentOperations {
|
||||
final String maintenanceTimeZone = maintenanceWindowLayout.getMaintenanceTimeZone();
|
||||
|
||||
final Set<Long> dsIds = distributionSets.stream().map(DistributionSet::getId).collect(Collectors.toSet());
|
||||
final List<TargetWithActionType> trgActionType = targets.stream()
|
||||
.map(t -> maintenanceWindowLayout.isEnabled()
|
||||
? new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp,
|
||||
maintenanceSchedule, maintenanceDuration, maintenanceTimeZone)
|
||||
: new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp))
|
||||
.collect(Collectors.toList());
|
||||
final List<DeploymentRequest> deploymentRequests = new ArrayList<>();
|
||||
dsIds.forEach(dsId -> targets.forEach(t -> {
|
||||
final DeploymentRequestBuilder request = DeploymentManagement.deploymentRequest(t.getControllerId(), dsId)
|
||||
.setActionType(actionType).setForceTime(forcedTimeStamp);
|
||||
if (maintenanceWindowLayout.isEnabled()) {
|
||||
request.setMaintenance(maintenanceSchedule, maintenanceDuration, maintenanceTimeZone);
|
||||
}
|
||||
deploymentRequests.add(request.build());
|
||||
}));
|
||||
|
||||
final List<DistributionSetAssignmentResult> results = deploymentManagement.assignDistributionSets(dsIds,
|
||||
trgActionType);
|
||||
try {
|
||||
final List<DistributionSetAssignmentResult> results = deploymentManagement
|
||||
.assignDistributionSets(deploymentRequests);
|
||||
// use the last one for the notification box
|
||||
final DistributionSetAssignmentResult assignmentResult = results.get(results.size() - 1);
|
||||
if (assignmentResult.getAssigned() > 0) {
|
||||
notification
|
||||
.displaySuccess(i18n.getMessage("message.target.assignment", assignmentResult.getAssigned()));
|
||||
}
|
||||
if (assignmentResult.getAlreadyAssigned() > 0) {
|
||||
notification.displaySuccess(
|
||||
i18n.getMessage("message.target.alreadyAssigned", assignmentResult.getAlreadyAssigned()));
|
||||
}
|
||||
|
||||
// use the last one for the notification box
|
||||
final DistributionSetAssignmentResult assignmentResult = results.get(results.size() - 1);
|
||||
if (assignmentResult.getAssigned() > 0) {
|
||||
notification.displaySuccess(i18n.getMessage("message.target.assignment", assignmentResult.getAssigned()));
|
||||
final Set<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toSet());
|
||||
refreshPinnedDetails(dsIds, targetIds, managementUIState, eventBus, eventSource);
|
||||
|
||||
notification.displaySuccess(i18n.getMessage("message.target.ds.assign.success"));
|
||||
eventBus.publish(eventSource, SaveActionWindowEvent.SAVED_ASSIGNMENTS);
|
||||
} catch (final MultiAssignmentIsNotEnabledException e) {
|
||||
notification.displayValidationError(i18n.getMessage("message.target.ds.multiassign.error"));
|
||||
LOG.error("UI allowed multiassignment although it is not enabled: {}", e);
|
||||
}
|
||||
if (assignmentResult.getAlreadyAssigned() > 0) {
|
||||
notification.displaySuccess(
|
||||
i18n.getMessage("message.target.alreadyAssigned", assignmentResult.getAlreadyAssigned()));
|
||||
}
|
||||
|
||||
final Set<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toSet());
|
||||
refreshPinnedDetails(dsIds, targetIds, managementUIState, eventBus, eventSource);
|
||||
|
||||
notification.displaySuccess(i18n.getMessage("message.target.ds.assign.success"));
|
||||
eventBus.publish(eventSource, SaveActionWindowEvent.SAVED_ASSIGNMENTS);
|
||||
}
|
||||
|
||||
private static void refreshPinnedDetails(final Set<Long> dsIds, final Set<Long> targetIds,
|
||||
@@ -275,4 +287,4 @@ public final class TargetAssignmentOperations {
|
||||
return SPUIComponentProvider.getHelpLink(i18n, maintenanceWindowHelpUrl);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
@@ -32,6 +33,7 @@ import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetTagManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
import org.eclipse.hawkbit.ui.common.tagdetails.TagData;
|
||||
import org.eclipse.hawkbit.ui.components.HawkbitErrorNotificationMessage;
|
||||
import org.eclipse.hawkbit.ui.management.event.BulkUploadValidationMessageEvent;
|
||||
@@ -300,13 +302,17 @@ public class BulkUploadHandler extends CustomComponent
|
||||
final ActionType actionType = ActionType.FORCED;
|
||||
final long forcedTimeStamp = new Date().getTime();
|
||||
final TargetBulkUpload targetBulkUpload = managementUIState.getTargetTableFilters().getBulkUpload();
|
||||
final List<String> targetsList = targetBulkUpload.getTargetsCreated();
|
||||
final List<String> targetIds = targetBulkUpload.getTargetsCreated();
|
||||
final Long dsSelected = (Long) comboBox.getValue();
|
||||
if (!distributionSetManagement.get(dsSelected).isPresent()) {
|
||||
return i18n.getMessage("message.bulk.upload.assignment.failed");
|
||||
}
|
||||
deploymentManagement.assignDistributionSet(targetBulkUpload.getDsNameAndVersion(), actionType,
|
||||
forcedTimeStamp, targetsList);
|
||||
final List<DeploymentRequest> deploymentRequests = targetIds.stream()
|
||||
.map(targetId -> DeploymentManagement
|
||||
.deploymentRequest(targetId, targetBulkUpload.getDsNameAndVersion())
|
||||
.setActionType(actionType).setForceTime(forcedTimeStamp).build())
|
||||
.collect(Collectors.toList());
|
||||
deploymentManagement.assignDistributionSets(deploymentRequests);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -516,6 +516,7 @@ message.dist.type.discard.success = All Distribution Types are discarded success
|
||||
message.dist.discard.success = All Distributions are discarded successfully !
|
||||
message.assign.discard.success = All assignments are discarded successfully !
|
||||
message.target.ds.assign.success = Assignment saved successfully !
|
||||
message.target.ds.multiassign.error = Cannot assign multiple distribution sets to a target!
|
||||
message.bulk.upload.assignment.failed = Distribution set assignment failed as distribution set no longer exists!
|
||||
message.bulk.upload.result.success = Successful: {0}
|
||||
message.bulk.upload.result.fail = Failed: {0}
|
||||
|
||||
Reference in New Issue
Block a user