Skip to content

Commit

Permalink
Assign multiple distribution sets to a target via mgmt api (#886)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
StefanKlt authored and stefbehl committed Sep 17, 2019
1 parent dba9724 commit 8687510
Show file tree
Hide file tree
Showing 50 changed files with 1,454 additions and 547 deletions.
10 changes: 7 additions & 3 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
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;
Expand All @@ -23,7 +22,6 @@
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;
Expand Down Expand Up @@ -193,8 +191,7 @@ public void artifactForFileResourceSHA1FoundTargetExistsIsAssigned() {
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);
Expand All @@ -212,8 +209,7 @@ public void artifactForFileResourceSHA1FoundByTargetIdTargetExistsIsAssigned() {
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);
Expand Down Expand Up @@ -246,8 +242,7 @@ public void targetTokenAuthentification() {
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);
Expand Down Expand Up @@ -309,8 +304,7 @@ public void artifactForFileResourceArtifactIdFoundTargetExistsIsAssigned() {
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);
Expand Down Expand Up @@ -365,8 +359,7 @@ public void artifactForFileResourceSoftwareModuleFilenameFoundTargetExistsIsAssi
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
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;
Expand Down Expand Up @@ -464,10 +463,6 @@ private void waitUntil(final Callable<Boolean> callable) {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;

Expand All @@ -21,20 +22,21 @@
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;
Expand All @@ -48,49 +50,17 @@
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
* @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
Expand All @@ -99,20 +69,24 @@ DistributionSetAssignmentResult assignDistributionSet(long dsID, @NotNull Action
* @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);
List<DistributionSetAssignmentResult> assignDistributionSets(
@NotEmpty List<DeploymentRequest> deploymentRequests);

/**
* Assigns the given set of {@link DistributionSet} entities to the given
* {@link Target}s using the specified {@link ActionType} and
* {@code forcetime}.
* Assigns {@link DistributionSet}s to {@link Target}s according to the
* {@link DeploymentRequest}.
*
* @param dsIDs
* the set of IDs of the distribution sets 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 list of assignment results
*
* @throws IncompleteDistributionSetException
Expand All @@ -126,42 +100,32 @@ DistributionSetAssignmentResult assignDistributionSet(long dsID,
* @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, String actionMessage);

/**
* Assigns the addressed {@link DistributionSet} to all {@link Target}s by
* their IDs with a specific {@link ActionType} and an action message.
*
* @param dsID
* the ID of the distribution set to assign
* @param targets
* a list of all targets and their action type
* @param actionMessage
* an optional message for the action status
* @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
* build a {@link DeploymentRequest} for a target distribution set
* assignment
*
* @throws QuotaExceededException
* if the maximum number of targets the distribution set can be
* assigned to at once is exceeded
* @param controllerId
* ID of target
* @param distributionSetId
* ID of distribution set
* @return the builder
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
DistributionSetAssignmentResult assignDistributionSet(long dsID, @NotEmpty Collection<TargetWithActionType> targets,
String actionMessage);
static DeploymentRequestBuilder deploymentRequest(final String controllerId, final long distributionSetId) {
return new DeploymentRequestBuilder(controllerId, distributionSetId);
}

/**
* Registers an "offline" assignment, i.e. adds a completed action for the
* given {@link DistributionSet} to the given {@link Target}s.
* 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/>
*
Expand All @@ -174,11 +138,10 @@ DistributionSetAssignmentResult assignDistributionSet(long dsID, @NotEmpty Colle
* <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
Expand All @@ -191,9 +154,13 @@ DistributionSetAssignmentResult assignDistributionSet(long dsID, @NotEmpty Colle
* @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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 8687510

Please sign in to comment.