Code format hawkbit-ddi-resource (#1941)
Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# 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
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
|
||||
-->
|
||||
|
||||
<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">
|
||||
<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>
|
||||
|
||||
@@ -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();
|
||||
@@ -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) {
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.springframework.stereotype.Controller;
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@Import({RestConfiguration.class, OpenApiConfiguration.class})
|
||||
@Import({ RestConfiguration.class, OpenApiConfiguration.class })
|
||||
public class DdiApiConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -216,28 +216,6 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
@@ -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"));
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
@@ -245,16 +247,6 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -533,8 +525,16 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
|
||||
.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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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 {
|
||||
@@ -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.")
|
||||
@@ -443,19 +406,6 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
.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),
|
||||
@@ -499,4 +449,54 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -407,10 +407,6 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
.andExpect(jsonPath("$.[?(@.filename=='filename2')]", hasSize(1)));
|
||||
}
|
||||
|
||||
private static Stream<Action.ActionType> actionTypeForDeployment() {
|
||||
return Stream.of(Action.ActionType.SOFT, Action.ActionType.FORCED);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("org.eclipse.hawkbit.ddi.rest.resource.DdiInstalledBaseTest#actionTypeForDeployment")
|
||||
@Description("Test forced deployment to a controller. Checks that action is represented as installedBase after installation.")
|
||||
@@ -615,8 +611,10 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
|
||||
.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();
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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),
|
||||
@@ -719,6 +665,59 @@ 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",
|
||||
|
||||
@@ -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++) {
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user