diff --git a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java index b07e2557c..d87de67d0 100644 --- a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java +++ b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java @@ -79,12 +79,12 @@ public final class DataConversionHelper { final org.eclipse.hawkbit.repository.model.SoftwareModule module, final ArtifactUrlHandler artifactUrlHandler) { final List files = new ArrayList<>(); - + module.getLocalArtifacts() - .forEach(artifact -> files.add(createArtifact(targetid, artifactUrlHandler, artifact))); + .forEach(artifact -> files.add(createArtifact(targetid, artifactUrlHandler, artifact))); return files; } - + private static DdiArtifact createArtifact(final String targetid, final ArtifactUrlHandler artifactUrlHandler, final LocalArtifact artifact) { final DdiArtifact file = new DdiArtifact(); @@ -125,7 +125,7 @@ public final class DataConversionHelper { // response because of eTags. result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant()) .getControllerBasedeploymentAction(target.getControllerId(), action.getId(), - actions.hashCode())).withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION)); + calculateEtag(action))).withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION)); addedUpdate = true; } else if (action.isCancelingOrCanceled() && !addedCancel) { result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant()) @@ -142,6 +142,22 @@ public final class DataConversionHelper { return result; } + /** + * Calculates an etag for the given {@link Action} based on the entities + * hashcode and the {@link Action#isHitAutoForceTime(long)} to reflect a + * force switch. + * + * @param action + * to calculate the etag for + * @return the etag + */ + private static int calculateEtag(final Action action) { + final int prime = 31; + int result = action.hashCode(); + result = prime * result + (action.isHitAutoForceTime(System.currentTimeMillis()) ? 1231 : 1237); + return result; + } + static void writeMD5FileResponse(final String fileName, final HttpServletResponse response, final LocalArtifact artifact) throws IOException { final StringBuilder builder = new StringBuilder(); diff --git a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java index 33d470b3d..0ec0d0d7e 100644 --- a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java +++ b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java @@ -23,15 +23,17 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.RandomUtils; +import org.eclipse.hawkbit.repository.DistributionSetAssignmentResult; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.ActionStatus; -import org.eclipse.hawkbit.repository.model.RepositoryModelConstants; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.LocalArtifact; +import org.eclipse.hawkbit.repository.model.RepositoryModelConstants; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB; @@ -43,6 +45,9 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort.Direction; import org.springframework.hateoas.MediaTypes; import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; + +import com.jayway.jsonpath.JsonPath; import ru.yandex.qatools.allure.annotations.Description; import ru.yandex.qatools.allure.annotations.Features; @@ -229,6 +234,45 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD assertThat(actionStatusMessage.getStatus()).isEqualTo(Status.RETRIEVED); } + @Test + @Description("Checks that the deployementBase URL changes when the action is switched from soft to forced in TIMEFORCED case.") + public void changeEtagIfActionSwitchesFromSoftToForced() throws Exception { + // Prepare test data + final Target target = targetManagement.createTarget(entityFactory.generateTarget("4712")); + final DistributionSet ds = testdataFactory.createDistributionSet("", true); + + final DistributionSetAssignmentResult result = deploymentManagement.assignDistributionSet(ds.getId(), + ActionType.TIMEFORCED, System.currentTimeMillis() + 1_000, target.getControllerId()); + + final Action action = deploymentManagement.findActiveActionsByTarget(result.getAssignedEntity().get(0)).get(0); + + MvcResult mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant())) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(content().contentType(MediaTypes.HAL_JSON)).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 = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant())) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(content().contentType(MediaTypes.HAL_JSON)).andReturn(); + assertThat(JsonPath.compile("_links.deploymentBase.href").read(mvcResult.getResponse().getContentAsString()) + .toString()).isEqualTo(urlBeforeSwitch) + .startsWith("http://localhost/" + tenantAware.getCurrentTenant() + + "/controller/v1/4712/deploymentBase/" + action.getId()); + + // After the time is over we should see a new etag + TimeUnit.MILLISECONDS.sleep(1_000); + + mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant())) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(content().contentType(MediaTypes.HAL_JSON)).andReturn(); + + assertThat(JsonPath.compile("_links.deploymentBase.href").read(mvcResult.getResponse().getContentAsString()) + .toString()).isNotEqualTo(urlBeforeSwitch); + } + @Test @Description("Attempt/soft deployment to a controller. Checks if the resource reponse payload for a given deployment is as expected.") public void deplomentAttemptAction() throws Exception { diff --git a/hawkbit-ddi-resource/src/test/resources/logback.xml b/hawkbit-ddi-resource/src/test/resources/logback.xml new file mode 100644 index 000000000..447712338 --- /dev/null +++ b/hawkbit-ddi-resource/src/test/resources/logback.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + \ No newline at end of file