Introduce basic functionality for invalidation of distributionsets (#1179)

* Basic DS invalidation functionality

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Add checks for valid/complete DS

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Stop rollouts + auto assignments when invalidating a DS

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Add methods to count AAs + rollouts for invalidation

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Small refactoring for DS management

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Add invalidation functionality to REST API

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Fix update stopped rollouts status

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Add various tests

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Introduce countActionsForInvalidation

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Fix event tests with incomplete DS

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Add H2 migration script

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Fix action count method

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Fix REST documentation tests

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Change flyway version number

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Add lock for DS invalidation + adapt tests

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Move concurrency test to own class

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Handle possible InterruptedException

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Fix concurrency test

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Use one transaction for all invalidations

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Add invalidate endpoint to REST docu

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Execute invalidation in transaction when actions are cancelled

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Check that distribution set is valid when editing/creating metadata

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Remove all changes in UI

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Add DB migration files for all databases

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Implement review findings

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Move DS invalidation to own class to check permissions for single steps

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Move invalidation count methods to management classes

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>

* Fix failing tests

Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>
This commit is contained in:
Sebastian Firsching
2021-09-30 15:26:36 +02:00
committed by GitHub
parent b25e118e6c
commit 825cb64448
66 changed files with 2413 additions and 511 deletions

View File

@@ -40,8 +40,12 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
@@ -1350,4 +1354,37 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
assertThat(actions).size().isEqualTo(1);
assertThat(actions.get(0).getWeight()).get().isEqualTo(weight);
}
@Test
@Description("Verify invalidation of distribution sets that removes distribution sets from auto assignments, stops rollouts and cancels assignments")
public void invalidateDistributionSet() throws Exception {
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
final List<Target> targets = testdataFactory.createTargets(5, "invalidateDistributionSet");
assignDistributionSet(distributionSet, targets);
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("invalidateDistributionSet").query("name==*")
.autoAssignDistributionSet(distributionSet));
final Rollout rollout = testdataFactory.createRolloutByVariables("invalidateDistributionSet", "desc", 2,
"name==*", distributionSet, "50", "80");
final JSONObject jsonObject = new JSONObject();
jsonObject.put("actionCancelationType", "soft");
jsonObject.put("cancelRollouts", true);
mvc.perform(post("/rest/v1/distributionsets/{ds}/invalidate", distributionSet.getId())
.content(jsonObject.toString()).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk());
assertThat(targetFilterQueryManagement.get(targetFilterQuery.getId()).get().getAutoAssignDistributionSet())
.isNull();
assertThat(rolloutManagement.get(rollout.getId()).get().getStatus()).isIn(RolloutStatus.STOPPING,
RolloutStatus.FINISHED);
for (final Target target : targets) {
assertThat(targetManagement.get(target.getId()).get().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.PENDING);
assertThat(deploymentManagement.findActionsByTarget(target.getControllerId(), PageRequest.of(0, 100))
.getNumberOfElements()).isEqualTo(1);
assertThat(deploymentManagement.findActionsByTarget(target.getControllerId(), PageRequest.of(0, 100))
.getContent().get(0).getStatus()).isEqualTo(Status.CANCELING);
}
}
}

View File

@@ -25,9 +25,10 @@ import java.util.List;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -294,7 +295,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
assertThat(exceptionInfo.getExceptionClass()).isEqualTo(MessageNotReadableException.class.getName());
assertThat(exceptionInfo.getErrorCode()).isEqualTo(SpServerError.SP_REST_BODY_NOT_READABLE.getKey());
}
@Test
@Description("Ensures that the creation of a target filter query based on an invalid RSQL query results in a HTTP Bad Request error (400).")
public void createTargetFilterWithInvalidQuery() throws Exception {
@@ -324,7 +325,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId() + "/autoAssignDS")
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
.andDo(print()).andExpect(status().isForbidden())
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(AssignmentQuotaExceededException.class.getName())))
.andExpect(
jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(AssignmentQuotaExceededException.class.getName())))
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
}
@@ -356,7 +358,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId())
.content("{\"query\":\"controllerId==target*\"}").contentType(MediaType.APPLICATION_JSON))
.andDo(print()).andExpect(status().isForbidden())
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(AssignmentQuotaExceededException.class.getName())))
.andExpect(
jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(AssignmentQuotaExceededException.class.getName())))
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
}
@@ -470,9 +473,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
.content("{\"id\":" + incompleteDistributionSet.getId() + "}").contentType(MediaType.APPLICATION_JSON))
.andDo(print()).andExpect(status().isBadRequest())
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
equalTo(InvalidAutoAssignDistributionSetException.class.getName())))
.andExpect(jsonPath(JSON_PATH_ERROR_CODE,
equalTo(SpServerError.SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID.getKey())));
equalTo(IncompleteDistributionSetException.class.getName())))
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_DS_INCOMPLETE.getKey())));
}
@Step
@@ -483,11 +485,9 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
.content("{\"id\":" + softDeletedDs.getId() + "}").contentType(MediaType.APPLICATION_JSON))
.andDo(print()).andExpect(status().isBadRequest())
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
equalTo(InvalidAutoAssignDistributionSetException.class.getName())))
.andExpect(jsonPath(JSON_PATH_ERROR_CODE,
equalTo(SpServerError.SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID.getKey())));
.andDo(print()).andExpect(status().isNotFound())
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(EntityNotFoundException.class.getName())))
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_REPO_ENTITY_NOT_EXISTS.getKey())));
}
@Test