Code format hawkbit-ddi-resource (#1941)

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-11-05 11:30:02 +02:00
committed by GitHub
parent d47944758d
commit 0319487525
14 changed files with 909 additions and 919 deletions

View File

@@ -1,6 +1,7 @@
# Eclipse.IoT hawkBit - DDI Resource
# Eclipse.IoT hawkBit - DDI Resource
This is the server-side implementation of the hawkBit DDI API and the hawkBit DDI Download API that is used by devices for communicating with the HawkBit Update Server through HTTP.
This is the server-side implementation of the hawkBit DDI API and the hawkBit DDI Download API that is used by devices
for communicating with the HawkBit Update Server through HTTP.
# Compile

View File

@@ -10,104 +10,105 @@
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-rest-parent</artifactId>
<version>${revision}</version>
</parent>
<artifactId>hawkbit-ddi-resource</artifactId>
<name>hawkBit :: REST :: DDI Resources</name>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-rest-parent</artifactId>
<version>${revision}</version>
</parent>
<artifactId>hawkbit-ddi-resource</artifactId>
<name>hawkBit :: REST :: DDI Resources</name>
<dependencies>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-ddi-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-rest-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependencies>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-ddi-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-rest-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-test</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-jpa</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-rest-core</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-http-security</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- Test -->
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-test</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-jpa</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-rest-core</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-http-security</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -45,72 +45,12 @@ import org.springframework.util.CollectionUtils;
* Utility class for the DDI API.
*/
public final class DataConversionHelper {
// utility class, private constructor.
private DataConversionHelper() {
}
static List<DdiChunk> createChunks(final Target target, final Action uAction,
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement,
final HttpRequest request, final ControllerManagement controllerManagement) {
final Map<Long, List<SoftwareModuleMetadata>> metadata = controllerManagement
.findTargetVisibleMetaDataBySoftwareModuleId(uAction.getDistributionSet().getModules().stream()
.map(SoftwareModule::getId).collect(Collectors.toList()));
return new ResponseList<>(uAction.getDistributionSet().getModules().stream()
.map(module -> new DdiChunk(mapChunkLegacyKeys(module.getType().getKey()), module.getVersion(),
module.getName(), module.isEncrypted() ? Boolean.TRUE : null,
createArtifacts(target, module, artifactUrlHandler, systemManagement, request),
mapMetadata(metadata.get(module.getId()))))
.collect(Collectors.toList()));
}
private static List<DdiMetadata> mapMetadata(final List<SoftwareModuleMetadata> metadata) {
return CollectionUtils.isEmpty(metadata) ? null
: metadata.stream().map(md -> new DdiMetadata(md.getKey(), md.getValue())).collect(Collectors.toList());
}
private static String mapChunkLegacyKeys(final String key) {
if ("application".equals(key)) {
return "bApp";
}
if ("runtime".equals(key)) {
return "jvm";
}
return key;
}
static List<DdiArtifact> createArtifacts(final Target target, final SoftwareModule module,
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement,
final HttpRequest request) {
return new ResponseList<>(module.getArtifacts().stream()
.map(artifact -> createArtifact(target, artifactUrlHandler, artifact, systemManagement, request))
.collect(Collectors.toList()));
}
private static DdiArtifact createArtifact(final Target target, final ArtifactUrlHandler artifactUrlHandler,
final Artifact artifact, final SystemManagement systemManagement, final HttpRequest request) {
final DdiArtifact file = new DdiArtifact();
file.setHashes(new DdiArtifactHash(artifact.getSha1Hash(), artifact.getMd5Hash(), artifact.getSha256Hash()));
file.setFilename(artifact.getFilename());
file.setSize(artifact.getSize());
artifactUrlHandler
.getUrls(new URLPlaceholder(systemManagement.getTenantMetadata().getTenant(),
systemManagement.getTenantMetadata().getId(), target.getControllerId(), target.getId(),
new SoftwareData(artifact.getSoftwareModule().getId(), artifact.getFilename(), artifact.getId(),
artifact.getSha1Hash())),
ApiType.DDI, request.getURI())
.forEach(entry -> file.add(Link.of(entry.getRef()).withRel(entry.getRel()).expand()));
return file;
}
public static DdiConfirmationBase createConfirmationBase(final Target target, final Action activeAction,
final DdiAutoConfirmationState autoConfirmationState, final TenantAware tenantAware) {
final String controllerId = target.getControllerId();
@@ -145,9 +85,9 @@ public final class DataConversionHelper {
if (activeAction != null) {
if (activeAction.isWaitingConfirmation()) {
result.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder
.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.getConfirmationBaseAction(tenantAware.getCurrentTenant(), target.getControllerId(),
activeAction.getId(), calculateEtag(activeAction), null))
.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.getConfirmationBaseAction(tenantAware.getCurrentTenant(), target.getControllerId(),
activeAction.getId(), calculateEtag(activeAction), null))
.withRel(DdiRestConstants.CONFIRMATION_BASE).expand());
} else if (activeAction.isCancelingOrCanceled()) {
@@ -163,9 +103,9 @@ public final class DataConversionHelper {
// change the payload of the
// response because of eTags.
result.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder
.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.getControllerDeploymentBaseAction(tenantAware.getCurrentTenant(), target.getControllerId(),
activeAction.getId(), calculateEtag(activeAction), null))
.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.getControllerDeploymentBaseAction(tenantAware.getCurrentTenant(), target.getControllerId(),
activeAction.getId(), calculateEtag(activeAction), null))
.withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION).expand());
}
}
@@ -189,13 +129,73 @@ public final class DataConversionHelper {
return result;
}
static List<DdiChunk> createChunks(final Target target, final Action uAction,
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement,
final HttpRequest request, final ControllerManagement controllerManagement) {
final Map<Long, List<SoftwareModuleMetadata>> metadata = controllerManagement
.findTargetVisibleMetaDataBySoftwareModuleId(uAction.getDistributionSet().getModules().stream()
.map(SoftwareModule::getId).collect(Collectors.toList()));
return new ResponseList<>(uAction.getDistributionSet().getModules().stream()
.map(module -> new DdiChunk(mapChunkLegacyKeys(module.getType().getKey()), module.getVersion(),
module.getName(), module.isEncrypted() ? Boolean.TRUE : null,
createArtifacts(target, module, artifactUrlHandler, systemManagement, request),
mapMetadata(metadata.get(module.getId()))))
.collect(Collectors.toList()));
}
static List<DdiArtifact> createArtifacts(final Target target, final SoftwareModule module,
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement,
final HttpRequest request) {
return new ResponseList<>(module.getArtifacts().stream()
.map(artifact -> createArtifact(target, artifactUrlHandler, artifact, systemManagement, request))
.collect(Collectors.toList()));
}
private static List<DdiMetadata> mapMetadata(final List<SoftwareModuleMetadata> metadata) {
return CollectionUtils.isEmpty(metadata) ? null
: metadata.stream().map(md -> new DdiMetadata(md.getKey(), md.getValue())).collect(Collectors.toList());
}
private static String mapChunkLegacyKeys(final String key) {
if ("application".equals(key)) {
return "bApp";
}
if ("runtime".equals(key)) {
return "jvm";
}
return key;
}
private static DdiArtifact createArtifact(final Target target, final ArtifactUrlHandler artifactUrlHandler,
final Artifact artifact, final SystemManagement systemManagement, final HttpRequest request) {
final DdiArtifact file = new DdiArtifact();
file.setHashes(new DdiArtifactHash(artifact.getSha1Hash(), artifact.getMd5Hash(), artifact.getSha256Hash()));
file.setFilename(artifact.getFilename());
file.setSize(artifact.getSize());
artifactUrlHandler
.getUrls(new URLPlaceholder(systemManagement.getTenantMetadata().getTenant(),
systemManagement.getTenantMetadata().getId(), target.getControllerId(), target.getId(),
new SoftwareData(artifact.getSoftwareModule().getId(), artifact.getFilename(), artifact.getId(),
artifact.getSha1Hash())),
ApiType.DDI, request.getURI())
.forEach(entry -> file.add(Link.of(entry.getRef()).withRel(entry.getRel()).expand()));
return file;
}
/**
* Calculates an etag for the given {@link Action} based on the entities
* hashcode and the {@link Action#isHitAutoForceTime(long)} to reflect a
* force switch.
*
* @param action
* to calculate the etag for
*
* @param action to calculate the etag for
* @return the etag
*/
private static int calculateEtag(final Action action) {

View File

@@ -23,7 +23,7 @@ import org.springframework.stereotype.Controller;
*/
@Configuration
@ComponentScan
@Import({RestConfiguration.class, OpenApiConfiguration.class})
@Import({ RestConfiguration.class, OpenApiConfiguration.class })
public class DdiApiConfiguration {
}

View File

@@ -19,8 +19,8 @@ import java.util.Optional;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
@@ -28,6 +28,7 @@ import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback;
import org.eclipse.hawkbit.ddi.json.model.DdiActionHistory;
import org.eclipse.hawkbit.ddi.json.model.DdiActivateAutoConfirmation;
import org.eclipse.hawkbit.ddi.json.model.DdiArtifact;
import org.eclipse.hawkbit.ddi.json.model.DdiAssignedVersion;
import org.eclipse.hawkbit.ddi.json.model.DdiAutoConfirmationState;
import org.eclipse.hawkbit.ddi.json.model.DdiCancel;
import org.eclipse.hawkbit.ddi.json.model.DdiCancelActionToStop;
@@ -41,7 +42,6 @@ import org.eclipse.hawkbit.ddi.json.model.DdiDeployment;
import org.eclipse.hawkbit.ddi.json.model.DdiDeployment.DdiMaintenanceWindowStatus;
import org.eclipse.hawkbit.ddi.json.model.DdiDeployment.HandlingType;
import org.eclipse.hawkbit.ddi.json.model.DdiDeploymentBase;
import org.eclipse.hawkbit.ddi.json.model.DdiAssignedVersion;
import org.eclipse.hawkbit.ddi.json.model.DdiResult.FinalResult;
import org.eclipse.hawkbit.ddi.json.model.DdiUpdateMode;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
@@ -99,7 +99,7 @@ import org.springframework.web.context.WebApplicationContext;
@RestController
@Scope(value = WebApplicationContext.SCOPE_REQUEST)
public class DdiRootController implements DdiRootControllerRestApi {
private static final String GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET = "given action ({}) is not assigned to given target ({}).";
private static final String FALLBACK_REMARK = "Initiated using the Device Direct Integration API without providing a remark.";
@@ -208,36 +208,14 @@ public class DdiRootController implements DdiRootControllerRestApi {
RequestResponseContextHolder.getHttpServletRequest(),
(length, shippedSinceLastEvent,
total) -> eventPublisher.publishEvent(new DownloadProgressEvent(
tenantAware.getCurrentTenant(), statusId, shippedSinceLastEvent,
serviceMatcher != null ? serviceMatcher.getBusId() : bus.getId())));
tenantAware.getCurrentTenant(), statusId, shippedSinceLastEvent,
serviceMatcher != null ? serviceMatcher.getBusId() : bus.getId())));
}
}
return result;
}
private ActionStatus checkAndLogDownload(final HttpServletRequest request, final Target target, final Long module) {
final Action action = controllerManagement
.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), module)
.orElseThrow(() -> new SoftwareModuleNotAssignedToTargetException(module, target.getControllerId()));
final String range = request.getHeader("Range");
final String message;
if (range != null) {
message = RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads range " + range + " of: "
+ request.getRequestURI();
} else {
message = RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads " + request.getRequestURI();
}
return controllerManagement.addInformationalActionStatus(
entityFactory.actionStatus().create(action.getId()).status(Status.DOWNLOAD).message(message));
}
private static boolean checkModule(final String fileName, final SoftwareModule module) {
return module == null || module.getArtifactByFilename(fileName).isEmpty();
}
@Override
// Exception squid:S3655 - Optional access is checked in checkModule
// subroutine
@@ -301,30 +279,6 @@ public class DdiRootController implements DdiRootControllerRestApi {
return ResponseEntity.notFound().build();
}
private static HandlingType calculateDownloadType(final Action action) {
if (action.isDownloadOnly() || action.isForcedOrTimeForced()) {
return HandlingType.FORCED;
}
return HandlingType.ATTEMPT;
}
private static DdiMaintenanceWindowStatus calculateMaintenanceWindow(final Action action) {
if (action.hasMaintenanceSchedule()) {
return action.isMaintenanceWindowAvailable() ? DdiMaintenanceWindowStatus.AVAILABLE
: DdiMaintenanceWindowStatus.UNAVAILABLE;
}
return null;
}
private static HandlingType calculateUpdateType(final Action action, final HandlingType downloadType) {
if (action.isDownloadOnly()) {
return HandlingType.SKIP;
} else if (action.hasMaintenanceSchedule()) {
return action.isMaintenanceWindowAvailable() ? downloadType : HandlingType.SKIP;
}
return downloadType;
}
@Override
public ResponseEntity<Void> postDeploymentBaseActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback,
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
@@ -349,91 +303,6 @@ public class DdiRootController implements DdiRootControllerRestApi {
return ResponseEntity.ok().build();
}
private ActionStatusCreate generateUpdateStatus(final DdiActionFeedback feedback, final String controllerId,
final Long actionId) {
final ActionStatusCreate actionStatusCreate = entityFactory.actionStatus().create(actionId);
final List<String> messages = new ArrayList<>();
if (!CollectionUtils.isEmpty(feedback.getStatus().getDetails())) {
messages.addAll(feedback.getStatus().getDetails());
}
final Integer code = feedback.getStatus().getCode();
if (code != null) {
actionStatusCreate.code(code);
messages.add("Device reported status code: " + code);
}
final Status status;
switch (feedback.getStatus().getExecution()) {
case CANCELED:
log.debug("Controller confirmed cancel (actionId: {}, controllerId: {}) as we got {} report.", actionId,
controllerId, feedback.getStatus().getExecution());
status = Status.CANCELED;
addMessageIfEmpty("Target confirmed cancellation.", messages);
break;
case REJECTED:
log.info("Controller reported internal error (actionId: {}, controllerId: {}) as we got {} report.",
actionId, controllerId, feedback.getStatus().getExecution());
status = Status.WARNING;
addMessageIfEmpty("Target REJECTED update", messages);
break;
case CLOSED:
status = handleClosedCase(feedback, controllerId, actionId, messages);
break;
case DOWNLOAD:
log.debug("Controller confirmed status of download (actionId: {}, controllerId: {}) as we got {} report.",
actionId, controllerId, feedback.getStatus().getExecution());
status = Status.DOWNLOAD;
addMessageIfEmpty("Target confirmed download start", messages);
break;
case DOWNLOADED:
log.debug("Controller confirmed download (actionId: {}, controllerId: {}) as we got {} report.", actionId,
controllerId, feedback.getStatus().getExecution());
status = Status.DOWNLOADED;
addMessageIfEmpty("Target confirmed download finished", messages);
break;
default:
status = handleDefaultCase(feedback, controllerId, actionId, messages);
break;
}
return actionStatusCreate.status(status).messages(messages);
}
private static void addMessageIfEmpty(final String text, final List<String> messages) {
if (messages != null && messages.isEmpty()) {
messages.add(RepositoryConstants.SERVER_MESSAGE_PREFIX + text + ".");
}
}
private Status handleDefaultCase(final DdiActionFeedback feedback, final String controllerId, final Long actionId,
final List<String> messages) {
final Status status;
log.debug("Controller reported intermediate status (actionId: {}, controllerId: {}) as we got {} report.",
actionId, controllerId, feedback.getStatus().getExecution());
status = Status.RUNNING;
addMessageIfEmpty("Target reported " + feedback.getStatus().getExecution(), messages);
return status;
}
private Status handleClosedCase(final DdiActionFeedback feedback, final String controllerId, final Long actionId,
final List<String> messages) {
final Status status;
log.debug("Controller reported closed (actionId: {}, controllerId: {}) as we got {} report.", actionId,
controllerId, feedback.getStatus().getExecution());
if (feedback.getStatus().getResult().getFinished() == FinalResult.FAILURE) {
status = Status.ERROR;
addMessageIfEmpty("Target reported CLOSED with ERROR!", messages);
} else {
status = Status.FINISHED;
addMessageIfEmpty("Target reported CLOSED with OK!", messages);
}
return status;
}
@Override
public ResponseEntity<Void> putConfigData(@Valid @RequestBody final DdiConfigData configData,
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId) {
@@ -501,6 +370,149 @@ public class DdiRootController implements DdiRootControllerRestApi {
}
@Override
public ResponseEntity<DdiConfirmationBase> getConfirmationBase(final String tenant, final String controllerId) {
log.debug("getConfirmationBase is called [controllerId={}].", controllerId);
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, IpUtil
.getClientIpFromRequest(RequestResponseContextHolder.getHttpServletRequest(), securityProperties));
final Action activeAction = controllerManagement.findActiveActionWithHighestWeight(controllerId).orElse(null);
final DdiAutoConfirmationState autoConfirmationState = getAutoConfirmationState(controllerId);
final DdiConfirmationBase confirmationBase = DataConversionHelper.createConfirmationBase(target, activeAction,
autoConfirmationState, tenantAware);
return new ResponseEntity<>(confirmationBase, HttpStatus.OK);
}
@Override
public ResponseEntity<DdiConfirmationBaseAction> getConfirmationBaseAction(
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
@PathVariable("actionId") final Long actionId,
@RequestParam(value = "c", required = false, defaultValue = "-1") final int resource,
@RequestParam(value = "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount) {
log.debug("getConfirmationBaseAction({},{})", controllerId, resource);
final Target target = findTarget(controllerId);
final Action action = findActionForTarget(actionId, target);
checkAndCancelExpiredAction(action);
if (!action.isCancelingOrCanceled() && action.isWaitingConfirmation()) {
final DdiConfirmationBaseAction base = generateDdiConfirmationBase(target, action,
actionHistoryMessageCount);
log.debug("Found an active UpdateAction for target {}. Returning confirmation: {}", controllerId, base);
return new ResponseEntity<>(base, HttpStatus.OK);
}
return ResponseEntity.notFound().build();
}
@Override
public ResponseEntity<Void> postConfirmationActionFeedback(
@Valid @RequestBody final DdiConfirmationFeedback feedback, @PathVariable("tenant") final String tenant,
@PathVariable("controllerId") final String controllerId,
@PathVariable("actionId") @NotNull final Long actionId) {
log.debug("provideConfirmationActionFeedback with feedback [controllerId={}, actionId={}]: {}", controllerId,
actionId, feedback);
final Target target = findTarget(controllerId);
final Action action = findActionForTarget(actionId, target);
try {
switch (feedback.getConfirmation()) {
case CONFIRMED:
log.info("Controller confirmed the action (actionId: {}, controllerId: {}) as we got {} report.",
actionId, controllerId, feedback.getConfirmation());
confirmationManagement.confirmAction(actionId, feedback.getCode(), feedback.getDetails());
break;
case DENIED:
default:
log.debug("Controller denied the action (actionId: {}, controllerId: {}) as we got {} report.",
actionId, controllerId, feedback.getConfirmation());
confirmationManagement.denyAction(actionId, feedback.getCode(), feedback.getDetails());
break;
}
} catch (final InvalidConfirmationFeedbackException e) {
if (e.getReason() == InvalidConfirmationFeedbackException.Reason.ACTION_CLOSED) {
log.warn("Updating action {} with confirmation {} not possible since action not active anymore.",
action.getId(), feedback.getConfirmation());
return new ResponseEntity<>(HttpStatus.GONE);
} else if (e.getReason() == InvalidConfirmationFeedbackException.Reason.NOT_AWAITING_CONFIRMATION) {
log.debug("Action is not waiting for confirmation, deny request.");
return ResponseEntity.notFound().build();
}
}
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<Void> activateAutoConfirmation(final String tenant, final String controllerId,
final DdiActivateAutoConfirmation body) {
final String initiator = body == null ? null : body.getInitiator();
final String remark = body == null ? FALLBACK_REMARK : body.getRemark();
log.debug("Activate auto-confirmation request for device '{}' with payload: [initiator='{}' | remark='{}'",
controllerId, initiator, remark);
confirmationManagement.activateAutoConfirmation(controllerId, initiator, remark);
return new ResponseEntity<>(HttpStatus.OK);
}
@Override
public ResponseEntity<Void> deactivateAutoConfirmation(final String tenant, final String controllerId) {
log.debug("Deactivate auto-confirmation request for device {}", controllerId);
confirmationManagement.deactivateAutoConfirmation(controllerId);
return new ResponseEntity<>(HttpStatus.OK);
}
@Override
public ResponseEntity<Void> setAsssignedOfflineVersion(@Valid @RequestBody DdiAssignedVersion ddiAssignedVersion,
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId) {
boolean updated = controllerManagement.updateOfflineAssignedVersion(controllerId,
ddiAssignedVersion.getName(), ddiAssignedVersion.getVersion());
if (updated) {
return new ResponseEntity<>(HttpStatus.CREATED);
}
return new ResponseEntity<>(HttpStatus.OK);
}
private static boolean checkModule(final String fileName, final SoftwareModule module) {
return module == null || module.getArtifactByFilename(fileName).isEmpty();
}
private static HandlingType calculateDownloadType(final Action action) {
if (action.isDownloadOnly() || action.isForcedOrTimeForced()) {
return HandlingType.FORCED;
}
return HandlingType.ATTEMPT;
}
private static DdiMaintenanceWindowStatus calculateMaintenanceWindow(final Action action) {
if (action.hasMaintenanceSchedule()) {
return action.isMaintenanceWindowAvailable() ? DdiMaintenanceWindowStatus.AVAILABLE
: DdiMaintenanceWindowStatus.UNAVAILABLE;
}
return null;
}
private static HandlingType calculateUpdateType(final Action action, final HandlingType downloadType) {
if (action.isDownloadOnly()) {
return HandlingType.SKIP;
} else if (action.hasMaintenanceSchedule()) {
return action.isMaintenanceWindowAvailable() ? downloadType : HandlingType.SKIP;
}
return downloadType;
}
private static void addMessageIfEmpty(final String text, final List<String> messages) {
if (messages != null && messages.isEmpty()) {
messages.add(RepositoryConstants.SERVER_MESSAGE_PREFIX + text + ".");
}
}
private static ActionStatusCreate generateActionCancelStatus(final DdiActionFeedback feedback, final Target target,
final Long actionId, final EntityFactory entityFactory) {
@@ -509,21 +521,21 @@ public class DdiRootController implements DdiRootControllerRestApi {
final List<String> messages = new ArrayList<>();
final Status status;
switch (feedback.getStatus().getExecution()) {
case CANCELED:
status = handleCaseCancelCanceled(feedback, target, actionId, messages);
break;
case REJECTED:
log.info("Target rejected the cancellation request (actionId: {}, controllerId: {}).", actionId,
target.getControllerId());
status = Status.CANCEL_REJECTED;
messages.add(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target rejected the cancellation request.");
break;
case CLOSED:
status = handleCancelClosedCase(feedback, messages);
break;
default:
status = Status.RUNNING;
break;
case CANCELED:
status = handleCaseCancelCanceled(feedback, target, actionId, messages);
break;
case REJECTED:
log.info("Target rejected the cancellation request (actionId: {}, controllerId: {}).", actionId,
target.getControllerId());
status = Status.CANCEL_REJECTED;
messages.add(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target rejected the cancellation request.");
break;
case CLOSED:
status = handleCancelClosedCase(feedback, messages);
break;
default:
status = Status.RUNNING;
break;
}
if (feedback.getStatus().getDetails() != null) {
@@ -564,6 +576,114 @@ public class DdiRootController implements DdiRootControllerRestApi {
return status;
}
/**
* Retrieve the update mode from the given update message.
*/
private static UpdateMode getUpdateMode(final DdiConfigData configData) {
final DdiUpdateMode mode = configData.getMode();
if (mode != null) {
return UpdateMode.valueOf(mode.name());
}
return null;
}
private ActionStatus checkAndLogDownload(final HttpServletRequest request, final Target target, final Long module) {
final Action action = controllerManagement
.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), module)
.orElseThrow(() -> new SoftwareModuleNotAssignedToTargetException(module, target.getControllerId()));
final String range = request.getHeader("Range");
final String message;
if (range != null) {
message = RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads range " + range + " of: "
+ request.getRequestURI();
} else {
message = RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads " + request.getRequestURI();
}
return controllerManagement.addInformationalActionStatus(
entityFactory.actionStatus().create(action.getId()).status(Status.DOWNLOAD).message(message));
}
private ActionStatusCreate generateUpdateStatus(final DdiActionFeedback feedback, final String controllerId,
final Long actionId) {
final ActionStatusCreate actionStatusCreate = entityFactory.actionStatus().create(actionId);
final List<String> messages = new ArrayList<>();
if (!CollectionUtils.isEmpty(feedback.getStatus().getDetails())) {
messages.addAll(feedback.getStatus().getDetails());
}
final Integer code = feedback.getStatus().getCode();
if (code != null) {
actionStatusCreate.code(code);
messages.add("Device reported status code: " + code);
}
final Status status;
switch (feedback.getStatus().getExecution()) {
case CANCELED:
log.debug("Controller confirmed cancel (actionId: {}, controllerId: {}) as we got {} report.", actionId,
controllerId, feedback.getStatus().getExecution());
status = Status.CANCELED;
addMessageIfEmpty("Target confirmed cancellation.", messages);
break;
case REJECTED:
log.info("Controller reported internal error (actionId: {}, controllerId: {}) as we got {} report.",
actionId, controllerId, feedback.getStatus().getExecution());
status = Status.WARNING;
addMessageIfEmpty("Target REJECTED update", messages);
break;
case CLOSED:
status = handleClosedCase(feedback, controllerId, actionId, messages);
break;
case DOWNLOAD:
log.debug("Controller confirmed status of download (actionId: {}, controllerId: {}) as we got {} report.",
actionId, controllerId, feedback.getStatus().getExecution());
status = Status.DOWNLOAD;
addMessageIfEmpty("Target confirmed download start", messages);
break;
case DOWNLOADED:
log.debug("Controller confirmed download (actionId: {}, controllerId: {}) as we got {} report.", actionId,
controllerId, feedback.getStatus().getExecution());
status = Status.DOWNLOADED;
addMessageIfEmpty("Target confirmed download finished", messages);
break;
default:
status = handleDefaultCase(feedback, controllerId, actionId, messages);
break;
}
return actionStatusCreate.status(status).messages(messages);
}
private Status handleDefaultCase(final DdiActionFeedback feedback, final String controllerId, final Long actionId,
final List<String> messages) {
final Status status;
log.debug("Controller reported intermediate status (actionId: {}, controllerId: {}) as we got {} report.",
actionId, controllerId, feedback.getStatus().getExecution());
status = Status.RUNNING;
addMessageIfEmpty("Target reported " + feedback.getStatus().getExecution(), messages);
return status;
}
private Status handleClosedCase(final DdiActionFeedback feedback, final String controllerId, final Long actionId,
final List<String> messages) {
final Status status;
log.debug("Controller reported closed (actionId: {}, controllerId: {}) as we got {} report.", actionId,
controllerId, feedback.getStatus().getExecution());
if (feedback.getStatus().getResult().getFinished() == FinalResult.FAILURE) {
status = Status.ERROR;
addMessageIfEmpty("Target reported CLOSED with ERROR!", messages);
} else {
status = Status.FINISHED;
addMessageIfEmpty("Target reported CLOSED with OK!", messages);
}
return status;
}
private Target findTarget(final String controllerId) {
return controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
@@ -588,8 +708,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
* If the action has a maintenance schedule defined but is no longer valid,
* cancel the action.
*
* @param action
* is the {@link Action} to check.
* @param action is the {@link Action} to check.
*/
private void checkAndCancelExpiredAction(final Action action) {
if (action != null && action.hasMaintenanceSchedule() && action.isMaintenanceScheduleLapsed()) {
@@ -601,44 +720,6 @@ public class DdiRootController implements DdiRootControllerRestApi {
}
}
/**
* Retrieve the update mode from the given update message.
*/
private static UpdateMode getUpdateMode(final DdiConfigData configData) {
final DdiUpdateMode mode = configData.getMode();
if (mode != null) {
return UpdateMode.valueOf(mode.name());
}
return null;
}
@Override
public ResponseEntity<DdiConfirmationBaseAction> getConfirmationBaseAction(
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
@PathVariable("actionId") final Long actionId,
@RequestParam(value = "c", required = false, defaultValue = "-1") final int resource,
@RequestParam(value = "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount) {
log.debug("getConfirmationBaseAction({},{})", controllerId, resource);
final Target target = findTarget(controllerId);
final Action action = findActionForTarget(actionId, target);
checkAndCancelExpiredAction(action);
if (!action.isCancelingOrCanceled() && action.isWaitingConfirmation()) {
final DdiConfirmationBaseAction base = generateDdiConfirmationBase(target, action,
actionHistoryMessageCount);
log.debug("Found an active UpdateAction for target {}. Returning confirmation: {}", controllerId, base);
return new ResponseEntity<>(base, HttpStatus.OK);
}
return ResponseEntity.notFound().build();
}
private DdiDeploymentBase generateDdiDeploymentBase(final Target target, final Action action,
final Integer actionHistoryMessageCount) {
final DdiActionHistory actionHistory = generateDdiActionHistory(action, actionHistoryMessageCount).orElse(null);
@@ -672,60 +753,6 @@ public class DdiRootController implements DdiRootControllerRestApi {
: Optional.of(new DdiActionHistory(action.getStatus().name(), actionHistoryMsgs));
}
@Override
public ResponseEntity<Void> postConfirmationActionFeedback(
@Valid @RequestBody final DdiConfirmationFeedback feedback, @PathVariable("tenant") final String tenant,
@PathVariable("controllerId") final String controllerId,
@PathVariable("actionId") @NotNull final Long actionId) {
log.debug("provideConfirmationActionFeedback with feedback [controllerId={}, actionId={}]: {}", controllerId,
actionId, feedback);
final Target target = findTarget(controllerId);
final Action action = findActionForTarget(actionId, target);
try {
switch (feedback.getConfirmation()) {
case CONFIRMED:
log.info("Controller confirmed the action (actionId: {}, controllerId: {}) as we got {} report.",
actionId, controllerId, feedback.getConfirmation());
confirmationManagement.confirmAction(actionId, feedback.getCode(), feedback.getDetails());
break;
case DENIED:
default:
log.debug("Controller denied the action (actionId: {}, controllerId: {}) as we got {} report.",
actionId, controllerId, feedback.getConfirmation());
confirmationManagement.denyAction(actionId, feedback.getCode(), feedback.getDetails());
break;
}
} catch (final InvalidConfirmationFeedbackException e) {
if (e.getReason() == InvalidConfirmationFeedbackException.Reason.ACTION_CLOSED) {
log.warn("Updating action {} with confirmation {} not possible since action not active anymore.",
action.getId(), feedback.getConfirmation());
return new ResponseEntity<>(HttpStatus.GONE);
} else if (e.getReason() == InvalidConfirmationFeedbackException.Reason.NOT_AWAITING_CONFIRMATION) {
log.debug("Action is not waiting for confirmation, deny request.");
return ResponseEntity.notFound().build();
}
}
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<DdiConfirmationBase> getConfirmationBase(final String tenant, final String controllerId) {
log.debug("getConfirmationBase is called [controllerId={}].", controllerId);
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, IpUtil
.getClientIpFromRequest(RequestResponseContextHolder.getHttpServletRequest(), securityProperties));
final Action activeAction = controllerManagement.findActiveActionWithHighestWeight(controllerId).orElse(null);
final DdiAutoConfirmationState autoConfirmationState = getAutoConfirmationState(controllerId);
final DdiConfirmationBase confirmationBase = DataConversionHelper.createConfirmationBase(target, activeAction,
autoConfirmationState, tenantAware);
return new ResponseEntity<>(confirmationBase, HttpStatus.OK);
}
private DdiAutoConfirmationState getAutoConfirmationState(final String controllerId) {
return confirmationManagement.getStatus(controllerId).map(status -> {
final DdiAutoConfirmationState state = DdiAutoConfirmationState.active(status.getActivatedAt());
@@ -739,33 +766,4 @@ public class DdiRootController implements DdiRootControllerRestApi {
return DdiAutoConfirmationState.disabled();
});
}
@Override
public ResponseEntity<Void> activateAutoConfirmation(final String tenant, final String controllerId,
final DdiActivateAutoConfirmation body) {
final String initiator = body == null ? null : body.getInitiator();
final String remark = body == null ? FALLBACK_REMARK : body.getRemark();
log.debug("Activate auto-confirmation request for device '{}' with payload: [initiator='{}' | remark='{}'",
controllerId, initiator, remark);
confirmationManagement.activateAutoConfirmation(controllerId, initiator, remark);
return new ResponseEntity<>(HttpStatus.OK);
}
@Override
public ResponseEntity<Void> deactivateAutoConfirmation(final String tenant, final String controllerId) {
log.debug("Deactivate auto-confirmation request for device {}", controllerId);
confirmationManagement.deactivateAutoConfirmation(controllerId);
return new ResponseEntity<>(HttpStatus.OK);
}
@Override
public ResponseEntity<Void> setAsssignedOfflineVersion(@Valid @RequestBody DdiAssignedVersion ddiAssignedVersion,
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId){
boolean updated = controllerManagement.updateOfflineAssignedVersion(controllerId,
ddiAssignedVersion.getName(), ddiAssignedVersion.getVersion());
if (updated) {
return new ResponseEntity<>(HttpStatus.CREATED);
}
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@@ -24,10 +24,18 @@ import java.time.Instant;
import java.util.Collections;
import java.util.List;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
import com.fasterxml.jackson.dataformat.cbor.CBORGenerator;
import com.fasterxml.jackson.dataformat.cbor.CBORParser;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback;
import org.eclipse.hawkbit.ddi.json.model.DdiConfirmationFeedback;
import org.eclipse.hawkbit.ddi.json.model.DdiAssignedVersion;
import org.eclipse.hawkbit.ddi.json.model.DdiConfirmationFeedback;
import org.eclipse.hawkbit.ddi.json.model.DdiProgress;
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
@@ -49,15 +57,6 @@ import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
import com.fasterxml.jackson.dataformat.cbor.CBORGenerator;
import com.fasterxml.jackson.dataformat.cbor.CBORParser;
@ContextConfiguration(classes = { DdiApiConfiguration.class, RestConfiguration.class,
RepositoryApplicationConfiguration.class, TestConfiguration.class })
@Import(TestChannelBinderConfiguration.class)
@@ -65,19 +64,17 @@ import com.fasterxml.jackson.dataformat.cbor.CBORParser;
public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrationTest {
public static final int HTTP_PORT = 8080;
protected static final String HTTP_LOCALHOST = String.format("http://localhost:%s/",HTTP_PORT);
protected static final String HTTP_LOCALHOST = String.format("http://localhost:%s/", HTTP_PORT);
protected static final String CONTROLLER_BASE = "/{tenant}/controller/v1/{controllerId}";
protected static final String SOFTWARE_MODULE_ARTIFACTS = CONTROLLER_BASE
+ "/softwaremodules/{softwareModuleId}/artifacts";
protected static final String DEPLOYMENT_BASE = CONTROLLER_BASE + "/deploymentBase/{actionId}";
protected static final String DEPLOYMENT_FEEDBACK = DEPLOYMENT_BASE + "/feedback";
protected static final String CANCEL_ACTION = CONTROLLER_BASE + "/cancelAction/{actionId}";
protected static final String CANCEL_FEEDBACK = CANCEL_ACTION + "/feedback";
protected static final String INSTALLED_BASE = CONTROLLER_BASE + "/installedBase/{actionId}";
protected static final String INSTALLED_BASE_ROOT = CONTROLLER_BASE + "/installedBase";
protected static final String DEPLOYMENT_FEEDBACK = DEPLOYMENT_BASE + "/feedback";
protected static final String CANCEL_FEEDBACK = CANCEL_ACTION + "/feedback";
protected static final String CONFIRMATION_BASE = CONTROLLER_BASE + "/confirmationBase";
protected static final String ACTIVATE_AUTO_CONFIRM = CONFIRMATION_BASE + "/activateAutoConfirm";
protected static final String DEACTIVATE_AUTO_CONFIRM = CONFIRMATION_BASE + "/deactivateAutoConfirm";
@@ -91,11 +88,9 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
/**
* Convert JSON to a CBOR equivalent.
*
* @param json
* JSON object to convert
* @param json JSON object to convert
* @return Equivalent CBOR data
* @throws IOException
* Invalid JSON input
* @throws IOException Invalid JSON input
*/
protected static byte[] jsonToCbor(final String json) throws IOException {
final JsonFactory jsonFactory = new JsonFactory();
@@ -113,11 +108,9 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
/**
* Convert CBOR to JSON equivalent.
*
* @param input
* CBOR data to convert
* @param input CBOR data to convert
* @return Equivalent JSON string
* @throws IOException
* Invalid CBOR input
* @throws IOException Invalid CBOR input
*/
protected static String cborToJson(final byte[] input) throws IOException {
final CBORFactory cborFactory = new CBORFactory();
@@ -132,6 +125,10 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
return stringWriter.toString();
}
protected static ObjectMapper getMapper() {
return objectMapper;
}
protected ResultActions postDeploymentFeedback(final String controllerId, final Long actionId, final String content,
final ResultMatcher statusMatcher) throws Exception {
return postDeploymentFeedback(MediaType.APPLICATION_JSON_UTF8, controllerId, actionId, content.getBytes(),
@@ -139,7 +136,7 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
}
protected ResultActions putInstalledBase(final String controllerId, final String content,
final ResultMatcher statusMatcher) throws Exception {
final ResultMatcher statusMatcher) throws Exception {
return mvc.perform(put(INSTALLED_BASE_ROOT, tenantAware.getCurrentTenant(), controllerId)
.content(content.getBytes()).contentType(MediaType.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultPrinter.print()).andExpect(statusMatcher);
@@ -170,7 +167,7 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
protected ResultActions performGet(final String url, final MediaType mediaType, final ResultMatcher statusMatcher,
final String... values) throws Exception {
return mvc.perform(MockMvcRequestBuilders.get(url, values).accept(mediaType)
.with(new RequestOnHawkbitDefaultPortPostProcessor()))
.with(new RequestOnHawkbitDefaultPortPostProcessor()))
.andDo(MockMvcResultPrinter.print()).andExpect(statusMatcher)
.andExpect(content().contentTypeCompatibleWith(mediaType));
}
@@ -200,61 +197,6 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
getDownloadAndUploadType(actionType), getDownloadAndUploadType(actionType));
}
private ResultActions verifyBasePayload(final String prefix, final ResultActions resultActions, final String controllerId,
final DistributionSet ds, final Artifact artifact, final Artifact artifactSignature, final Long actionId,
final Long osModuleId, final String downloadType, final String updateType) throws Exception {
return resultActions.andExpect(jsonPath("$.id", equalTo(String.valueOf(actionId))))
.andExpect(jsonPath(prefix + ".download", equalTo(downloadType)))
.andExpect(jsonPath(prefix + ".update", equalTo(updateType)))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='jvm')].name",
contains(ds.findFirstModuleByType(runtimeType).get().getName())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='jvm')].version",
contains(ds.findFirstModuleByType(runtimeType).get().getVersion())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].name",
contains(ds.findFirstModuleByType(osType).get().getName())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].version",
contains(ds.findFirstModuleByType(osType).get().getVersion())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].size", contains(ARTIFACT_SIZE)))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].filename",
contains(artifact.getFilename())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].hashes.md5",
contains(artifact.getMd5Hash())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].hashes.sha1",
contains(artifact.getSha1Hash())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].hashes.sha256",
contains(artifact.getSha256Hash())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0]._links.download-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifact.getFilename()
+ "/download")))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0]._links.md5sum-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifact.getFilename()
+ "/download.MD5SUM")))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].size", contains(ARTIFACT_SIZE)))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].filename",
contains(artifactSignature.getFilename())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].hashes.md5",
contains(artifactSignature.getMd5Hash())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].hashes.sha1",
contains(artifactSignature.getSha1Hash())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].hashes.sha256",
contains(artifactSignature.getSha256Hash())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1]._links.download-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifactSignature.getFilename()
+ "/download")))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1]._links.md5sum-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifactSignature.getFilename()
+ "/download.MD5SUM")))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].version",
contains(ds.findFirstModuleByType(appType).get().getVersion())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].metadata").doesNotExist())
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].name")
.value(ds.findFirstModuleByType(appType).get().getName()));
}
protected String installedBaseLink(final String controllerId, final String actionId) {
return HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/installedBase/" + actionId;
@@ -265,13 +207,6 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
+ "/deploymentBase/" + actionId;
}
private static String getDownloadAndUploadType(final Action.ActionType actionType) {
if (Action.ActionType.FORCED.equals(actionType)) {
return "forced";
}
return "attempt";
}
protected String getJsonRejectedCancelActionFeedback() throws JsonProcessingException {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.SUCCESS,
Collections.singletonList("rejected"));
@@ -367,7 +302,7 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
}
protected String getJsonConfirmationFeedback(final DdiConfirmationFeedback.Confirmation confirmation,
final Integer code, final List<String> messages) throws JsonProcessingException {
final Integer code, final List<String> messages) throws JsonProcessingException {
return objectMapper.writeValueAsString(new DdiConfirmationFeedback(confirmation, code, messages));
}
@@ -375,10 +310,6 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
return objectMapper.writeValueAsString(new DdiAssignedVersion(name, version));
}
protected static ObjectMapper getMapper(){
return objectMapper;
}
protected ResultActions getAndVerifyConfirmationBasePayload(final String controllerId, final MediaType mediaType,
final DistributionSet ds, final Artifact artifact, final Artifact artifactSignature, final Long actionId,
final Long osModuleId, final String downloadType, final String updateType) throws Exception {
@@ -391,4 +322,66 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
static void implicitLock(final DistributionSet set) {
((JpaDistributionSet) set).setOptLockRevision(set.getOptLockRevision() + 1);
}
private static String getDownloadAndUploadType(final Action.ActionType actionType) {
if (Action.ActionType.FORCED.equals(actionType)) {
return "forced";
}
return "attempt";
}
private ResultActions verifyBasePayload(final String prefix, final ResultActions resultActions, final String controllerId,
final DistributionSet ds, final Artifact artifact, final Artifact artifactSignature, final Long actionId,
final Long osModuleId, final String downloadType, final String updateType) throws Exception {
return resultActions.andExpect(jsonPath("$.id", equalTo(String.valueOf(actionId))))
.andExpect(jsonPath(prefix + ".download", equalTo(downloadType)))
.andExpect(jsonPath(prefix + ".update", equalTo(updateType)))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='jvm')].name",
contains(ds.findFirstModuleByType(runtimeType).get().getName())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='jvm')].version",
contains(ds.findFirstModuleByType(runtimeType).get().getVersion())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].name",
contains(ds.findFirstModuleByType(osType).get().getName())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].version",
contains(ds.findFirstModuleByType(osType).get().getVersion())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].size", contains(ARTIFACT_SIZE)))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].filename",
contains(artifact.getFilename())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].hashes.md5",
contains(artifact.getMd5Hash())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].hashes.sha1",
contains(artifact.getSha1Hash())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0].hashes.sha256",
contains(artifact.getSha256Hash())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0]._links.download-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifact.getFilename()
+ "/download")))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[0]._links.md5sum-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifact.getFilename()
+ "/download.MD5SUM")))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].size", contains(ARTIFACT_SIZE)))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].filename",
contains(artifactSignature.getFilename())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].hashes.md5",
contains(artifactSignature.getMd5Hash())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].hashes.sha1",
contains(artifactSignature.getSha1Hash())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1].hashes.sha256",
contains(artifactSignature.getSha256Hash())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1]._links.download-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifactSignature.getFilename()
+ "/download")))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='os')].artifacts[1]._links.md5sum-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifactSignature.getFilename()
+ "/download.MD5SUM")))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].version",
contains(ds.findFirstModuleByType(appType).get().getVersion())))
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].metadata").doesNotExist())
.andExpect(jsonPath(prefix + ".chunks[?(@.part=='bApp')].name")
.value(ds.findFirstModuleByType(appType).get().getName()));
}
}

View File

@@ -29,6 +29,9 @@ import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.apache.commons.lang3.RandomUtils;
import org.eclipse.hawkbit.ddi.rest.resource.DdiArtifactDownloadTest.DownloadTestConfiguration;
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
@@ -48,10 +51,6 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
/**
* Test artifact downloads from the controller.
*/
@@ -102,55 +101,55 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
// test now consistent data to test allowed methods
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
.header(HttpHeaders.IF_MATCH, artifact.getSha1Hash()))
.andExpect(status().isOk());
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isOk());
// test failed If-match
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
.header(HttpHeaders.IF_MATCH, "fsjkhgjfdhg"))
.andExpect(status().isPreconditionFailed());
// test invalid range
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
.header("Range", "bytes=1-10,hdsfjksdh"))
.andExpect(header().string("Content-Range", "bytes */" + 5 * 1024))
.andExpect(status().isRequestedRangeNotSatisfiable());
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
.header("Range", "bytes=100-10"))
.andExpect(header().string("Content-Range", "bytes */" + 5 * 1024))
.andExpect(status().isRequestedRangeNotSatisfiable());
// not allowed methods
mvc.perform(put("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
mvc.perform(post("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
mvc.perform(put("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
mvc.perform(post("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
}
@@ -182,8 +181,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
// now assign and download successful
assignDistributionSet(ds, targets);
final MvcResult result = mvc.perform(get(
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("Last-Modified", dateFormat.format(new Date(artifact.getCreatedAt()))))
@@ -191,7 +190,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
.andReturn();
assertTrue(
Arrays.equals(result.getResponse().getContentAsByteArray(), random), "The same file that was uploaded is expected when downloaded");
Arrays.equals(result.getResponse().getContentAsByteArray(), random),
"The same file that was uploaded is expected when downloaded");
// download complete
assertThat(downLoadProgress).isEqualTo(10);
@@ -217,8 +217,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
// download
final MvcResult result = mvc.perform(get(
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isOk()).andExpect(header().string("Content-Disposition",
"attachment;filename=" + artifact.getFilename() + ".MD5SUM"))
.andReturn();
@@ -258,8 +258,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
final String rangeString = "" + i * range + "-" + ((i + 1) * range - 1);
final MvcResult result = mvc.perform(get(
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1").header("Range",
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1").header("Range",
"bytes=" + rangeString))
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
@@ -276,8 +276,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
// return last 1000 Bytes
MvcResult result = mvc.perform(
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
.header("Range", "bytes=-1000"))
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
@@ -293,8 +293,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
// skip first 1000 Bytes and return the rest
result = mvc.perform(
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
.header("Range", "bytes=1000-"))
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
@@ -310,8 +310,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
// Start download from file end fails
mvc.perform(
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
.header("Range", "bytes=" + random.length + "-"))
.andExpect(status().isRequestedRangeNotSatisfiable())
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
@@ -322,8 +322,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
// multipart download - first 20 bytes in 2 parts
result = mvc.perform(
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
.header("Range", "bytes=0-9,10-19"))
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
.andExpect(content().contentType("multipart/byteranges; boundary=THIS_STRING_SEPARATES_MULTIPART"))

View File

@@ -25,6 +25,9 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
@@ -41,10 +44,6 @@ import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.integration.json.JsonPathUtils;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
/**
* Test cancel action from the controller.
*/
@@ -52,6 +51,9 @@ import io.qameta.allure.Story;
@Story("Cancel Action Resource")
class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
@Autowired
ActionStatusRepository actionStatusRepository;
@Test
@Description("Tests that the cancel action resource can be used with CBOR.")
void cancelActionCbor() throws Exception {
@@ -65,7 +67,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
final byte[] result = mvc
.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId(), tenantAware.getCurrentTenant())
.accept(DdiRestConstants.MEDIA_TYPE_CBOR))
.accept(DdiRestConstants.MEDIA_TYPE_CBOR))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(DdiRestConstants.MEDIA_TYPE_CBOR)).andReturn().getResponse()
.getContentAsByteArray();
@@ -76,7 +78,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// and submit feedback as CBOR
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(jsonToCbor(getJsonProceedingCancelActionFeedback()))
.contentType(DdiRestConstants.MEDIA_TYPE_CBOR).accept(DdiRestConstants.MEDIA_TYPE_CBOR))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -97,7 +99,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// controller rejects cancellation
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(getJsonRejectedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -106,8 +108,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// get update action anyway
mvc.perform(
get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/" + actionId,
tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/" + actionId,
tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.id", equalTo(String.valueOf(actionId))))
@@ -122,9 +124,9 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// and finish it
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/"
+ actionId + "/feedback", tenantAware.getCurrentTenant()).content(
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.NONE,
Collections.singletonList("message")))
+ actionId + "/feedback", tenantAware.getCurrentTenant()).content(
getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.NONE,
Collections.singletonList("message")))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -135,7 +137,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
.isEqualTo(ds);
assertThat(
targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getInstallationDate())
.isGreaterThanOrEqualTo(current);
.isGreaterThanOrEqualTo(current);
}
@@ -150,7 +152,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
final long timeBeforeFirstPoll = System.currentTimeMillis();
mvc.perform(get("/{tenant}/controller/v1/{controller}", tenantAware.getCurrentTenant(),
TestdataFactory.DEFAULT_CONTROLLER_ID).accept(MediaTypes.HAL_JSON)).andDo(MockMvcResultPrinter.print())
TestdataFactory.DEFAULT_CONTROLLER_ID).accept(MediaTypes.HAL_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(content().contentType(MediaTypes.HAL_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href",
@@ -179,7 +181,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
final long timeBefore2ndPoll = System.currentTimeMillis();
mvc.perform(get("/{tenant}/controller/v1/{controller}", tenantAware.getCurrentTenant(),
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.cancelAction.href",
@@ -190,7 +192,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
.isBetween(timeBefore2ndPoll, timeAfter2ndPoll);
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
+ cancelAction.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.id", equalTo(String.valueOf(cancelAction.getId()))))
@@ -200,7 +202,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// controller confirmed cancelled action, should not be active anymore
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON)
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON)
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -220,41 +222,31 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// not allowed methods
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/1",
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
mvc.perform(put("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/1",
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/1",
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
// non existing target
mvc.perform(get("/{tenant}/controller/v1/34534543/cancelAction/1", tenantAware.getCurrentTenant())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
createCancelAction("34534543");
// wrong media type
mvc.perform(get("/{tenant}/controller/v1/34534543/cancelAction/1", tenantAware.getCurrentTenant())
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotAcceptable());
}
private Action createCancelAction(final String targetid) {
final DistributionSet ds = testdataFactory.createDistributionSet(targetid);
final Target savedTarget = testdataFactory.createTarget(targetid);
final List<Target> toAssign = new ArrayList<>();
toAssign.add(savedTarget);
final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds, toAssign));
return deploymentManagement.cancelAction(actionId);
}
@Test
@Description("Tests the feedback channel of the cancel operation.")
void rootRsCancelActionFeedback() throws Exception {
@@ -272,7 +264,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(getJsonProceedingCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -281,7 +273,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(countActionStatusAll()).isEqualTo(3);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(getJsonResumedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -289,7 +281,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(countActionStatusAll()).isEqualTo(4);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(getJsonScheduledCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -299,7 +291,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// cancellation canceled -> should remove the action from active
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(getJsonCanceledCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -312,7 +304,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// error
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(getJsonRejectedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -321,7 +313,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// update closed -> should remove the action from active
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -359,7 +351,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(3);
assertThat(deploymentManagement.countActionsByTarget(savedTarget.getControllerId())).isEqualTo(3);
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
+ cancelAction.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.id", equalTo(String.valueOf(cancelAction.getId()))))
@@ -367,7 +359,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(countActionStatusAll()).isEqualTo(6);
mvc.perform(get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(),
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.cancelAction.href",
@@ -376,7 +368,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// now lets return feedback for the first cancelation
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -386,7 +378,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(2);
assertThat(deploymentManagement.countActionsByTarget(savedTarget.getControllerId())).isEqualTo(3);
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction2.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
+ cancelAction2.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.id", equalTo(String.valueOf(cancelAction2.getId()))))
@@ -394,7 +386,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(countActionStatusAll()).isEqualTo(8);
mvc.perform(get("/{tenant}/controller/v1/{controller}", tenantAware.getCurrentTenant(),
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.cancelAction.href",
@@ -403,7 +395,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// now lets return feedback for the second cancelation
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction2.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction2.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -412,8 +404,8 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(deploymentManagement.getAssignedDistributionSet(TestdataFactory.DEFAULT_CONTROLLER_ID).get())
.isEqualTo(ds3);
mvc.perform(
get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/" + actionId3,
tenantAware.getCurrentTenant()))
get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/" + actionId3,
tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(countActionStatusAll()).isEqualTo(10);
@@ -429,7 +421,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
.isEqualTo(ds3);
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction3.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
+ cancelAction3.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.id", equalTo(String.valueOf(cancelAction3.getId()))))
@@ -438,7 +430,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// now lets return feedback for the third cancelation
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction3.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction3.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -466,13 +458,13 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// only 97 action status left
for (int i = 0; i < 98; i++) {
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(feedback)
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(feedback)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(feedback)
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(feedback)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isForbidden());
}
@@ -485,22 +477,22 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// not allowed methods
mvc.perform(put("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()))
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()))
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
// bad content type
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_ATOM_XML)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isUnsupportedMediaType());
@@ -508,33 +500,41 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
// bad body
String invalidFeedback = "{\"status\":{\"execution\":\"546456456\",\"result\":{\"finished\":\"none\",\"progress\":{\"cnt\":2,\"of\":5}},\"details\":\"none\"]}}";
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(invalidFeedback)
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(invalidFeedback)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
// non existing target
mvc.perform(post("/{tenant}/controller/v1/12345/cancelAction/" + cancelAction.getId() + "/feedback",
tenantAware.getCurrentTenant()).content(getJsonClosedCancelActionFeedback())
tenantAware.getCurrentTenant()).content(getJsonClosedCancelActionFeedback())
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// invalid action
invalidFeedback = "{\"id\":\"sdfsdfsdfs\",\"status\":{\"execution\":\"closed\",\"result\":{\"finished\":\"none\",\"progress\":{\"cnt\":2,\"of\":5}},\"details\":\"details\"]}}";
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(invalidFeedback)
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(invalidFeedback)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
// finaly, get it right :)
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(getJsonClosedCancelActionFeedback()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
}
@Autowired
ActionStatusRepository actionStatusRepository;
private Action createCancelAction(final String targetid) {
final DistributionSet ds = testdataFactory.createDistributionSet(targetid);
final Target savedTarget = testdataFactory.createTarget(targetid);
final List<Target> toAssign = new ArrayList<>();
toAssign.add(savedTarget);
final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds, toAssign));
return deploymentManagement.cancelAction(actionId);
}
private long countActionStatusAll() {
return actionStatusRepository.count();
}

View File

@@ -28,6 +28,10 @@ import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Step;
import io.qameta.allure.Story;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
@@ -40,10 +44,6 @@ import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Step;
import io.qameta.allure.Story;
/**
* Test config data from the controller.
*/
@@ -66,8 +66,8 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
attributes.put(ATTRIBUTE_KEY_VALID, ATTRIBUTE_VALUE_VALID);
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(jsonToCbor(JsonBuilder.configData(attributes).toString()))
.contentType(DdiRestConstants.MEDIA_TYPE_CBOR)).andDo(MockMvcResultPrinter.print())
.content(jsonToCbor(JsonBuilder.configData(attributes).toString()))
.contentType(DdiRestConstants.MEDIA_TYPE_CBOR)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
assertThat(targetManagement.getControllerAttributes(TARGET1_ID)).isEqualTo(attributes);
}
@@ -81,15 +81,15 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
final long current = System.currentTimeMillis();
mvc.perform(
get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()).accept(MediaTypes.HAL_JSON))
get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()).accept(MediaTypes.HAL_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.configData.href", equalTo(
"http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/4712/configData")));
Thread.sleep(1); // is required: otherwise processing the next line is
// often too fast and
// the following assert will fail
// often too fast and
// the following assert will fail
assertThat(targetManagement.getByControllerID("4712").orElseThrow(NoSuchElementException::new)
.getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
@@ -107,7 +107,7 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
assertThat(updateControllerAttributes.isRequestControllerAttributes()).isFalse();
mvc.perform(
get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()).accept(MediaTypes.HAL_JSON))
get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()).accept(MediaTypes.HAL_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
@@ -125,14 +125,14 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
attributes.put(ATTRIBUTE_KEY_VALID, ATTRIBUTE_VALUE_VALID);
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(targetManagement.getControllerAttributes(TARGET1_ID)).isEqualTo(attributes);
// update
attributes.put("sdsds", "123412");
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(targetManagement.getControllerAttributes(TARGET1_ID)).isEqualTo(attributes);
}
@@ -149,13 +149,13 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
attributes.put("dsafsdf" + i, "sdsds" + i);
}
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
attributes = new HashMap<>();
attributes.put("on too many", "sdsds");
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isForbidden())
.andExpect(jsonPath("$.exceptionClass", equalTo(AssignmentQuotaExceededException.class.getName())))
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
@@ -183,17 +183,17 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
final Map<String, String> attributes = new HashMap<>();
attributes.put("dsafsdf", "sdsds");
mvc.perform(put("/{tenant}/controller/v1/4712/configData", tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaTypes.HAL_JSON))
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaTypes.HAL_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isUnsupportedMediaType());
// non existing target
mvc.perform(put("/{tenant}/controller/v1/456456/configData", tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// bad body
mvc.perform(put("/{tenant}/controller/v1/4712/configData", tenantAware.getCurrentTenant())
.content("{\"id\": \"51659181\"}").contentType(MediaType.APPLICATION_JSON))
.content("{\"id\": \"51659181\"}").contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
}
@@ -206,26 +206,6 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
putAndVerifyConfigDataWithValueTooLong();
}
@Step
private void putAndVerifyConfigDataWithKeyTooLong() throws Exception {
final Map<String, String> attributes = Collections.singletonMap(ATTRIBUTE_KEY_TOO_LONG, ATTRIBUTE_VALUE_VALID);
mvc.perform(put(DdiConfigDataTest.TARGET2_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.exceptionClass", equalTo(InvalidTargetAttributeException.class.getName())))
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_TARGET_ATTRIBUTES_INVALID.getKey())));
}
@Step
private void putAndVerifyConfigDataWithValueTooLong() throws Exception {
final Map<String, String> attributes = Collections.singletonMap(ATTRIBUTE_KEY_VALID, ATTRIBUTE_VALUE_TOO_LONG);
mvc.perform(put(DdiConfigDataTest.TARGET2_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.exceptionClass", equalTo(InvalidTargetAttributeException.class.getName())))
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_TARGET_ATTRIBUTES_INVALID.getKey())));
}
@Test
@Description("Verify that config data (device attributes) can be updated by the controller using different update modes (merge, replace, remove).")
void putConfigDataWithDifferentUpdateModes() throws Exception {
@@ -249,6 +229,26 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
putConfigDataWithInvalidUpdateMode();
}
@Step
private void putAndVerifyConfigDataWithKeyTooLong() throws Exception {
final Map<String, String> attributes = Collections.singletonMap(ATTRIBUTE_KEY_TOO_LONG, ATTRIBUTE_VALUE_VALID);
mvc.perform(put(DdiConfigDataTest.TARGET2_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.exceptionClass", equalTo(InvalidTargetAttributeException.class.getName())))
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_TARGET_ATTRIBUTES_INVALID.getKey())));
}
@Step
private void putAndVerifyConfigDataWithValueTooLong() throws Exception {
final Map<String, String> attributes = Collections.singletonMap(ATTRIBUTE_KEY_VALID, ATTRIBUTE_VALUE_TOO_LONG);
mvc.perform(put(DdiConfigDataTest.TARGET2_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.exceptionClass", equalTo(InvalidTargetAttributeException.class.getName())))
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_TARGET_ATTRIBUTES_INVALID.getKey())));
}
@Step
private void putConfigDataWithInvalidUpdateMode() throws Exception {
// create some attriutes
@@ -258,8 +258,8 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
// use an invalid update mode
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes, "KJHGKJHGKJHG").toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.content(JsonBuilder.configData(attributes, "KJHGKJHGKJHG").toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isBadRequest());
}
@@ -274,8 +274,8 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
removeAttributes.put("k3", "bar");
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(removeAttributes, "remove").toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.content(JsonBuilder.configData(removeAttributes, "remove").toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
// verify attribute removal
@@ -298,8 +298,8 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
mergeAttributes.put("k1", "v1_modified_again");
mergeAttributes.put("k4", "v4");
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(mergeAttributes, "merge").toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.content(JsonBuilder.configData(mergeAttributes, "merge").toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
// verify attribute merge
@@ -325,8 +325,8 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
replacementAttributes.put("k2", "v2");
replacementAttributes.put("k3", "v3");
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(replacementAttributes, "replace").toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.content(JsonBuilder.configData(replacementAttributes, "replace").toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
// verify attribute replacement
@@ -349,7 +349,7 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
// set the initial attributes
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
// verify the initial parameters

View File

@@ -9,6 +9,21 @@
*/
package org.eclipse.hawkbit.ddi.rest.resource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.hawkbit.repository.jpa.management.JpaConfirmationManagement.CONFIRMATION_CODE_MSG_PREFIX;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Step;
@@ -49,21 +64,6 @@ import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.ResultActions;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.hawkbit.repository.jpa.management.JpaConfirmationManagement.CONFIRMATION_CODE_MSG_PREFIX;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Test confirmation base from the controller.
*/
@@ -91,7 +91,7 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
final List<Target> targetsAssignedToDs = assignDistributionSet(ds.getId(), savedTarget.getControllerId(),
Action.ActionType.FORCED).getAssignedEntity().stream().map(Action::getTarget)
.collect(Collectors.toList());
.collect(Collectors.toList());
implicitLock(ds);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
@@ -116,10 +116,10 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID)
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.confirmationBase.href",
containsString(expectedConfirmationBaseLink)))
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist());
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.confirmationBase.href",
containsString(expectedConfirmationBaseLink)))
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist());
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
@@ -179,12 +179,12 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
.get(0);
mvc.perform(
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.confirmationBase.href").doesNotExist());
mvc.perform(get(CONFIRMATION_BASE_ACTION, tenantAware.getCurrentTenant(), controllerId, savedAction.getId())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
}
@@ -203,7 +203,7 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
.get(0);
mvc.perform(
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.confirmationBase.href").exists())
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist());
@@ -212,7 +212,7 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), controllerId, savedAction.getId())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
}
@@ -238,14 +238,14 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
// verify confirmation endpoint is still accessible
sendConfirmationFeedback(savedTarget, savedAction, DdiConfirmationFeedback.Confirmation.DENIED, 20,
"Action denied message.").andExpect(status().isOk());
"Action denied message.").andExpect(status().isOk());
// confirmation base should still be exposed
verifyActionInConfirmationBaseState(savedTarget.getControllerId(), savedAction.getId());
// verify confirmation endpoint is still accessible
sendConfirmationFeedback(savedTarget, savedAction, DdiConfirmationFeedback.Confirmation.CONFIRMED, 10,
"Action confirmed message.").andExpect(status().isOk());
"Action confirmed message.").andExpect(status().isOk());
// assert deployment link is exposed to the target
verifyActionInDeploymentBaseState(controllerId, savedAction.getId());
@@ -282,38 +282,6 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
verifyActionInDeploymentBaseState(controllerId, savedAction.getId());
}
@Step
private void verifyActionInDeploymentBaseState(final String controllerId, final long actionId) throws Exception {
final String expectedDeploymentBaseLink = String.format("/%s/controller/v1/%s/deploymentBase/%d",
tenantAware.getCurrentTenant(), controllerId, actionId);
mvc.perform(
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.deploymentBase.href", containsString(expectedDeploymentBaseLink)))
.andExpect(jsonPath("$._links.confirmationBase.href").doesNotExist());
// assert that deployment endpoint is working
mvc.perform(get(expectedDeploymentBaseLink).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
}
@Step
private void verifyActionInConfirmationBaseState(final String controllerId, final long actionId) throws Exception {
mvc.perform(
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.confirmationBase.href").exists())
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist());
mvc.perform(get(CONFIRMATION_BASE_ACTION, tenantAware.getCurrentTenant(), controllerId, actionId)
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), controllerId, actionId)
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
}
@Test
@Description("Confirmation base provides right values if auto-confirm not active.")
void getConfirmationBaseProvidesAutoConfirmStatusNotActive() throws Exception {
@@ -331,7 +299,7 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
tenantAware.getCurrentTenant(), controllerId);
mvc.perform(
get(CONFIRMATION_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
get(CONFIRMATION_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("autoConfirm.active", equalTo(Boolean.FALSE)))
.andExpect(jsonPath("$._links.confirmationBase.href", containsString(confirmationBaseActionLink)))
@@ -353,7 +321,7 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
controllerId);
mvc.perform(
get(CONFIRMATION_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
get(CONFIRMATION_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("autoConfirm.active", equalTo(Boolean.TRUE)))
.andExpect(initiator == null ? jsonPath("autoConfirm.initiator").doesNotExist()
@@ -364,11 +332,6 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
.andExpect(jsonPath("$._links.activateAutoConfirm").doesNotExist());
}
private static Stream<Arguments> possibleActiveStates() {
return Stream.of(Arguments.of("someInitiator", "someRemark"), Arguments.of(null, "someRemark"),
Arguments.of("someInitiator", null), Arguments.of(null, null));
}
@ParameterizedTest
@MethodSource("possibleActiveStates")
@Description("Verify auto-confirm activation is handled correctly.")
@@ -378,7 +341,7 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
final DdiActivateAutoConfirmation body = new DdiActivateAutoConfirmation(initiator, remark);
mvc.perform(post(ACTIVATE_AUTO_CONFIRM, tenantAware.getCurrentTenant(), controllerId)
.content(getMapper().writeValueAsString(body)).contentType(MediaType.APPLICATION_JSON_UTF8))
.content(getMapper().writeValueAsString(body)).contentType(MediaType.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(confirmationManagement.getStatus(controllerId)).hasValueSatisfying(status -> {
@@ -433,29 +396,16 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
tenantAware.getCurrentTenant(), controllerId, savedAction.getId());
mvc.perform(
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist())
.andExpect(jsonPath("$._links.confirmationBase.href", containsString(expectedConfirmationBaseLink)));
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), controllerId, savedAction.getId())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
}
private ResultActions sendConfirmationFeedback(final Target target, final Action action,
final DdiConfirmationFeedback.Confirmation confirmation, Integer code, String message) throws Exception {
if (message == null) {
message = RandomStringUtils.randomAlphanumeric(1000);
}
final String feedback = getJsonConfirmationFeedback(confirmation, code, Collections.singletonList(message));
return mvc.perform(
post(CONFIRMATION_FEEDBACK, tenantAware.getCurrentTenant(), target.getControllerId(), action.getId())
.content(feedback).contentType(MediaType.APPLICATION_JSON));
}
@Test
@Description("Test to verify that only a specific count of messages are returned based on the input actionHistory for getControllerDeploymentActionFeedback endpoint.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@@ -485,18 +435,68 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
// confirmationBase not available in RUNNING state anymore
mvc.perform(get(CONFIRMATION_BASE_ACTION, tenantAware.getCurrentTenant(), savedTarget.getControllerId(),
savedAction.getId()).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
savedAction.getId()).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// assert confirmed message against deploymentBase endpoint
// this call will update the action due to retrieved action status update
mvc.perform(
get(DEPLOYMENT_BASE + "?actionHistory=2", tenantAware.getCurrentTenant(), savedTarget.getControllerId(),
savedAction.getId()).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
get(DEPLOYMENT_BASE + "?actionHistory=2", tenantAware.getCurrentTenant(), savedTarget.getControllerId(),
savedAction.getId()).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages", hasItem(containsString(CONFIRMED_MESSAGE))))
.andExpect(jsonPath("$.actionHistory.messages",
hasItem(containsString(String.format(CONFIRMATION_CODE_MSG_PREFIX, CONFIRMED_CODE)))));
}
private static Stream<Arguments> possibleActiveStates() {
return Stream.of(Arguments.of("someInitiator", "someRemark"), Arguments.of(null, "someRemark"),
Arguments.of("someInitiator", null), Arguments.of(null, null));
}
@Step
private void verifyActionInDeploymentBaseState(final String controllerId, final long actionId) throws Exception {
final String expectedDeploymentBaseLink = String.format("/%s/controller/v1/%s/deploymentBase/%d",
tenantAware.getCurrentTenant(), controllerId, actionId);
mvc.perform(
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.deploymentBase.href", containsString(expectedDeploymentBaseLink)))
.andExpect(jsonPath("$._links.confirmationBase.href").doesNotExist());
// assert that deployment endpoint is working
mvc.perform(get(expectedDeploymentBaseLink).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
}
@Step
private void verifyActionInConfirmationBaseState(final String controllerId, final long actionId) throws Exception {
mvc.perform(
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.confirmationBase.href").exists())
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist());
mvc.perform(get(CONFIRMATION_BASE_ACTION, tenantAware.getCurrentTenant(), controllerId, actionId)
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), controllerId, actionId)
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
}
private ResultActions sendConfirmationFeedback(final Target target, final Action action,
final DdiConfirmationFeedback.Confirmation confirmation, Integer code, String message) throws Exception {
if (message == null) {
message = RandomStringUtils.randomAlphanumeric(1000);
}
final String feedback = getJsonConfirmationFeedback(confirmation, code, Collections.singletonList(message));
return mvc.perform(
post(CONFIRMATION_FEEDBACK, tenantAware.getCurrentTenant(), target.getControllerId(), action.getId())
.content(feedback).contentType(MediaType.APPLICATION_JSON));
}
}

View File

@@ -26,6 +26,10 @@ import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import com.jayway.jsonpath.JsonPath;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.apache.commons.lang3.RandomUtils;
import org.assertj.core.api.Condition;
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
@@ -66,12 +70,6 @@ import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import com.jayway.jsonpath.JsonPath;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
/**
* Test deployment base from the controller.
*/
@@ -79,6 +77,10 @@ import io.qameta.allure.Story;
@Story("Deployment Action Resource")
public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
@Autowired
ActionRepository actionRepository;
@Autowired
ActionStatusRepository actionStatusRepository;
private static final String DEFAULT_CONTROLLER_ID = "4712";
@Test
@@ -130,10 +132,10 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.APPLICATION_JSON, status().isOk(),
tenantAware.getCurrentTenant(), target.getControllerId(), softwareModuleId.toString())
.andExpect(jsonPath("$", hasSize(3)))
.andExpect(jsonPath("$.[?(@.filename=='filename0')]", hasSize(1)))
.andExpect(jsonPath("$.[?(@.filename=='filename1')]", hasSize(1)))
.andExpect(jsonPath("$.[?(@.filename=='filename2')]", hasSize(1)));
.andExpect(jsonPath("$", hasSize(3)))
.andExpect(jsonPath("$.[?(@.filename=='filename0')]", hasSize(1)))
.andExpect(jsonPath("$.[?(@.filename=='filename1')]", hasSize(1)))
.andExpect(jsonPath("$.[?(@.filename=='filename2')]", hasSize(1)));
}
@Test
@@ -171,8 +173,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final long current = System.currentTimeMillis();
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID).andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
@@ -214,7 +216,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
DEFAULT_CONTROLLER_ID).andReturn();
assertThat(JsonPath.compile("_links.deploymentBase.href").read(mvcResult.getResponse().getContentAsString())
.toString()).isEqualTo(urlBeforeSwitch)
.startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, actionId.toString()));
.startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, actionId.toString()));
// After the time is over we should see a new etag
TimeUnit.MILLISECONDS.sleep(2_000);
@@ -269,8 +271,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final long current = System.currentTimeMillis();
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID).andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
@@ -327,8 +329,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final long current = System.currentTimeMillis();
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID).andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
@@ -373,7 +375,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final List<Target> saved = assignDistributionSet(ds.getId(), savedTarget.getControllerId(),
ActionType.DOWNLOAD_ONLY).getAssignedEntity().stream().map(Action::getTarget)
.collect(Collectors.toList());
.collect(Collectors.toList());
implicitLock(ds);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
@@ -394,8 +396,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final long current = System.currentTimeMillis();
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID).andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, uaction.getId().toString()))));
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
@@ -417,17 +419,6 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
}
private void getAndVerifyDeploymentBasePayload(final String controllerId, final MediaType mediaType,
final DistributionSet ds, final String visibleMetadataOsKey, final String visibleMetadataOsValue,
final Artifact artifact, final Artifact artifactSignature, final Long actionId, final String downloadType,
final String updateType, final Long osModuleId) throws Exception {
getAndVerifyDeploymentBasePayload(controllerId, mediaType, ds, artifact, artifactSignature, actionId,
osModuleId, downloadType, updateType).andExpect(
jsonPath("$.deployment.chunks[?(@.part=='os')].metadata[0].key").value(visibleMetadataOsKey))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].metadata[0].value")
.value(visibleMetadataOsValue));
}
@Test
@Description("Test various invalid access attempts to the deployment resource und the expected behaviour of the server.")
public void badDeploymentAction() throws Exception {
@@ -449,7 +440,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
// no deployment
mvc.perform(
MockMvcRequestBuilders.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, "1"))
MockMvcRequestBuilders.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, "1"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// wrong media type
@@ -460,8 +451,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
mvc.perform(MockMvcRequestBuilders.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID,
actionId)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(MockMvcRequestBuilders
.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, actionId)
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, actionId)
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotAcceptable());
}
@@ -552,8 +543,6 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
assertStatusMessagesCount(6);
}
@Autowired
ActionRepository actionRepository;
@Test
@Description("Verifies that an update action is correctly set to error if the controller provides error feedback.")
public void rootRsSingleDeploymentActionWithErrorFeedback() throws Exception {
@@ -669,7 +658,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
// not allowed methods
mvc.perform(MockMvcRequestBuilders.get(DEPLOYMENT_FEEDBACK, tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID, "2")).andDo(MockMvcResultPrinter.print())
DEFAULT_CONTROLLER_ID, "2")).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
mvc.perform(put(DEPLOYMENT_FEEDBACK, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, "2"))
@@ -750,6 +739,21 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
.andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName())));
}
public long countActionStatusAll() {
return actionStatusRepository.count();
}
private void getAndVerifyDeploymentBasePayload(final String controllerId, final MediaType mediaType,
final DistributionSet ds, final String visibleMetadataOsKey, final String visibleMetadataOsValue,
final Artifact artifact, final Artifact artifactSignature, final Long actionId, final String downloadType,
final String updateType, final Long osModuleId) throws Exception {
getAndVerifyDeploymentBasePayload(controllerId, mediaType, ds, artifact, artifactSignature, actionId,
osModuleId, downloadType, updateType).andExpect(
jsonPath("$.deployment.chunks[?(@.part=='os')].metadata[0].key").value(visibleMetadataOsKey))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].metadata[0].value")
.value(visibleMetadataOsValue));
}
private void assertActionStatusCount(final int actionStatusCount, final int minActionStatusCountInPage) {
final Target target = targetManagement.getByControllerID(DdiDeploymentBaseTest.DEFAULT_CONTROLLER_ID).get();
assertThat(target.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
@@ -762,19 +766,6 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
new ActionStatusCondition(Status.RUNNING));
}
private static class ActionStatusCondition extends Condition<ActionStatus> {
private final Action.Status status;
public ActionStatusCondition(final Action.Status status) {
this.status = status;
}
@Override
public boolean matches(final ActionStatus value) {
return value.getStatus() == status;
}
}
private Target createTargetAndAssertNoActiveActions() {
final Target savedTarget = testdataFactory.createTarget(DdiDeploymentBaseTest.DEFAULT_CONTROLLER_ID);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).isEmpty();
@@ -829,12 +820,21 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
.hasSize(activeActions);
}
@Autowired
ActionStatusRepository actionStatusRepository;
private Page<ActionStatus> findActionStatusAll(final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(actionStatusRepository, pageable, null);
}
public long countActionStatusAll() {
return actionStatusRepository.count();
private static class ActionStatusCondition extends Condition<ActionStatus> {
private final Action.Status status;
public ActionStatusCondition(final Action.Status status) {
this.status = status;
}
@Override
public boolean matches(final ActionStatus value) {
return value.getStatus() == status;
}
}
}

View File

@@ -27,6 +27,9 @@ import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.apache.commons.lang3.RandomUtils;
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
@@ -62,10 +65,6 @@ import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
/**
* Test installed base from the controller.
*/
@@ -73,6 +72,8 @@ import io.qameta.allure.Story;
@Story("Installed Base Resource")
public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
@Autowired
ActionStatusRepository actionStatusRepository;
private static final int ARTIFACT_SIZE = 5 * 1024;
private static final String CONTROLLER_ID = "4715";
@@ -105,17 +106,17 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
final Target target = createTargetAndAssertNoActiveActions();
final DistributionSet ds = testdataFactory.createDistributionSet("");
// update assigned version
putInstalledBase(target.getControllerId(),getJsonInstalledBase(ds.getName(),ds.getVersion()),status()
putInstalledBase(target.getControllerId(), getJsonInstalledBase(ds.getName(), ds.getVersion()), status()
.isCreated());
assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get().getId())
.isEqualTo(ds.getId());
// update assigned version while version already assigned
putInstalledBase(target.getControllerId(),getJsonInstalledBase(ds.getName(),ds.getVersion()),status().isOk());
putInstalledBase(target.getControllerId(), getJsonInstalledBase(ds.getName(), ds.getVersion()), status().isOk());
}
@Test
@Description("Ensure that installedVersion is version self assigned")
public void installedVersionNotExist() throws Exception {
@@ -123,9 +124,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
final String dsName = "unknown";
final String dsVersion = "1.0.0";
// get installed base
putInstalledBase(target.getControllerId(),getJsonInstalledBase(dsName,dsVersion),status().isNotFound());
putInstalledBase(target.getControllerId(), getJsonInstalledBase(dsName, dsVersion), status().isNotFound());
assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).isEmpty()).isTrue();
}
@@ -401,14 +401,10 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.APPLICATION_JSON, status().isOk(),
tenantAware.getCurrentTenant(), target.getControllerId(), softwareModuleId.toString())
.andExpect(jsonPath("$", hasSize(3)))
.andExpect(jsonPath("$.[?(@.filename=='filename0')]", hasSize(1)))
.andExpect(jsonPath("$.[?(@.filename=='filename1')]", hasSize(1)))
.andExpect(jsonPath("$.[?(@.filename=='filename2')]", hasSize(1)));
}
private static Stream<Action.ActionType> actionTypeForDeployment() {
return Stream.of(Action.ActionType.SOFT, Action.ActionType.FORCED);
.andExpect(jsonPath("$", hasSize(3)))
.andExpect(jsonPath("$.[?(@.filename=='filename0')]", hasSize(1)))
.andExpect(jsonPath("$.[?(@.filename=='filename1')]", hasSize(1)))
.andExpect(jsonPath("$.[?(@.filename=='filename2')]", hasSize(1)));
}
@ParameterizedTest
@@ -484,8 +480,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
performGet(CONTROLLER_BASE, MediaType.APPLICATION_JSON, status().isOk(), tenantAware.getCurrentTenant(),
target.getControllerId()).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.deploymentBase.href").exists())
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
.andExpect(jsonPath("$._links.deploymentBase.href").exists())
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
postDeploymentFeedback(target.getControllerId(), actionId, getJsonDownloadDeploymentActionFeedback(),
status().isOk());
@@ -495,8 +491,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
// Test
performGet(CONTROLLER_BASE, MediaType.APPLICATION_JSON, status().isOk(), tenantAware.getCurrentTenant(),
target.getControllerId()).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist())
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist())
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
}
@ParameterizedTest
@@ -511,8 +507,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
performGet(CONTROLLER_BASE, MediaType.APPLICATION_JSON, status().isOk(), tenantAware.getCurrentTenant(),
target.getControllerId()).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.deploymentBase.href").exists())
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
.andExpect(jsonPath("$._links.deploymentBase.href").exists())
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
postDeploymentFeedback(target.getControllerId(), actionId,
getJsonActionFeedback(DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE),
@@ -525,8 +521,8 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
// Test
performGet(CONTROLLER_BASE, MediaType.APPLICATION_JSON, status().isOk(), tenantAware.getCurrentTenant(),
target.getControllerId()).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist())
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist())
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist());
}
@Test
@@ -556,14 +552,14 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
// Test
// for zero input no action history is returned
mvc.perform(get(INSTALLED_BASE + "?actionHistory", tenantAware.getCurrentTenant(), 911, savedAction.getId())
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages").doesNotExist());
// depending on given query parameter value, only the latest messages are
// returned
mvc.perform(get(INSTALLED_BASE + "?actionHistory=2", tenantAware.getCurrentTenant(), 911, savedAction.getId())
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages", hasItem(containsString("Installation completed"))))
.andExpect(jsonPath("$.actionHistory.messages", hasItem(containsString("Installation proceeding"))))
@@ -572,7 +568,7 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
// for negative input the entire action history is returned
mvc.perform(get(INSTALLED_BASE + "?actionHistory=-3", tenantAware.getCurrentTenant(), 911, savedAction.getId())
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages", hasItem(containsString("Installation completed"))))
.andExpect(jsonPath("$.actionHistory.messages", hasItem(containsString("Installation proceeding"))))
@@ -611,12 +607,14 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
mvc.perform(MockMvcRequestBuilders.get(INSTALLED_BASE, tenantAware.getCurrentTenant(), CONTROLLER_ID, actionId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(MockMvcRequestBuilders.get(INSTALLED_BASE, tenantAware.getCurrentTenant(), CONTROLLER_ID, actionId)
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotAcceptable());
}
@Autowired
ActionStatusRepository actionStatusRepository;
private static Stream<Action.ActionType> actionTypeForDeployment() {
return Stream.of(Action.ActionType.SOFT, Action.ActionType.FORCED);
}
private Target createTargetAndAssertNoActiveActions() {
final Target savedTarget = testdataFactory.createTarget(CONTROLLER_ID);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).isEmpty();

View File

@@ -33,6 +33,10 @@ import java.util.Collections;
import java.util.Map;
import java.util.UUID;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Step;
import io.qameta.allure.Story;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.ddi.json.model.DdiResult;
import org.eclipse.hawkbit.ddi.json.model.DdiStatus;
@@ -72,11 +76,6 @@ import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Step;
import io.qameta.allure.Story;
/**
* Test the root controller resources.
*/
@@ -270,7 +269,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
assertThat(etagWithFirstUpdate).isNotNull();
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).header("If-None-Match",
etagWithFirstUpdate).with(new RequestOnHawkbitDefaultPortPostProcessor()))
etagWithFirstUpdate).with(new RequestOnHawkbitDefaultPortPostProcessor()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotModified());
// now lets finish the update
@@ -300,8 +299,8 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
.getContent().get(0);
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId)
.header("If-None-Match", etagAfterInstallation).accept(MediaType.APPLICATION_JSON)
.with(new RequestOnHawkbitDefaultPortPostProcessor()))
.header("If-None-Match", etagAfterInstallation).accept(MediaType.APPLICATION_JSON)
.with(new RequestOnHawkbitDefaultPortPostProcessor()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
@@ -430,7 +429,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
assertThatAttributesUpdateIsRequested(savedTarget.getControllerId());
mvc.perform(put(CONTROLLER_BASE + "/configData", tenantAware.getCurrentTenant(), savedTarget.getControllerId())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
assertThatAttributesUpdateIsNotRequested(savedTarget.getControllerId());
@@ -439,59 +438,6 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
assertAttributesUpdateRequestedAfterSuccessfulDeployment(savedTarget, ds);
}
@Step
private void assertAttributesUpdateNotRequestedAfterFailedDeployment(Target target, final DistributionSet ds)
throws Exception {
target = getFirstAssignedTarget(assignDistributionSet(ds.getId(), target.getControllerId()));
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
.getContent().get(0);
sendDeploymentActionFeedback(target, action, "closed", "failure").andExpect(status().isOk());
assertThatAttributesUpdateIsNotRequested(target.getControllerId());
}
@Step
private void assertAttributesUpdateRequestedAfterSuccessfulDeployment(Target target, final DistributionSet ds)
throws Exception {
target = getFirstAssignedTarget(assignDistributionSet(ds.getId(), target.getControllerId()));
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
.getContent().get(0);
sendDeploymentActionFeedback(target, action, "closed", null).andExpect(status().isOk());
assertThatAttributesUpdateIsRequested(target.getControllerId());
}
private void assertThatAttributesUpdateIsRequested(final String targetControllerId) throws Exception {
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), targetControllerId)
.accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andExpect(jsonPath("$._links.configData.href").isNotEmpty());
}
private void assertThatAttributesUpdateIsNotRequested(final String targetControllerId) throws Exception {
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), targetControllerId)
.accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andExpect(jsonPath("$._links.configData").doesNotExist());
}
private ResultActions sendDeploymentActionFeedback(final Target target, final Action action, final String execution,
String finished, String message) throws Exception {
if (finished == null) {
finished = "none";
}
if (message == null) {
message = RandomStringUtils.randomAlphanumeric(1000);
}
final String feedback = getJsonActionFeedback(DdiStatus.ExecutionStatus.valueOf(execution.toUpperCase()),
DdiResult.FinalResult.valueOf(finished.toUpperCase()), Collections.singletonList(message));
return mvc.perform(
post(DEPLOYMENT_FEEDBACK, tenantAware.getCurrentTenant(), target.getControllerId(), action.getId())
.content(feedback).contentType(MediaType.APPLICATION_JSON));
}
private ResultActions sendDeploymentActionFeedback(final Target target, final Action action, final String execution,
final String finished) throws Exception {
return sendDeploymentActionFeedback(target, action, execution, finished, null);
}
@Test
@Description("Test to verify that only a specific count of messages are returned based on the input actionHistory for getControllerDeploymentActionFeedback endpoint.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@@ -520,7 +466,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get(DEPLOYMENT_BASE + "?actionHistory=2", tenantAware.getCurrentTenant(), 911, savedAction.getId())
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages",
hasItem(containsString(TARGET_COMPLETED_INSTALLATION_MSG))))
@@ -558,12 +504,12 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get(DEPLOYMENT_BASE + "?actionHistory=0", tenantAware.getCurrentTenant(), 911, savedAction.getId())
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages").doesNotExist());
mvc.perform(get(DEPLOYMENT_BASE + "?actionHistory", tenantAware.getCurrentTenant(), 911, savedAction.getId())
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages").doesNotExist());
}
@@ -596,7 +542,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get(DEPLOYMENT_BASE + "?actionHistory=-1", tenantAware.getCurrentTenant(), 911, savedAction.getId())
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages",
hasItem(containsString(TARGET_SCHEDULED_INSTALLATION_MSG))))
@@ -668,7 +614,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
.getContent().get(0);
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), "1911", action.getId())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.deployment.download", equalTo("forced")))
.andExpect(jsonPath("$.deployment.update", equalTo("skip")))
.andExpect(jsonPath("$.deployment.maintenanceWindow", equalTo("unavailable")));
@@ -688,7 +634,7 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
.getContent().get(0);
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), "1911", action.getId())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.deployment.download", equalTo("forced")))
.andExpect(jsonPath("$.deployment.update", equalTo("forced")))
.andExpect(jsonPath("$.deployment.maintenanceWindow", equalTo("available")));
@@ -719,12 +665,65 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
.andExpect(status().isBadRequest());
}
@Step
private void assertAttributesUpdateNotRequestedAfterFailedDeployment(Target target, final DistributionSet ds)
throws Exception {
target = getFirstAssignedTarget(assignDistributionSet(ds.getId(), target.getControllerId()));
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
.getContent().get(0);
sendDeploymentActionFeedback(target, action, "closed", "failure").andExpect(status().isOk());
assertThatAttributesUpdateIsNotRequested(target.getControllerId());
}
@Step
private void assertAttributesUpdateRequestedAfterSuccessfulDeployment(Target target, final DistributionSet ds)
throws Exception {
target = getFirstAssignedTarget(assignDistributionSet(ds.getId(), target.getControllerId()));
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
.getContent().get(0);
sendDeploymentActionFeedback(target, action, "closed", null).andExpect(status().isOk());
assertThatAttributesUpdateIsRequested(target.getControllerId());
}
private void assertThatAttributesUpdateIsRequested(final String targetControllerId) throws Exception {
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), targetControllerId)
.accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andExpect(jsonPath("$._links.configData.href").isNotEmpty());
}
private void assertThatAttributesUpdateIsNotRequested(final String targetControllerId) throws Exception {
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), targetControllerId)
.accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andExpect(jsonPath("$._links.configData").doesNotExist());
}
private ResultActions sendDeploymentActionFeedback(final Target target, final Action action, final String execution,
String finished, String message) throws Exception {
if (finished == null) {
finished = "none";
}
if (message == null) {
message = RandomStringUtils.randomAlphanumeric(1000);
}
final String feedback = getJsonActionFeedback(DdiStatus.ExecutionStatus.valueOf(execution.toUpperCase()),
DdiResult.FinalResult.valueOf(finished.toUpperCase()), Collections.singletonList(message));
return mvc.perform(
post(DEPLOYMENT_FEEDBACK, tenantAware.getCurrentTenant(), target.getControllerId(), action.getId())
.content(feedback).contentType(MediaType.APPLICATION_JSON));
}
private ResultActions sendDeploymentActionFeedback(final Target target, final Action action, final String execution,
final String finished) throws Exception {
return sendDeploymentActionFeedback(target, action, execution, finished, null);
}
private void assertDeploymentActionIsExposedToTarget(final String controllerId, final long expectedActionId)
throws Exception {
final String expectedDeploymentBaseLink = String.format("/%s/controller/v1/%s/deploymentBase/%d",
tenantAware.getCurrentTenant(), controllerId, expectedActionId);
mvc.perform(
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.deploymentBase.href", containsString(expectedDeploymentBaseLink)));

View File

@@ -17,6 +17,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import java.util.Collections;
import java.util.List;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
@@ -30,13 +33,8 @@ import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.web.context.WebApplicationContext;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
/**
* Test potential DOS attack scenarios and check if the filter prevents them.
*
*/
@ActiveProfiles({ "test" })
@Feature("Component Tests - REST Security")
@@ -100,7 +98,8 @@ class DosFilterTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that a relatively high number of READ requests is allowed if it is below the DoS detection threshold")
@SuppressWarnings("squid:S2925") // No idea how to get rid of the Thread.sleep here
@SuppressWarnings("squid:S2925")
// No idea how to get rid of the Thread.sleep here
void acceptableGetLoad() throws Exception {
for (int x = 0; x < 3; x++) {
@@ -123,7 +122,7 @@ class DosFilterTest extends AbstractDDiApiIntegrationTest {
int requests = 0;
do {
result = mvc.perform(post("/{tenant}/controller/v1/4711/deploymentBase/" + actionId + "/feedback",
tenantAware.getCurrentTenant()).header(X_FORWARDED_FOR, "10.0.0.1").content(feedback)
tenantAware.getCurrentTenant()).header(X_FORWARDED_FOR, "10.0.0.1").content(feedback)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andReturn();
requests++;
@@ -139,7 +138,8 @@ class DosFilterTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that a relatively high number of WRITE requests is allowed if it is below the DoS detection threshold")
@SuppressWarnings("squid:S2925") // No idea how to get rid of the Thread.sleep here
@SuppressWarnings("squid:S2925")
// No idea how to get rid of the Thread.sleep here
void acceptablePutPostLoad() throws Exception {
final Long actionId = prepareDeploymentBase();
final String feedback = getJsonProceedingDeploymentActionFeedback();
@@ -150,7 +150,7 @@ class DosFilterTest extends AbstractDDiApiIntegrationTest {
for (int i = 0; i < 9; i++) {
mvc.perform(post("/{tenant}/controller/v1/4711/deploymentBase/" + actionId + "/feedback",
tenantAware.getCurrentTenant()).header(X_FORWARDED_FOR, "10.0.0.1")
tenantAware.getCurrentTenant()).header(X_FORWARDED_FOR, "10.0.0.1")
.content(feedback).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());