DDI API: /controller/v1/{controllerid}/installedBase/{actionId} (#1220)

* installedBase DDI: initial impl + rest docs

Signed-off-by: Natalia Kislicyn <natalia.kislicyn@bosch.io>

* refactor installedBase implementation

Signed-off-by: Natalia Kislicyn <natalia.kislicyn@bosch.io>

* ddi installed base tests

Signed-off-by: Natalia Kislicyn <natalia.kislicyn@bosch.io>

* restructure root controller rest docs

Signed-off-by: Natalia Kislicyn <natalia.kislicyn@bosch.io>

* fix some java docs and formatting

Signed-off-by: Natalia Kislicyn <natalia.kislicyn@bosch.io>

* don't add action history message when calling /installedBase

Signed-off-by: Natalia Kislicyn <natalia.kislicyn@bosch.io>

* fix test after removing action history message

Signed-off-by: Natalia Kislicyn <natalia.kislicyn@bosch.io>

* fix review comments

Signed-off-by: Natalia Kislicyn <natalia.kislicyn@bosch.io>

* fix review comments

Signed-off-by: Natalia Kislicyn <natalia.kislicyn@bosch.io>
This commit is contained in:
Natalia Kislicyn
2022-01-24 18:13:03 +01:00
committed by GitHub
parent 7a1905cf5b
commit 69e6488004
15 changed files with 1077 additions and 332 deletions

View File

@@ -108,17 +108,17 @@ public final class DataConversionHelper {
}
static DdiControllerBase fromTarget(final Target target, final Action action,
final String defaultControllerPollTime, final TenantAware tenantAware) {
public static DdiControllerBase fromTarget(final Target target, final Action installedAction,
final Action activeAction, final String defaultControllerPollTime, final TenantAware tenantAware) {
final DdiControllerBase result = new DdiControllerBase(
new DdiConfig(new DdiPolling(defaultControllerPollTime)));
if (action != null) {
if (action.isCancelingOrCanceled()) {
if (activeAction != null) {
if (activeAction.isCancelingOrCanceled()) {
result.add(WebMvcLinkBuilder
.linkTo(WebMvcLinkBuilder.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.getControllerCancelAction(tenantAware.getCurrentTenant(), target.getControllerId(),
action.getId()))
activeAction.getId()))
.withRel(DdiRestConstants.CANCEL_ACTION));
} else {
// we need to add the hashcode here of the actionWithStatus
@@ -129,11 +129,20 @@ public final class DataConversionHelper {
result.add(WebMvcLinkBuilder
.linkTo(WebMvcLinkBuilder.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.getControllerBasedeploymentAction(tenantAware.getCurrentTenant(),
target.getControllerId(), action.getId(), calculateEtag(action), null))
target.getControllerId(), activeAction.getId(), calculateEtag(activeAction), null))
.withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION));
}
}
if (installedAction != null && !installedAction.isActive()) {
result.add(
WebMvcLinkBuilder
.linkTo(WebMvcLinkBuilder.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.getControllerInstalledAction(tenantAware.getCurrentTenant(),
target.getControllerId(), installedAction.getId(), null))
.withRel(DdiRestConstants.INSTALLED_BASE_ACTION));
}
if (target.isRequestControllerAttributes()) {
result.add(WebMvcLinkBuilder
.linkTo(WebMvcLinkBuilder.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())

View File

@@ -130,8 +130,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
@PathVariable("softwareModuleId") final Long softwareModuleId) {
LOG.debug("getSoftwareModulesArtifacts({})", controllerId);
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final Target target = findTargetWithExceptionIfNotFound(controllerId);
final SoftwareModule softwareModule = controllerManagement.getSoftwareModule(softwareModuleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
@@ -149,14 +148,16 @@ public class DdiRootController implements DdiRootControllerRestApi {
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, IpUtil
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
final Action action = controllerManagement.findActiveActionWithHighestWeight(controllerId).orElse(null);
final Action activeAction = controllerManagement.findActiveActionWithHighestWeight(controllerId).orElse(null);
checkAndCancelExpiredAction(action);
final Action installedAction = controllerManagement.getInstalledActionByTarget(controllerId).orElse(null);
checkAndCancelExpiredAction(activeAction);
return new ResponseEntity<>(
DataConversionHelper.fromTarget(target, action,
action == null ? controllerManagement.getPollingTime()
: controllerManagement.getPollingTimeForAction(action.getId()),
DataConversionHelper.fromTarget(target, installedAction, activeAction,
activeAction == null ? controllerManagement.getPollingTime()
: controllerManagement.getPollingTimeForAction(activeAction.getId()),
tenantAware),
HttpStatus.OK);
}
@@ -168,8 +169,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
@PathVariable("fileName") final String fileName) {
final ResponseEntity<InputStream> result;
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final Target target = findTargetWithExceptionIfNotFound(controllerId);
final SoftwareModule module = controllerManagement.getSoftwareModule(softwareModuleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
@@ -239,8 +239,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
@PathVariable("controllerId") final String controllerId,
@PathVariable("softwareModuleId") final Long softwareModuleId,
@PathVariable("fileName") final String fileName) {
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final Target target = findTargetWithExceptionIfNotFound(controllerId);
final SoftwareModule module = controllerManagement.getSoftwareModule(softwareModuleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
@@ -275,38 +274,15 @@ public class DdiRootController implements DdiRootControllerRestApi {
@RequestParam(value = "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount) {
LOG.debug("getControllerBasedeploymentAction({},{})", controllerId, resource);
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final Target target = findTargetWithExceptionIfNotFound(controllerId);
final Action action = findActionWithExceptionIfNotFound(actionId);
if (!action.getTarget().getId().equals(target.getId())) {
LOG.warn(GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET, action.getId(), target.getId());
return ResponseEntity.notFound().build();
}
verifyActionAssignedToTarget(target, action);
checkAndCancelExpiredAction(action);
if (!action.isCancelingOrCanceled()) {
final List<DdiChunk> chunks = DataConversionHelper.createChunks(target, action, artifactUrlHandler,
systemManagement,
new ServletServerHttpRequest(requestResponseContextHolder.getHttpServletRequest()),
controllerManagement);
final List<String> actionHistoryMsgs = controllerManagement.getActionHistoryMessages(action.getId(),
actionHistoryMessageCount == null ? Integer.parseInt(DdiRestConstants.NO_ACTION_HISTORY)
: actionHistoryMessageCount);
final DdiActionHistory actionHistory = actionHistoryMsgs.isEmpty() ? null
: new DdiActionHistory(action.getStatus().name(), actionHistoryMsgs);
final HandlingType downloadType = calculateDownloadType(action);
final HandlingType updateType = calculateUpdateType(action, downloadType);
final DdiMaintenanceWindowStatus maintenanceWindow = calculateMaintenanceWindow(action);
final DdiDeploymentBase base = new DdiDeploymentBase(Long.toString(action.getId()),
new DdiDeployment(downloadType, updateType, chunks, maintenanceWindow), actionHistory);
final DdiDeploymentBase base = generateDdiDeploymentBase(target, action, actionHistoryMessageCount);
LOG.debug("Found an active UpdateAction for target {}. returning deployment: {}", controllerId, base);
@@ -349,14 +325,9 @@ public class DdiRootController implements DdiRootControllerRestApi {
@PathVariable("actionId") @NotEmpty final Long actionId) {
LOG.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", controllerId, actionId, feedback);
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final Target target = findTargetWithExceptionIfNotFound(controllerId);
final Action action = findActionWithExceptionIfNotFound(actionId);
if (!action.getTarget().getId().equals(target.getId())) {
LOG.warn(GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET, action.getId(), target.getId());
return ResponseEntity.notFound().build();
}
verifyActionAssignedToTarget(target, action);
if (!action.isActive()) {
LOG.warn("Updating action {} with feedback {} not possible since action not active anymore.",
@@ -461,14 +432,9 @@ public class DdiRootController implements DdiRootControllerRestApi {
@PathVariable("actionId") @NotEmpty final Long actionId) {
LOG.debug("getControllerCancelAction({})", controllerId);
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final Target target = findTargetWithExceptionIfNotFound(controllerId);
final Action action = findActionWithExceptionIfNotFound(actionId);
if (!action.getTarget().getId().equals(target.getId())) {
LOG.warn(GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET, action.getId(), target.getId());
return ResponseEntity.notFound().build();
}
verifyActionAssignedToTarget(target, action);
if (action.isCancelingOrCanceled()) {
final DdiCancel cancel = new DdiCancel(String.valueOf(action.getId()),
@@ -492,20 +458,58 @@ public class DdiRootController implements DdiRootControllerRestApi {
@PathVariable("actionId") @NotEmpty final Long actionId) {
LOG.debug("provideCancelActionFeedback for target [{}]: {}", controllerId, feedback);
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final Target target = findTargetWithExceptionIfNotFound(controllerId);
final Action action = findActionWithExceptionIfNotFound(actionId);
if (!action.getTarget().getId().equals(target.getId())) {
LOG.warn(GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET, action.getId(), target.getId());
return ResponseEntity.notFound().build();
}
verifyActionAssignedToTarget(target, action);
controllerManagement
.addCancelActionStatus(generateActionCancelStatus(feedback, target, actionId, entityFactory));
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<DdiDeploymentBase> getControllerInstalledAction(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") final String controllerId, @PathVariable("actionId") final Long actionId,
@RequestParam(value = "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount) {
LOG.debug("getControllerInstalledAction({})", controllerId);
final Target target = findTargetWithExceptionIfNotFound(controllerId);
final Action action = findActionWithExceptionIfNotFound(actionId);
verifyActionAssignedToTarget(target, action);
if (action.isActive() || action.isCancelingOrCanceled()) {
return ResponseEntity.notFound().build();
}
final DdiDeploymentBase base = generateDdiDeploymentBase(target, action, actionHistoryMessageCount);
LOG.debug("Found an installed UpdateAction for target {}. returning deployment: {}", controllerId, base);
return new ResponseEntity<>(base, HttpStatus.OK);
}
private DdiDeploymentBase generateDdiDeploymentBase(Target target, Action action,
Integer actionHistoryMessageCount) {
final List<DdiChunk> chunks = DataConversionHelper.createChunks(target, action, artifactUrlHandler,
systemManagement, new ServletServerHttpRequest(requestResponseContextHolder.getHttpServletRequest()),
controllerManagement);
final List<String> actionHistoryMsgs = controllerManagement.getActionHistoryMessages(action.getId(),
actionHistoryMessageCount == null ? Integer.parseInt(DdiRestConstants.NO_ACTION_HISTORY)
: actionHistoryMessageCount);
final DdiActionHistory actionHistory = actionHistoryMsgs.isEmpty() ? null
: new DdiActionHistory(action.getStatus().name(), actionHistoryMsgs);
final HandlingType downloadType = calculateDownloadType(action);
final HandlingType updateType = calculateUpdateType(action, downloadType);
final DdiMaintenanceWindowStatus maintenanceWindow = calculateMaintenanceWindow(action);
return new DdiDeploymentBase(Long.toString(action.getId()),
new DdiDeployment(downloadType, updateType, chunks, maintenanceWindow), actionHistory);
}
private static ActionStatusCreate generateActionCancelStatus(final DdiActionFeedback feedback, final Target target,
final Long actionid, final EntityFactory entityFactory) {
@@ -561,11 +565,23 @@ public class DdiRootController implements DdiRootControllerRestApi {
return status;
}
private Target findTargetWithExceptionIfNotFound(final String controllerId) {
return controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
}
private Action findActionWithExceptionIfNotFound(final Long actionId) {
return controllerManagement.findActionWithDetails(actionId)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
}
private void verifyActionAssignedToTarget(final Target target, final Action action) {
if (!action.getTarget().getId().equals(target.getId())) {
LOG.warn(GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET, action.getId(), target.getId());
throw new EntityNotFoundException(Action.class, action.getId());
}
}
/**
* If the action has a maintenance schedule defined but is no longer valid,
* cancel the action.

View File

@@ -8,17 +8,32 @@
*/
package org.eclipse.hawkbit.ddi.rest.resource;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.contains;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.test.TestConfiguration;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.eclipse.hawkbit.rest.RestConfiguration;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
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;
@@ -32,6 +47,19 @@ import com.fasterxml.jackson.dataformat.cbor.CBORParser;
@TestPropertySource(locations = "classpath:/ddi-test.properties")
public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrationTest {
protected static final String HTTP_LOCALHOST = "http://localhost:8080/";
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 CANCEL_ACTION = CONTROLLER_BASE + "/cancelAction/{actionId}";
protected static final String INSTALLED_BASE = CONTROLLER_BASE + "/installedBase/{actionId}";
protected static final String DEPLOYMENT_FEEDBACK = DEPLOYMENT_BASE + "/feedback";
protected static final String CANCEL_FEEDBACK = CANCEL_ACTION + "/feedback";
protected static final int ARTIFACT_SIZE = 5 * 1024;
/**
* Convert JSON to a CBOR equivalent.
*
@@ -75,4 +103,121 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
jsonGenerator.flush();
return stringWriter.toString();
}
protected ResultActions postDeploymentFeedback(final String controllerId, final Long actionId, final String content,
final ResultMatcher statusMatcher) throws Exception {
return postDeploymentFeedback(MediaType.APPLICATION_JSON, controllerId, actionId, content.getBytes(),
statusMatcher);
}
protected ResultActions postDeploymentFeedback(final MediaType mediaType, final String controllerId,
final Long actionId, final byte[] content, final ResultMatcher statusMatcher) throws Exception {
return mvc
.perform(post(DEPLOYMENT_FEEDBACK, tenantAware.getCurrentTenant(), controllerId, actionId)
.content(content).contentType(mediaType).accept(mediaType))
.andDo(MockMvcResultPrinter.print()).andExpect(statusMatcher);
}
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))
.andDo(MockMvcResultPrinter.print()).andExpect(statusMatcher)
.andExpect(content().contentTypeCompatibleWith(mediaType));
}
protected ResultActions getAndVerifyDeploymentBasePayload(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 {
final ResultActions resultActions = performGet(DEPLOYMENT_BASE, mediaType, status().isOk(),
tenantAware.getCurrentTenant(), controllerId, actionId.toString());
return verifyBasePayload(resultActions, controllerId, ds, artifact, artifactSignature, actionId, osModuleId,
downloadType, updateType);
}
protected ResultActions getAndVerifyDeploymentBasePayload(final String controllerId, final MediaType mediaType,
final DistributionSet ds, final Artifact artifact, final Artifact artifactSignature, final Long actionId,
final Long osModuleId, final Action.ActionType actionType) throws Exception {
return getAndVerifyDeploymentBasePayload(controllerId, mediaType, ds, artifact, artifactSignature, actionId,
osModuleId, getDownloadAndUploadType(actionType), getDownloadAndUploadType(actionType));
}
protected ResultActions getAndVerifyInstalledBasePayload(final String controllerId, final MediaType mediaType,
final DistributionSet ds, final Artifact artifact, final Artifact artifactSignature, final Long actionId,
final Long osModuleId, final Action.ActionType actionType) throws Exception {
final ResultActions resultActions = performGet(INSTALLED_BASE, mediaType, status().isOk(),
tenantAware.getCurrentTenant(), controllerId, actionId.toString());
return verifyBasePayload(resultActions, controllerId, ds, artifact, artifactSignature, actionId, osModuleId,
getDownloadAndUploadType(actionType), getDownloadAndUploadType(actionType));
}
private ResultActions verifyBasePayload(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("$.deployment.download", equalTo(downloadType)))
.andExpect(jsonPath("$.deployment.update", equalTo(updateType)))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='jvm')].name",
contains(ds.findFirstModuleByType(runtimeType).get().getName())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='jvm')].version",
contains(ds.findFirstModuleByType(runtimeType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].name",
contains(ds.findFirstModuleByType(osType).get().getName())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].version",
contains(ds.findFirstModuleByType(osType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0].size", contains(ARTIFACT_SIZE)))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0].filename",
contains(artifact.getFilename())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0].hashes.md5",
contains(artifact.getMd5Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0].hashes.sha1",
contains(artifact.getSha1Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0].hashes.sha256",
contains(artifact.getSha256Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0]._links.download-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifact.getFilename())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0]._links.md5sum-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifact.getFilename()
+ ".MD5SUM")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1].size", contains(ARTIFACT_SIZE)))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1].filename",
contains(artifactSignature.getFilename())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1].hashes.md5",
contains(artifactSignature.getMd5Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1].hashes.sha1",
contains(artifactSignature.getSha1Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1].hashes.sha256",
contains(artifactSignature.getSha256Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1]._links.download-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifactSignature.getFilename())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1]._links.md5sum-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + osModuleId + "/artifacts/" + artifactSignature.getFilename()
+ ".MD5SUM")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='bApp')].version",
contains(ds.findFirstModuleByType(appType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='bApp')].metadata").doesNotExist())
.andExpect(jsonPath("$.deployment.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;
}
protected String deploymentBaseLink(final String controllerId, final String actionId) {
return "http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/deploymentBase/" + actionId;
}
private static String getDownloadAndUploadType(final Action.ActionType actionType) {
if (Action.ActionType.FORCED.equals(actionType)) {
return "forced";
}
return "attempt";
}
}

View File

@@ -10,13 +10,11 @@ package org.eclipse.hawkbit.ddi.rest.resource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.startsWith;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -55,8 +53,6 @@ import org.springframework.data.domain.Sort.Direction;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import com.jayway.jsonpath.JsonPath;
@@ -72,12 +68,6 @@ import io.qameta.allure.Story;
@Story("Deployment Action Resource")
public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
private static final String HTTP_LOCALHOST = "http://localhost:8080/";
private static final String CONTROLLER_BY_ID = "/{tenant}/controller/v1/{controllerId}";
private static final String SOFTWARE_MODULE_ARTIFACTS = CONTROLLER_BY_ID
+ "/softwaremodules/{softwareModuleId}/artifacts";
private static final String DEPLOYMENT_BASE = CONTROLLER_BY_ID + "/deploymentBase/";
private static final int ARTIFACT_SIZE = 5 * 1024;
private static final String DEFAULT_CONTROLLER_ID = "4712";
@Test
@@ -91,9 +81,9 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
.getContent().get(0);
// get deployment base
performGet(DEPLOYMENT_BASE + action.getId().toString(),
performGet(DEPLOYMENT_BASE,
MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), status().isOk(),
tenantAware.getCurrentTenant(), target.getControllerId());
tenantAware.getCurrentTenant(), target.getControllerId(), action.getId().toString());
final Long softwareModuleId = distributionSet.getModules().stream().findAny().get().getId();
testdataFactory.createArtifacts(softwareModuleId);
@@ -105,7 +95,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final byte[] feedback = jsonToCbor(
JsonBuilder.deploymentActionFeedback(action.getId().toString(), "proceeding"));
postFeedback(MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), target.getControllerId(),
postDeploymentFeedback(MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), target.getControllerId(),
action.getId(), feedback, status().isOk());
}
@@ -173,12 +163,11 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
// Run test
final long current = System.currentTimeMillis();
performGet(CONTROLLER_BY_ID, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
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("http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/"
+ DEFAULT_CONTROLLER_ID + "/deploymentBase/" + uaction.getId())));
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())
@@ -209,24 +198,23 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds.getId(), target.getControllerId(),
ActionType.TIMEFORCED, System.currentTimeMillis() + 2_000));
MvcResult mvcResult = performGet("/{tenant}/controller/v1/" + DEFAULT_CONTROLLER_ID, MediaTypes.HAL_JSON,
status().isOk(), tenantAware.getCurrentTenant()).andReturn();
MvcResult mvcResult = performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(),
tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID).andReturn();
final String urlBeforeSwitch = JsonPath.compile("_links.deploymentBase.href")
.read(mvcResult.getResponse().getContentAsString()).toString();
// Time is not yet over, so we should see the same URL
mvcResult = performGet(CONTROLLER_BY_ID, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
mvcResult = performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID).andReturn();
assertThat(JsonPath.compile("_links.deploymentBase.href").read(mvcResult.getResponse().getContentAsString())
.toString()).isEqualTo(urlBeforeSwitch)
.startsWith("http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/"
+ DEFAULT_CONTROLLER_ID + "/deploymentBase/" + actionId);
.startsWith(deploymentBaseLink(DEFAULT_CONTROLLER_ID, actionId.toString()));
// After the time is over we should see a new etag
TimeUnit.MILLISECONDS.sleep(2_000);
mvcResult = performGet(CONTROLLER_BY_ID, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
mvcResult = performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID).andReturn();
assertThat(JsonPath.compile("_links.deploymentBase.href").read(mvcResult.getResponse().getContentAsString())
@@ -273,12 +261,11 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
// Run test
final long current = System.currentTimeMillis();
performGet(CONTROLLER_BY_ID, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
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("http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/"
+ DEFAULT_CONTROLLER_ID + "/deploymentBase/" + uaction.getId())));
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())
@@ -333,12 +320,11 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
// Run test
final long current = System.currentTimeMillis();
performGet(CONTROLLER_BY_ID, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
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("http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/"
+ DEFAULT_CONTROLLER_ID + "/deploymentBase/" + uaction.getId())));
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())
@@ -401,12 +387,11 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
// Run test
final long current = System.currentTimeMillis();
performGet(CONTROLLER_BY_ID, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(),
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("http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/"
+ DEFAULT_CONTROLLER_ID + "/deploymentBase/" + uaction.getId())));
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())
@@ -439,97 +424,39 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
.value(visibleMetadataOsValue));
}
private ResultActions getAndVerifyDeploymentBasePayload(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 {
return performGet("/{tenant}/controller/v1/" + controllerId + "/deploymentBase/{actionId}", mediaType,
status().isOk(), tenantAware.getCurrentTenant(), actionId.toString())
.andExpect(jsonPath("$.id", equalTo(String.valueOf(actionId))))
.andExpect(jsonPath("$.deployment.download", equalTo(downloadType)))
.andExpect(jsonPath("$.deployment.update", equalTo(updateType)))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='jvm')].name",
contains(ds.findFirstModuleByType(runtimeType).get().getName())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='jvm')].version",
contains(ds.findFirstModuleByType(runtimeType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].name",
contains(ds.findFirstModuleByType(osType).get().getName())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].version",
contains(ds.findFirstModuleByType(osType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0].size",
contains(ARTIFACT_SIZE)))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0].filename",
contains("test1")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0].hashes.md5",
contains(artifact.getMd5Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0].hashes.sha1",
contains(artifact.getSha1Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0].hashes.sha256",
contains(artifact.getSha256Hash())))
.andExpect(jsonPath(
"$.deployment.chunks[?(@.part=='os')].artifacts[0]._links.download-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/"
+ controllerId + "/softwaremodules/" + osModuleId + "/artifacts/test1")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0]._links.md5sum-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/"
+ controllerId + "/softwaremodules/" + osModuleId + "/artifacts/test1.MD5SUM")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1].size",
contains(ARTIFACT_SIZE)))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1].filename",
contains("test1.signature")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1].hashes.md5",
contains(artifactSignature.getMd5Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1].hashes.sha1",
contains(artifactSignature.getSha1Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1].hashes.sha256",
contains(artifactSignature.getSha256Hash())))
.andExpect(
jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1]._links.download-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/"
+ controllerId + "/softwaremodules/" + osModuleId
+ "/artifacts/test1.signature")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1]._links.md5sum-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/"
+ controllerId + "/softwaremodules/" + osModuleId
+ "/artifacts/test1.signature.MD5SUM")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='bApp')].version",
contains(ds.findFirstModuleByType(appType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='bApp')].metadata").doesNotExist())
.andExpect(jsonPath("$.deployment.chunks[?(@.part=='bApp')].name")
.value(ds.findFirstModuleByType(appType).get().getName()));
}
@Test
@Description("Test various invalid access attempts to the deployment resource und the expected behaviour of the server.")
public void badDeploymentAction() throws Exception {
final Target target = testdataFactory.createTarget(DEFAULT_CONTROLLER_ID);
// not allowed methods
mvc.perform(post(DEPLOYMENT_BASE + "1", tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID))
mvc.perform(post(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, "1"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(put(DEPLOYMENT_BASE + "1", tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID))
mvc.perform(put(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, "1"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(delete(DEPLOYMENT_BASE + "1", tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID))
mvc.perform(delete(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, "1"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
// non existing target
mvc.perform(MockMvcRequestBuilders.get(DEPLOYMENT_BASE + "1", tenantAware.getCurrentTenant(), "not-existing"))
mvc.perform(MockMvcRequestBuilders.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), "not-existing", "1"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// no deployment
mvc.perform(MockMvcRequestBuilders.get(DEPLOYMENT_BASE + "1", tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
mvc.perform(
MockMvcRequestBuilders.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, "1"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// wrong media type
final List<Target> toAssign = Collections.singletonList(target);
final DistributionSet savedSet = testdataFactory.createDistributionSet("");
final Long actionId = getFirstAssignedActionId(assignDistributionSet(savedSet, toAssign));
mvc.perform(MockMvcRequestBuilders.get(DEPLOYMENT_BASE + actionId, tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(MockMvcRequestBuilders.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID,
actionId)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(MockMvcRequestBuilders
.get(DEPLOYMENT_BASE + actionId, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID)
.get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, actionId)
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotAcceptable());
}
@@ -548,11 +475,10 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final String feedback = JsonBuilder.deploymentActionFeedback(action.getId().toString(), "proceeding");
// assign distribution set creates an action status, so only 99 left
for (int i = 0; i < 99; i++) {
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, action.getId(), feedback, status().isOk());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, action.getId(), feedback, status().isOk());
}
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, action.getId(), feedback,
status().isForbidden());
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, action.getId(), feedback, status().isForbidden());
}
@Test
@@ -573,7 +499,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
final String feedback = JsonBuilder.deploymentActionFeedback(action.getId().toString(), "proceeding", "none",
messages);
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, action.getId(), feedback,
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, action.getId(), feedback,
status().isForbidden());
}
@@ -596,21 +522,21 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
assertThat(targetManagement.findByUpdateStatus(PageRequest.of(0, 10), TargetUpdateStatus.UNKNOWN)).hasSize(2);
// action1 done
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, actionId1,
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId1,
JsonBuilder.deploymentActionFeedback(actionId1.toString(), "closed"), status().isOk());
findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.PENDING, 2, Optional.of(ds1));
assertStatusMessagesCount(4);
// action2 done
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, actionId2,
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId2,
JsonBuilder.deploymentActionFeedback(actionId2.toString(), "closed"), status().isOk());
findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.PENDING, 1, Optional.of(ds2));
assertStatusMessagesCount(5);
// action3 done
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, actionId3,
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId3,
JsonBuilder.deploymentActionFeedback(actionId3.toString(), "closed"), status().isOk());
findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.IN_SYNC, 0, Optional.of(ds3));
@@ -629,7 +555,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
assignDistributionSet(ds, Collections.singletonList(savedTarget));
final Action action = deploymentManagement.findActionsByDistributionSet(PAGE, ds.getId()).getContent().get(0);
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, action.getId(),
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, action.getId(),
JsonBuilder.deploymentActionFeedback(action.getId().toString(), "closed", "failure", "error message"),
status().isOk());
@@ -643,7 +569,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
Collections.singletonList(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get()));
final Action action2 = deploymentManagement.findActiveActionsByTarget(PAGE, DEFAULT_CONTROLLER_ID).getContent()
.get(0);
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, action2.getId(),
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, action2.getId(),
JsonBuilder.deploymentActionFeedback(action2.getId().toString(), "closed", "SUCCESS"), status().isOk());
findTargetAndAssertUpdateStatus(Optional.of(ds), TargetUpdateStatus.IN_SYNC, 0, Optional.of(ds));
assertTargetCountByStatus(0, 0, 1);
@@ -666,32 +592,32 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
// Now valid Feedback
for (int i = 0; i < 4; i++) {
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, actionId,
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "proceeding"), status().isOk());
assertActionStatusCount(i + 2, i);
}
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, actionId,
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "scheduled"), status().isOk());
assertActionStatusCount(6, 5);
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, actionId,
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "resumed"), status().isOk());
assertActionStatusCount(7, 6);
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, actionId,
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "canceled"), status().isOk());
assertStatusAndActiveActionsCount(TargetUpdateStatus.PENDING, 1);
assertActionStatusCount(8, 7, 0, 0, 1);
assertTargetCountByStatus(1, 0, 0);
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, actionId,
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "rejected"), status().isOk());
assertStatusAndActiveActionsCount(TargetUpdateStatus.PENDING, 1);
assertActionStatusCount(9, 6, 1, 0, 1);
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, actionId,
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed"), status().isOk());
assertStatusAndActiveActionsCount(TargetUpdateStatus.IN_SYNC, 0);
assertActionStatusCount(10, 7, 1, 1, 1);
@@ -709,13 +635,13 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
// target does not exist
postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, 1234L,
postDeploymentFeedback(DEFAULT_CONTROLLER_ID, 1234L,
JsonBuilder.deploymentActionInProgressFeedback("1234"), status().isNotFound());
final Target savedTarget = testdataFactory.createTarget(DEFAULT_CONTROLLER_ID);
// Action does not exists
postFeedback(MediaType.APPLICATION_JSON, "4713", 1234L, JsonBuilder.deploymentActionInProgressFeedback("1234"),
// Action does not exist
postDeploymentFeedback("4713", 1234L, JsonBuilder.deploymentActionInProgressFeedback("1234"),
status().isNotFound());
assignDistributionSet(savedSet, Collections.singletonList(savedTarget)).getAssignedEntity().iterator().next();
@@ -725,17 +651,18 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
.getContent().get(0);
// action exists but is not assigned to this target
postFeedback(MediaType.APPLICATION_JSON, "4713", updateAction.getId(),
postDeploymentFeedback("4713", updateAction.getId(),
JsonBuilder.deploymentActionInProgressFeedback(updateAction.getId().toString()), status().isNotFound());
// not allowed methods
mvc.perform(MockMvcRequestBuilders.get(DEPLOYMENT_BASE + "2/feedback", tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(MockMvcRequestBuilders.get(DEPLOYMENT_FEEDBACK, tenantAware.getCurrentTenant(),
DEFAULT_CONTROLLER_ID, "2")).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
mvc.perform(put(DEPLOYMENT_BASE + "2/feedback", tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID))
mvc.perform(put(DEPLOYMENT_FEEDBACK, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, "2"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(delete(DEPLOYMENT_BASE + "2/feedback", tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID))
mvc.perform(delete(DEPLOYMENT_FEEDBACK, tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID, "2"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
}
@@ -754,7 +681,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
assignDistributionSet(ds.getId(), "1080");
final Action action = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent()
.get(0);
postFeedback(MediaType.APPLICATION_JSON, "1080", action.getId(),
postDeploymentFeedback("1080", action.getId(),
JsonBuilder.deploymentActionInProgressFeedback("AAAA"), status().isBadRequest());
}
@@ -775,7 +702,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
.get(0);
final String missingResultInFeedback = JsonBuilder.missingResultInFeedback(action.getId().toString(), "closed",
"test");
postFeedback(MediaType.APPLICATION_JSON, "1080", action.getId(), missingResultInFeedback,
postDeploymentFeedback("1080", action.getId(), missingResultInFeedback,
status().isBadRequest()).andExpect(jsonPath("$.*", hasSize(3))).andExpect(
jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName())));
}
@@ -797,7 +724,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
.get(0);
final String missingFinishedResultInFeedback = JsonBuilder
.missingFinishedResultInFeedback(action.getId().toString(), "closed", "test");
postFeedback(MediaType.APPLICATION_JSON, "1080", action.getId(), missingFinishedResultInFeedback,
postDeploymentFeedback("1080", action.getId(), missingFinishedResultInFeedback,
status().isBadRequest()).andExpect(jsonPath("$.*", hasSize(3))).andExpect(
jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName())));
}
@@ -814,7 +741,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
new ActionStatusCondition(Status.RUNNING));
}
private class ActionStatusCondition extends Condition<ActionStatus> {
private static class ActionStatusCondition extends Condition<ActionStatus> {
private final Status status;
public ActionStatusCondition(final Status status) {
@@ -827,31 +754,11 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
}
}
private 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))
.andDo(MockMvcResultPrinter.print()).andExpect(statusMatcher)
.andExpect(content().contentTypeCompatibleWith(mediaType));
}
private ResultActions postFeedback(final MediaType mediaType, final String controllerId, final Long id,
final String content, final ResultMatcher statusMatcher) throws Exception {
return postFeedback(mediaType, controllerId, id, content.getBytes(), statusMatcher);
}
private ResultActions postFeedback(final MediaType mediaType, final String controllerId, final Long id,
final byte[] content, final ResultMatcher statusMatcher) throws Exception {
return mvc
.perform(post(DEPLOYMENT_BASE + id + "/feedback", tenantAware.getCurrentTenant(), controllerId)
.content(content).contentType(mediaType).accept(mediaType))
.andDo(MockMvcResultPrinter.print()).andExpect(statusMatcher);
}
private Target createTargetAndAssertNoActiveActions() {
final Target savedTarget = testdataFactory.createTarget(DdiDeploymentBaseTest.DEFAULT_CONTROLLER_ID);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).isEmpty();
assertThat(deploymentManagement.countActionsAll()).isEqualTo(0);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(0);
assertThat(deploymentManagement.countActionsAll()).isZero();
assertThat(deploymentManagement.countActionStatusAll()).isZero();
return savedTarget;
}

View File

@@ -0,0 +1,415 @@
/**
* Copyright (c) 2022 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.rest.resource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
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.request.MockMvcRequestBuilders.put;
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.Stream;
import org.apache.commons.lang3.RandomUtils;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.hateoas.MediaTypes;
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.
*/
@Feature("Component Tests - Direct Device Integration API")
@Story("Installed Base Resource")
public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest {
private static final int ARTIFACT_SIZE = 5 * 1024;
private static final String CONTROLLER_ID = "4715";
@Test
@Description("Ensure that the installed base resource is available as CBOR")
public void installedBaseResourceCbor() throws Exception {
final Target target = testdataFactory.createTarget();
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds, target));
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed"), status().isOk());
// get installed base
performGet(INSTALLED_BASE, MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), status().isOk(),
tenantAware.getCurrentTenant(), target.getControllerId(), actionId.toString());
final Long softwareModuleId = ds.getModules().stream().findAny().get().getId();
testdataFactory.createArtifacts(softwareModuleId);
// get artifacts
performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR),
status().isOk(), tenantAware.getCurrentTenant(), target.getControllerId(),
String.valueOf(softwareModuleId));
}
@Test
@Description("Test several deployments to a controller. Checks that action is represented as installedBase after installation.")
public void deploymentSeveralActionsInInstalledBase() throws Exception {
// Prepare test data
final Target target = createTargetAndAssertNoActiveActions();
final DistributionSet ds1 = testdataFactory.createDistributionSet("1", true);
final Artifact artifact1 = testdataFactory.createArtifact(RandomUtils.nextBytes(ARTIFACT_SIZE),
getOsModule(ds1), "test1", ARTIFACT_SIZE);
final Artifact artifactSignature1 = testdataFactory.createArtifact(RandomUtils.nextBytes(ARTIFACT_SIZE),
getOsModule(ds1), "test1.signature", ARTIFACT_SIZE);
final DistributionSet ds2 = testdataFactory.createDistributionSet("2", true);
final Artifact artifact2 = testdataFactory.createArtifact(RandomUtils.nextBytes(ARTIFACT_SIZE),
getOsModule(ds2), "test2", ARTIFACT_SIZE);
final Artifact artifactSignature2 = testdataFactory.createArtifact(RandomUtils.nextBytes(ARTIFACT_SIZE),
getOsModule(ds2), "test2.signature", ARTIFACT_SIZE);
// Run test with 1st action
final Long actionId1 = getFirstAssignedActionId(
assignDistributionSet(ds1.getId(), target.getControllerId(), Action.ActionType.SOFT));
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(), CONTROLLER_ID)
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist())
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(CONTROLLER_ID, actionId1.toString()))));
getAndVerifyDeploymentBasePayload(CONTROLLER_ID, MediaType.APPLICATION_JSON, ds1, artifact1, artifactSignature1,
actionId1, ds1.findFirstModuleByType(osType).get().getId(), Action.ActionType.SOFT);
postDeploymentFeedback(target.getControllerId(), actionId1,
JsonBuilder.deploymentActionFeedback(actionId1.toString(), "closed", "success", "Closed"),
status().isOk());
getAndVerifyInstalledBasePayload(CONTROLLER_ID, MediaType.APPLICATION_JSON, ds1, artifact1, artifactSignature1,
actionId1, ds1.findFirstModuleByType(osType).get().getId(), Action.ActionType.SOFT);
// Run test with 2nd action
final Long actionId2 = getFirstAssignedActionId(
assignDistributionSet(ds2.getId(), target.getControllerId(), Action.ActionType.FORCED));
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(), CONTROLLER_ID)
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.installedBase.href",
startsWith(installedBaseLink(CONTROLLER_ID, actionId1.toString()))))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(CONTROLLER_ID, actionId2.toString()))));
getAndVerifyDeploymentBasePayload(CONTROLLER_ID, MediaType.APPLICATION_JSON, ds2, artifact2, artifactSignature2,
actionId2, ds2.findFirstModuleByType(osType).get().getId(), Action.ActionType.FORCED);
postDeploymentFeedback(target.getControllerId(), actionId2,
JsonBuilder.deploymentActionFeedback(actionId2.toString(), "closed", "success", "Closed"),
status().isOk());
getAndVerifyInstalledBasePayload(CONTROLLER_ID, MediaType.APPLICATION_JSON, ds2, artifact2, artifactSignature2,
actionId2, ds2.findFirstModuleByType(osType).get().getId(), Action.ActionType.FORCED);
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(), CONTROLLER_ID)
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.installedBase.href",
startsWith(installedBaseLink(CONTROLLER_ID, actionId2.toString()))))
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist());
// older installed action is still accessible, although not part of controller base
getAndVerifyInstalledBasePayload(CONTROLLER_ID, MediaType.APPLICATION_JSON, ds1, artifact1, artifactSignature1,
actionId1, ds1.findFirstModuleByType(osType).get().getId(), Action.ActionType.SOFT);
}
@Test
@Description("Test open deployment to a controller. Checks that installedBase returns 404 for a pending action.")
public void installedBaseReturns404ForPendingAction() throws Exception {
// Prepare test data
final Target target = createTargetAndAssertNoActiveActions();
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds, target));
performGet(CONTROLLER_BASE, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(), CONTROLLER_ID)
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist())
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith(deploymentBaseLink(CONTROLLER_ID, actionId.toString()))));
performGet(DEPLOYMENT_BASE, MediaType.APPLICATION_JSON, status().isOk(), tenantAware.getCurrentTenant(),
target.getControllerId(), actionId.toString());
mvc.perform(MockMvcRequestBuilders.get(INSTALLED_BASE, tenantAware.getCurrentTenant(), target.getControllerId(),
actionId.toString())).andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
}
@Test
@Description("Ensures that artifacts are found, after the action was already closed.")
public void artifactsOfInstalledActionExist() throws Exception {
final Target target = createTargetAndAssertNoActiveActions();
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds, target));
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed"), status().isOk());
final Long softwareModuleId = ds.getModules().stream().findAny().get().getId();
performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.APPLICATION_JSON, status().isOk(),
tenantAware.getCurrentTenant(), target.getControllerId(), softwareModuleId.toString())
.andExpect(jsonPath("$", hasSize(0)));
testdataFactory.createArtifacts(softwareModuleId);
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);
}
@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.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 2),
@Expect(type = TargetPollEvent.class, count = 1) })
public void deploymentActionInInstalledBase(final Action.ActionType actionType) throws Exception {
// Prepare test data
final Target target = createTargetAndAssertNoActiveActions();
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
final Artifact artifact = testdataFactory.createArtifact(RandomUtils.nextBytes(ARTIFACT_SIZE), getOsModule(ds),
"test1", ARTIFACT_SIZE);
final Artifact artifactSignature = testdataFactory.createArtifact(RandomUtils.nextBytes(ARTIFACT_SIZE),
getOsModule(ds), "test1.signature", ARTIFACT_SIZE);
final Long actionId = getFirstAssignedActionId(
assignDistributionSet(ds.getId(), target.getControllerId(), actionType));
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed", "success", "Closed"),
status().isOk());
// Run test
final ResultActions resultActions = performGet(CONTROLLER_BASE, MediaType.APPLICATION_JSON, status().isOk(),
tenantAware.getCurrentTenant(), target.getControllerId());
resultActions.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist())
.andExpect(jsonPath("$._links.installedBase.href",
containsString(String.format("/%s/controller/v1/%s/installedBase/%d",
tenantAware.getCurrentTenant(), target.getControllerId(), actionId))));
getAndVerifyInstalledBasePayload(CONTROLLER_ID, MediaType.APPLICATION_JSON, ds, artifact, artifactSignature,
actionId, ds.findFirstModuleByType(osType).get().getId(), actionType);
getAndVerifyInstalledBasePayload(CONTROLLER_ID, MediaTypes.HAL_JSON, ds, artifact, artifactSignature, actionId,
ds.findFirstModuleByType(osType).get().getId(), actionType);
// Action is still finished after calling installedBase
final Iterable<ActionStatus> actionStatusMessages = deploymentManagement
.findActionStatusByAction(PageRequest.of(0, 100, Sort.Direction.DESC, "id"), actionId);
assertThat(actionStatusMessages).hasSize(2);
final ActionStatus actionStatusMessage = actionStatusMessages.iterator().next();
assertThat(actionStatusMessage.getStatus()).isEqualTo(Action.Status.FINISHED);
}
@Test
@Description("Test download-only deployment to a controller. Checks that download-only is not represented as installedBase.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = TargetPollEvent.class, count = 2) })
public void deploymentDownloadOnlyActionNotInInstalledBase() throws Exception {
// Prepare test data
final Target target = testdataFactory.createTarget();
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Long actionId = getFirstAssignedActionId(
assignDistributionSet(ds.getId(), target.getControllerId(), Action.ActionType.DOWNLOAD_ONLY));
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());
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "download"), status().isOk());
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "downloaded"), status().isOk());
// 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());
}
@ParameterizedTest
@MethodSource("org.eclipse.hawkbit.ddi.rest.resource.DdiInstalledBaseTest#actionTypeForDeployment")
@Description("Test a failed deployment to a controller. Checks that closed action is not represented as installedBase.")
public void deploymentActionFailedNotInInstalledBase(Action.ActionType actionType) throws Exception {
// Prepare test data
final Target target = testdataFactory.createTarget();
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Long actionId = getFirstAssignedActionId(
assignDistributionSet(ds.getId(), target.getControllerId(), actionType));
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());
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "proceeding"), status().isOk());
postDeploymentFeedback(target.getControllerId(), actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed", "failure", "Installation failed"),
status().isOk());
// 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());
}
@Test
@Description("Test to verify that only a specific count of messages are returned based on the input actionHistory for getControllerInstalledAction endpoint.")
public void testActionHistoryCount() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = testdataFactory.createTarget("911");
savedTarget = getFirstAssignedTarget(assignDistributionSet(ds.getId(), savedTarget.getControllerId()));
final Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
postDeploymentFeedback(savedTarget.getControllerId(), savedAction.getId(), JsonBuilder.deploymentActionFeedback(
savedAction.getId().toString(), "scheduled", "Installation scheduled"), status().isOk());
postDeploymentFeedback(savedTarget.getControllerId(), savedAction.getId(), JsonBuilder.deploymentActionFeedback(
savedAction.getId().toString(), "proceeding", "Installation proceeding"), status().isOk());
// only this feedback triggers the ActionUpdateEvent
postDeploymentFeedback(savedTarget.getControllerId(), savedAction.getId(), JsonBuilder.deploymentActionFeedback(
savedAction.getId().toString(), "closed", "success", "Installation completed"), status().isOk());
// 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))
.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))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages", hasItem(containsString("Installation completed"))))
.andExpect(jsonPath("$.actionHistory.messages", hasItem(containsString("Installation proceeding"))))
.andExpect(
jsonPath("$.actionHistory.messages", not(hasItem(containsString("Installation scheduled")))));
// 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))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages", hasItem(containsString("Installation completed"))))
.andExpect(jsonPath("$.actionHistory.messages", hasItem(containsString("Installation proceeding"))))
.andExpect(jsonPath("$.actionHistory.messages", hasItem(containsString("Installation scheduled"))));
}
@Test
@Description("Test various invalid access attempts to the installed resource und the expected behaviour of the server.")
public void badInstalledAction() throws Exception {
final Target target = testdataFactory.createTarget(CONTROLLER_ID);
// not allowed methods
mvc.perform(post(INSTALLED_BASE, tenantAware.getCurrentTenant(), CONTROLLER_ID, "1"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(put(INSTALLED_BASE, tenantAware.getCurrentTenant(), CONTROLLER_ID, "1"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(delete(INSTALLED_BASE, tenantAware.getCurrentTenant(), CONTROLLER_ID, "1"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
// non existing target
mvc.perform(MockMvcRequestBuilders.get(INSTALLED_BASE, tenantAware.getCurrentTenant(), "not-existing", "1"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// no deployment
mvc.perform(MockMvcRequestBuilders.get(INSTALLED_BASE, tenantAware.getCurrentTenant(), CONTROLLER_ID, "1"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// wrong media type
final List<Target> toAssign = Collections.singletonList(target);
final DistributionSet savedSet = testdataFactory.createDistributionSet("");
final Long actionId = getFirstAssignedActionId(assignDistributionSet(savedSet, toAssign));
postDeploymentFeedback(CONTROLLER_ID, actionId,
JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed", "success"), status().isOk());
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())
.andExpect(status().isNotAcceptable());
}
private Target createTargetAndAssertNoActiveActions() {
final Target savedTarget = testdataFactory.createTarget(CONTROLLER_ID);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).isEmpty();
assertThat(deploymentManagement.countActionsAll()).isZero();
assertThat(deploymentManagement.countActionStatusAll()).isZero();
return savedTarget;
}
}

View File

@@ -18,6 +18,7 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -87,7 +88,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensure that the root poll resource is available as CBOR")
public void rootPollResourceCbor() throws Exception {
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), 4711)
.accept(DdiRestConstants.MEDIA_TYPE_CBOR)).andDo(MockMvcResultPrinter.print())
.andExpect(content().contentType(DdiRestConstants.MEDIA_TYPE_CBOR)).andExpect(status().isOk());
}
@@ -95,7 +96,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that the API returns JSON when no Accept header is specified by the client.")
public void apiReturnsJSONByDefault() throws Exception {
final MvcResult result = mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()))
final MvcResult result = mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), 4711))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON)).andReturn();
@@ -120,13 +121,13 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
// create tenant -- creates softwaremoduletypes and distributionsettypes
systemManagement.getTenantMetadata("tenantDoesNotExists");
mvc.perform(get("/{}/controller/v1/aControllerId", tenantAware.getCurrentTenant()))
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), "aControllerId"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
// delete tenant again, will also deleted target aControllerId
systemManagement.deleteTenant("tenantDoesNotExists");
mvc.perform(get("/{}/controller/v1/aControllerId", tenantAware.getCurrentTenant()))
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), "aControllerId"))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
}
@@ -148,7 +149,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
// make a poll, audit information should not be changed, run as
// controller principal!
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withController("controller", CONTROLLER_ROLE_ANONYMOUS), () -> {
mvc.perform(get("/{tenant}/controller/v1/" + knownTargetControllerId, tenantAware.getCurrentTenant()))
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), knownTargetControllerId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
return null;
});
@@ -162,7 +163,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
}
@Test
@Description("Ensures that server returns a not found response in case of empty controlloer ID.")
@Description("Ensures that server returns a not found response in case of empty controller ID.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void rootRsWithoutId() throws Exception {
mvc.perform(get("/controller/v1/")).andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
@@ -175,24 +176,25 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
public void rootRsPlugAndPlay() throws Exception {
final long current = System.currentTimeMillis();
String controllerId = "4711";
mvc.perform(get("/default-tenant/controller/v1/4711")).andDo(MockMvcResultPrinter.print())
mvc.perform(get(CONTROLLER_BASE, "default-tenant", controllerId)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(content().contentType(MediaTypes.HAL_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")));
assertThat(targetManagement.getByControllerID("4711").get().getLastTargetQuery())
assertThat(targetManagement.getByControllerID(controllerId).get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
assertThat(targetManagement.getByControllerID("4711").get().getUpdateStatus())
assertThat(targetManagement.getByControllerID(controllerId).get().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.REGISTERED);
// not allowed methods
mvc.perform(post("/default-tenant/controller/v1/4711")).andDo(MockMvcResultPrinter.print())
mvc.perform(post(CONTROLLER_BASE, "default-tenant", controllerId)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
mvc.perform(put("/default-tenant/controller/v1/4711")).andDo(MockMvcResultPrinter.print())
mvc.perform(put(CONTROLLER_BASE, "default-tenant", controllerId)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/default-tenant/controller/v1/4711")).andDo(MockMvcResultPrinter.print())
mvc.perform(delete(CONTROLLER_BASE, "default-tenant", controllerId)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
}
@@ -210,7 +212,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
});
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withUser("controller", CONTROLLER_ROLE_ANONYMOUS), () -> {
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()))
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), 4711))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:02:00")));
@@ -229,63 +231,76 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6) })
public void rootRsNotModified() throws Exception {
final String etag = mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()))
String controllerId = "4711";
final String etag = mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON))
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist())
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00"))).andReturn().getResponse()
.getHeader("ETag");
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()).header("If-None-Match", etag))
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).header("If-None-Match", etag))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotModified());
final Target target = targetManagement.getByControllerID("4711").get();
final Target target = targetManagement.getByControllerID(controllerId).get();
final DistributionSet ds = testdataFactory.createDistributionSet("");
assignDistributionSet(ds.getId(), "4711");
assignDistributionSet(ds.getId(), controllerId);
final Action updateAction = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
.getContent().get(0);
final String etagWithFirstUpdate = mvc
.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())
.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId)
.header("If-None-Match", etag).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.installedBase.href").doesNotExist())
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith("http://localhost/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4711/deploymentBase/" + updateAction.getId())))
startsWith(deploymentBaseLink("4711", updateAction.getId().toString()))))
.andReturn().getResponse().getHeader("ETag");
assertThat(etagWithFirstUpdate).isNotNull();
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()).header("If-None-Match",
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId).header("If-None-Match",
etagWithFirstUpdate)).andDo(MockMvcResultPrinter.print()).andExpect(status().isNotModified());
// now lets finish the update
sendDeploymentActionFeedback(target, updateAction, "closed", null).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
// we are again at the original state
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()).header("If-None-Match", etag))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotModified());
// as the update was installed, and we always receive the installed action, the
// original state cannot be restored
final String etagAfterInstallation = mvc
.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId)
.header("If-None-Match", etag).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href").doesNotExist())
.andExpect(jsonPath("$._links.installedBase.href",
startsWith(installedBaseLink("4711", updateAction.getId().toString()))))
.andReturn().getResponse().getHeader("ETag");
// Now another deployment
final DistributionSet ds2 = testdataFactory.createDistributionSet("2");
assignDistributionSet(ds2.getId(), "4711");
assignDistributionSet(ds2.getId(), controllerId);
final Action updateAction2 = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
.getContent().get(0);
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())
.header("If-None-Match", etagWithFirstUpdate).accept(MediaType.APPLICATION_JSON))
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId)
.header("If-None-Match", etagAfterInstallation).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.installedBase.href",
startsWith(installedBaseLink("4711", updateAction.getId().toString()))))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith("http://localhost/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4711/deploymentBase/" + updateAction2.getId())))
startsWith(deploymentBaseLink("4711", updateAction2.getId().toString()))))
.andReturn().getResponse().getHeader("ETag");
}
@Test
@@ -294,23 +309,24 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsPrecommissioned() throws Exception {
testdataFactory.createTarget("4711");
String controllerId = "4711";
testdataFactory.createTarget(controllerId);
assertThat(targetManagement.getByControllerID("4711").get().getUpdateStatus())
assertThat(targetManagement.getByControllerID(controllerId).get().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.UNKNOWN);
final long current = System.currentTimeMillis();
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()))
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")));
assertThat(targetManagement.getByControllerID("4711").get().getLastTargetQuery())
assertThat(targetManagement.getByControllerID(controllerId).get().getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
assertThat(targetManagement.getByControllerID("4711").get().getLastTargetQuery())
assertThat(targetManagement.getByControllerID(controllerId).get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
assertThat(targetManagement.getByControllerID("4711").get().getUpdateStatus())
assertThat(targetManagement.getByControllerID(controllerId).get().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.REGISTERED);
}
@@ -326,7 +342,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
// make a poll, audit information should be set on plug and play
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withController("controller", CONTROLLER_ROLE_ANONYMOUS), () -> {
mvc.perform(
get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(), knownControllerId1))
get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), knownControllerId1))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
return null;
});
@@ -350,7 +366,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
// test
final String knownControllerId1 = "0815";
mvc.perform(get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(), knownControllerId1))
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), knownControllerId1))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
// verify
@@ -399,7 +415,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
final Map<String, String> attributes = Collections.singletonMap("AttributeKey", "AttributeValue");
assertThatAttributesUpdateIsRequested(savedTarget.getControllerId());
mvc.perform(put("/{tenant}/controller/v1/{controllerId}/configData", tenantAware.getCurrentTenant(),
mvc.perform(put(CONTROLLER_BASE + "/configData", tenantAware.getCurrentTenant(),
savedTarget.getControllerId()).content(JsonBuilder.configData(attributes).toString())
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
@@ -431,13 +447,13 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
}
private void assertThatAttributesUpdateIsRequested(final String targetControllerId) throws Exception {
mvc.perform(get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(), targetControllerId)
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("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(), targetControllerId)
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), targetControllerId)
.accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andExpect(jsonPath("$._links.configData").doesNotExist());
}
@@ -452,9 +468,9 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
}
final String feedback = JsonBuilder.deploymentActionFeedback(action.getId().toString(), execution, finished,
message);
return mvc.perform(post("/{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}/feedback",
tenantAware.getCurrentTenant(), target.getControllerId(), action.getId()).content(feedback)
.contentType(MediaType.APPLICATION_JSON));
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,
@@ -487,14 +503,16 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "success", TARGET_COMPLETED_INSTALLATION_MSG)
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "?actionHistory=3",
tenantAware.getCurrentTenant()).contentType(MediaType.APPLICATION_JSON)
mvc.perform(get(DEPLOYMENT_BASE + "?actionHistory=2", tenantAware.getCurrentTenant(), 911, savedAction.getId())
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages",
hasItem(containsString(TARGET_COMPLETED_INSTALLATION_MSG))))
.andExpect(jsonPath("$.actionHistory.messages",
hasItem(containsString(TARGET_PROCEEDING_INSTALLATION_MSG))))
.andExpect(jsonPath("$.actionHistory.messages",
hasItem(containsString(TARGET_SCHEDULED_INSTALLATION_MSG))));
not(hasItem(containsString(TARGET_SCHEDULED_INSTALLATION_MSG)))));
}
@Test
@@ -522,10 +540,16 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "success", TARGET_COMPLETED_INSTALLATION_MSG)
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "?actionHistory=-2",
tenantAware.getCurrentTenant()).contentType(MediaType.APPLICATION_JSON)
mvc.perform(get(DEPLOYMENT_BASE + "?actionHistory=0", tenantAware.getCurrentTenant(), 911, savedAction.getId())
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
.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))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages").doesNotExist());
}
@Test
@@ -553,8 +577,8 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "success", TARGET_COMPLETED_INSTALLATION_MSG)
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "?actionHistory=-1",
tenantAware.getCurrentTenant()).contentType(MediaType.APPLICATION_JSON)
mvc.perform(get(DEPLOYMENT_BASE + "?actionHistory=-1", tenantAware.getCurrentTenant(), 911, savedAction.getId())
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages",
@@ -582,7 +606,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
assignDistributionSetWithMaintenanceWindow(ds.getId(), savedTarget.getControllerId(), getTestSchedule(16),
getTestDuration(10), getTestTimeZone()).getAssignedEntity().iterator().next();
mvc.perform(get("/default-tenant/controller/v1/1911/")).andExpect(status().isOk())
mvc.perform(get(CONTROLLER_BASE, "default-tenant", "1911")).andExpect(status().isOk())
.andExpect(jsonPath("$.config.polling.sleep", greaterThanOrEqualTo("00:05:00")));
final Target savedTarget1 = testdataFactory.createTarget("2911");
@@ -590,7 +614,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
assignDistributionSetWithMaintenanceWindow(ds1.getId(), savedTarget1.getControllerId(), getTestSchedule(10),
getTestDuration(10), getTestTimeZone()).getAssignedEntity().iterator().next();
mvc.perform(get("/default-tenant/controller/v1/2911/")).andExpect(status().isOk())
mvc.perform(get(CONTROLLER_BASE, "default-tenant", "2911")).andExpect(status().isOk())
.andExpect(jsonPath("$.config.polling.sleep", lessThan("00:05:00")))
.andExpect(jsonPath("$.config.polling.sleep", greaterThanOrEqualTo("00:03:00")));
@@ -599,7 +623,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
assignDistributionSetWithMaintenanceWindow(ds2.getId(), savedTarget2.getControllerId(), getTestSchedule(5),
getTestDuration(5), getTestTimeZone()).getAssignedEntity().iterator().next();
mvc.perform(get("/default-tenant/controller/v1/3911/")).andExpect(status().isOk())
mvc.perform(get(CONTROLLER_BASE, "default-tenant", "3911")).andExpect(status().isOk())
.andExpect(jsonPath("$.config.polling.sleep", lessThan("00:02:00")));
final Target savedTarget3 = testdataFactory.createTarget("4911");
@@ -607,7 +631,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
assignDistributionSetWithMaintenanceWindow(ds3.getId(), savedTarget3.getControllerId(), getTestSchedule(-5),
getTestDuration(15), getTestTimeZone()).getAssignedEntity().iterator().next();
mvc.perform(get("/default-tenant/controller/v1/4911/")).andExpect(status().isOk())
mvc.perform(get(CONTROLLER_BASE, "default-tenant", "4911")).andExpect(status().isOk())
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:05:00")));
}
@@ -620,12 +644,12 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
savedTarget = getFirstAssignedTarget(assignDistributionSetWithMaintenanceWindow(ds.getId(),
savedTarget.getControllerId(), getTestSchedule(2), getTestDuration(1), getTestTimeZone()));
mvc.perform(get("/default-tenant/controller/v1/1911/")).andExpect(status().isOk());
mvc.perform(get(CONTROLLER_BASE, "default-tenant", "1911")).andExpect(status().isOk());
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
mvc.perform(get("/{tenant}/controller/v1/1911/deploymentBase/{actionId}", tenantAware.getCurrentTenant(),
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), "1911",
action.getId()).accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(jsonPath("$.deployment.download", equalTo("forced")))
.andExpect(jsonPath("$.deployment.update", equalTo("skip")))
@@ -640,12 +664,12 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
savedTarget = getFirstAssignedTarget(assignDistributionSetWithMaintenanceWindow(ds.getId(),
savedTarget.getControllerId(), getTestSchedule(-5), getTestDuration(10), getTestTimeZone()));
mvc.perform(get("/default-tenant/controller/v1/1911/")).andExpect(status().isOk());
mvc.perform(get(CONTROLLER_BASE, "default-tenant", "1911")).andExpect(status().isOk());
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
mvc.perform(get("/{tenant}/controller/v1/1911/deploymentBase/{actionId}", tenantAware.getCurrentTenant(),
mvc.perform(get(DEPLOYMENT_BASE, tenantAware.getCurrentTenant(), "1911",
action.getId()).accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(jsonPath("$.deployment.download", equalTo("forced")))
.andExpect(jsonPath("$.deployment.update", equalTo("forced")))
@@ -673,15 +697,15 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Description("The system should not create a new target because of a too long controller id.")
public void rootRsWithInvalidControllerId() throws Exception {
final String invalidControllerId = RandomStringUtils.randomAlphabetic(Target.CONTROLLER_ID_MAX_SIZE + 1);
mvc.perform(get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(), invalidControllerId))
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), invalidControllerId))
.andExpect(status().isBadRequest());
}
public void assertDeploymentActionIsExposedToTarget(final String controllerId, final long expectedActionId)
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("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(), controllerId)
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)));