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:
committed by
GitHub
parent
b25e118e6c
commit
825cb64448
@@ -830,6 +830,54 @@ include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
|
||||
== POST /rest/v1/distributionsets/{distributionSetId}/invalidate
|
||||
|
||||
|
||||
=== Implementation Notes
|
||||
|
||||
Invalidate a distribution set. Once a distribution set is invalidated, it can not be valid again. An invalidated distribution set cannot be assigned to targets anymore. The distribution set that is going to be invalidated will be removed from all auto assignments. Furthermore, the user can choose to cancel all rollouts and (force) cancel all actions connected to this distribution set. Required permission: UPDATE_REPOSITORY
|
||||
|
||||
=== Invalidate a distribution set
|
||||
|
||||
==== Curl
|
||||
|
||||
include::{snippets}/distributionsets/invalidate/curl-request.adoc[]
|
||||
|
||||
==== Request URL
|
||||
|
||||
include::{snippets}/distributionsets/invalidate/http-request.adoc[]
|
||||
|
||||
==== Request path parameter
|
||||
|
||||
include::{snippets}/distributionsets/invalidate/path-parameters.adoc[]
|
||||
|
||||
==== Request fields
|
||||
|
||||
include::{snippets}/distributionsets/invalidate/request-fields.adoc[]
|
||||
|
||||
=== Response (Status 200)
|
||||
|
||||
==== Response example
|
||||
|
||||
include::{snippets}/distributionsets/invalidate/http-response.adoc[]
|
||||
|
||||
=== Error responses
|
||||
|
||||
|===
|
||||
| HTTP Status Code | Reason | Response Model
|
||||
|
||||
include::../errors/400.adoc[]
|
||||
include::../errors/401.adoc[]
|
||||
include::../errors/403.adoc[]
|
||||
include::../errors/404.adoc[]
|
||||
include::../errors/405.adoc[]
|
||||
include::../errors/406.adoc[]
|
||||
include::../errors/409.adoc[]
|
||||
include::../errors/415.adoc[]
|
||||
include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
|
||||
== Additional content
|
||||
|
||||
[[error-body]]
|
||||
|
||||
@@ -89,8 +89,9 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
|
||||
protected String host = "management-api.host";
|
||||
|
||||
/**
|
||||
* The generated REST docs snippets will be outputted to an own resource folder.
|
||||
* The child class has to specify the name of that output folder where to put its corresponding snippets.
|
||||
* The generated REST docs snippets will be outputted to an own resource
|
||||
* folder. The child class has to specify the name of that output folder
|
||||
* where to put its corresponding snippets.
|
||||
*
|
||||
* @return the name of the resource folder
|
||||
*/
|
||||
@@ -101,8 +102,8 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
|
||||
this.document = document(getResourceName() + "/{method-name}", preprocessRequest(prettyPrint()),
|
||||
preprocessResponse(prettyPrint()));
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
|
||||
.apply(MockMvcRestDocumentation.documentationConfiguration(restDocContext).uris()
|
||||
.withScheme("https").withHost(host + ".com").withPort(443))
|
||||
.apply(MockMvcRestDocumentation.documentationConfiguration(restDocContext).uris().withScheme("https")
|
||||
.withHost(host + ".com").withPort(443))
|
||||
.alwaysDo(this.document).addFilter(filterHttpResponse).build();
|
||||
arrayPrefix = "[]";
|
||||
}
|
||||
@@ -163,33 +164,36 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
|
||||
|
||||
protected Target createTargetByGivenNameWithAttributes(final String name, final boolean inSync,
|
||||
final boolean timeforced, final DistributionSet distributionSet) {
|
||||
return createTargetByGivenNameWithAttributes(name, inSync, timeforced, distributionSet, null, null, null, false);
|
||||
return createTargetByGivenNameWithAttributes(name, inSync, timeforced, distributionSet, null, null, null,
|
||||
false);
|
||||
}
|
||||
|
||||
protected Target createTargetByGivenNameWithAttributes(final String name, final boolean inSync,
|
||||
final boolean timeforced, final DistributionSet distributionSet, final boolean createRollout) {
|
||||
return createTargetByGivenNameWithAttributes(name, inSync, timeforced, distributionSet, null, null, null, createRollout);
|
||||
return createTargetByGivenNameWithAttributes(name, inSync, timeforced, distributionSet, null, null, null,
|
||||
createRollout);
|
||||
}
|
||||
|
||||
protected Target createTargetByGivenNameWithAttributes(final String name, final boolean inSync,
|
||||
final boolean timeforced, final DistributionSet distributionSet, final String maintenanceWindowSchedule,
|
||||
final String maintenanceWindowDuration, final String maintenanceWindowTimeZone, final boolean createRollout) {
|
||||
final String maintenanceWindowDuration, final String maintenanceWindowTimeZone,
|
||||
final boolean createRollout) {
|
||||
|
||||
final Target savedTarget = targetManagement.create(entityFactory.target().create().controllerId(name)
|
||||
.status(TargetUpdateStatus.UNKNOWN).address("http://192.168.0.1").description("My name is " + name)
|
||||
.lastTargetQuery(System.currentTimeMillis()));
|
||||
|
||||
|
||||
final List<Target> updatedTargets;
|
||||
if (createRollout) {
|
||||
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables("rollout", "rollout desc", 1,
|
||||
"name==" + name, distributionSet, "50", "5", timeforced ? ActionType.TIMEFORCED : ActionType.FORCED,
|
||||
isMultiAssignmentsEnabled() ? 600 : null);
|
||||
|
||||
|
||||
// start the rollout and handle it
|
||||
rolloutManagement.start(rollout.getId());
|
||||
rolloutManagement.handleRollouts();
|
||||
|
||||
|
||||
updatedTargets = Collections.singletonList(savedTarget);
|
||||
|
||||
} else {
|
||||
@@ -208,7 +212,7 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
|
||||
updatedTargets = makeAssignment(deploymentRequestBuilder.build()).getAssignedEntity().stream()
|
||||
.map(Action::getTarget).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
if (inSync) {
|
||||
feedbackToByInSync(distributionSet);
|
||||
}
|
||||
@@ -318,6 +322,7 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
|
||||
.description(MgmtApiModelProperties.DS_REQUIRED_STEP),
|
||||
fieldWithPath(arrayPrefix + "complete").description(MgmtApiModelProperties.DS_COMPLETE),
|
||||
fieldWithPath(arrayPrefix + "deleted").description(ApiModelPropertiesGeneric.DELETED),
|
||||
fieldWithPath(arrayPrefix + "valid").description(MgmtApiModelProperties.DS_VALID),
|
||||
fieldWithPath(arrayPrefix + "version").description(MgmtApiModelProperties.VERSION),
|
||||
fieldWithPath(arrayPrefix + "_links.self").ignored(), fieldWithPath(arrayPrefix + "modules").ignored());
|
||||
|
||||
|
||||
@@ -67,6 +67,8 @@ public final class MgmtApiModelProperties {
|
||||
public static final String DS_ALREADY_ASSIGNED_TARGETS = "Targets that had this distribution set already assigned (in \"offline\" case this includes targets that have arbitrary updates running)";
|
||||
public static final String DS_TOTAL_ASSIGNED_TARGETS = "Overall assigned as part of this request.";
|
||||
public static final String DS_ID = "Id of the distribution set.";
|
||||
public static final String DS_INVALIDATION_ACTION_CANCELATION_TYPE = "Type of cancelation for actions referring to the given distribution set.";
|
||||
public static final String DS_INVALIDATION_CANCEL_ROLLOUTS = "Defines if rollouts referring to this distribution set should be canceled.";
|
||||
|
||||
// Target
|
||||
public static final String INSTALLED_AT = "Installation time of current installed DistributionSet.";
|
||||
@@ -184,6 +186,8 @@ public final class MgmtApiModelProperties {
|
||||
|
||||
public static final String DS_COMPLETE = "True of the distribution set software module setup is complete as defined by the distribution set type.";
|
||||
|
||||
public static final String DS_VALID = "True by default and false after the distribution set is invalidated by the user.";
|
||||
|
||||
public static final String DS_TYPE_MANDATORY_MODULES = "Mandatory module type IDs.";
|
||||
|
||||
public static final String DS_TYPE_OPTIONAL_MODULES = "Optional module type IDs.";
|
||||
|
||||
@@ -379,8 +379,7 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
|
||||
parameterWithName("distributionSetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
|
||||
requestParameters(parameterWithName("offline")
|
||||
.description(MgmtApiModelProperties.OFFLINE_UPDATE).optional()),
|
||||
requestFields(
|
||||
requestFieldWithPath("[].id").description(ApiModelPropertiesGeneric.ITEM_ID),
|
||||
requestFields(requestFieldWithPath("[].id").description(ApiModelPropertiesGeneric.ITEM_ID),
|
||||
requestFieldWithPathMandatoryInMultiAssignMode("[].weight")
|
||||
.description(MgmtApiModelProperties.ASSIGNMENT_WEIGHT)
|
||||
.type(JsonFieldType.NUMBER).attributes(key("value").value("0 - 1000")),
|
||||
@@ -395,8 +394,8 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
|
||||
optionalRequestFieldWithPath("[].maintenanceWindow.timezone")
|
||||
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_TIMEZONE),
|
||||
optionalRequestFieldWithPath("[].type")
|
||||
.description(MgmtApiModelProperties.ASSIGNMENT_TYPE)
|
||||
.attributes(key("value").value("['soft', 'forced','timeforced', 'downloadonly']"))),
|
||||
.description(MgmtApiModelProperties.ASSIGNMENT_TYPE).attributes(
|
||||
key("value").value("['soft', 'forced','timeforced', 'downloadonly']"))),
|
||||
responseFields(
|
||||
fieldWithPath("assigned").description(MgmtApiModelProperties.DS_NEW_ASSIGNED_TARGETS),
|
||||
fieldWithPath("alreadyAssigned").type(JsonFieldType.NUMBER)
|
||||
@@ -445,8 +444,7 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
|
||||
mockMvc.perform(delete(
|
||||
MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING
|
||||
+ "/{distributionSetId}/assignedSM/{softwareModuleId}",
|
||||
set.getId(), set.findFirstModuleByType(osType).get().getId())
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
set.getId(), set.findFirstModuleByType(osType).get().getId()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andDo(this.document.document(pathParameters(
|
||||
parameterWithName("distributionSetId").description(ApiModelPropertiesGeneric.ITEM_ID),
|
||||
@@ -667,4 +665,28 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
|
||||
optionalRequestFieldWithPath("[]value")
|
||||
.description(MgmtApiModelProperties.META_DATA_VALUE))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Invalidates a distribution set. Required Permission: " + SpPermission.UPDATE_REPOSITORY)
|
||||
public void invalidate() throws Exception {
|
||||
final DistributionSet testDS = testdataFactory.createDistributionSet();
|
||||
|
||||
final JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("actionCancelationType", "soft");
|
||||
jsonObject.put("cancelRollouts", true);
|
||||
|
||||
mockMvc.perform(post(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/invalidate",
|
||||
testDS.getId()).content(jsonObject.toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andDo(
|
||||
this.document.document(
|
||||
pathParameters(parameterWithName("distributionSetId")
|
||||
.description(ApiModelPropertiesGeneric.ITEM_ID)),
|
||||
requestFields(
|
||||
requestFieldWithPath("actionCancelationType")
|
||||
.description(
|
||||
MgmtApiModelProperties.DS_INVALIDATION_ACTION_CANCELATION_TYPE)
|
||||
.attributes(key("value").value("['force','soft','none']")),
|
||||
optionalRequestFieldWithPath("cancelRollouts")
|
||||
.description(MgmtApiModelProperties.DS_INVALIDATION_CANCEL_ROLLOUTS))));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user