From a436426304227411a26aeb70b2ca727ab8e7dae2 Mon Sep 17 00:00:00 2001 From: waynedunican Date: Mon, 16 Jun 2025 18:35:24 +0100 Subject: [PATCH] Add rollback support - Add MIGRATION_ROLLBAKING enum in DeployState - Add new endpoint for rollback - Update migration messages for rollback - Add validation in resolver class - Add business logic Issue-ID: POLICY-5362 Change-Id: I936d6dd6b98fab13ad4548d2f994cc8313374d3f Signed-off-by: waynedunican --- .../clamp/models/acm/concepts/DeployState.java | 5 +- .../AutomationCompositionMigration.java | 3 +- .../provider/AcInstanceStateResolver.java | 7 +- .../provider/AutomationCompositionProvider.java | 14 +++ .../provider/AcInstanceStateResolverTest.java | 10 ++ .../AutomationCompositionProviderTest.java | 27 +++++ .../handler/AutomationCompositionHandlerTest.java | 5 +- ...AutomationCompositionInstantiationProvider.java | 31 ++++++ .../runtime/main/rest/InstantiationController.java | 8 +- .../rest/stub/InstantiationControllerStub.java | 5 + .../src/main/resources/openapi/openapi.yaml | 121 ++++++++++++++++++++- .../contract/InstantiationControllerStubTest.java | 12 ++ ...mationCompositionInstantiationProviderTest.java | 96 ++++++++++++++++ 13 files changed, 337 insertions(+), 7 deletions(-) diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/DeployState.java b/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/DeployState.java index 57669fe77..6b5953556 100644 --- a/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/DeployState.java +++ b/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/DeployState.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2023 Nordix Foundation. + * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,5 +28,6 @@ public enum DeployState { DELETING, DELETED, UPDATING, - MIGRATING + MIGRATING, + MIGRATION_REVERTING } diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/messages/kafka/participant/AutomationCompositionMigration.java b/models/src/main/java/org/onap/policy/clamp/models/acm/messages/kafka/participant/AutomationCompositionMigration.java index 2d7608afd..ee05a9521 100644 --- a/models/src/main/java/org/onap/policy/clamp/models/acm/messages/kafka/participant/AutomationCompositionMigration.java +++ b/models/src/main/java/org/onap/policy/clamp/models/acm/messages/kafka/participant/AutomationCompositionMigration.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2023-2024 Nordix Foundation. + * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,6 +38,7 @@ public class AutomationCompositionMigration extends ParticipantMessage { private List participantUpdatesList = new ArrayList<>(); private Boolean precheck = false; + private Boolean rollback = false; private Integer stage = 0; public AutomationCompositionMigration() { diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AcInstanceStateResolver.java b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AcInstanceStateResolver.java index 27c0d4072..defa804ca 100644 --- a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AcInstanceStateResolver.java +++ b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AcInstanceStateResolver.java @@ -42,6 +42,7 @@ public class AcInstanceStateResolver { private static final String DELETING = DeployState.DELETING.name(); private static final String MIGRATING = DeployState.MIGRATING.name(); private static final String MIGRATION_PRECHECKING = SubState.MIGRATION_PRECHECKING.name(); + private static final String MIGRATION_REVERTING = DeployState.MIGRATION_REVERTING.name(); private static final String SUB_STATE_NONE = SubState.NONE.name(); private static final String PREPARING = SubState.PREPARING.name(); private static final String REVIEWING = SubState.REVIEWING.name(); @@ -126,6 +127,10 @@ public class AcInstanceStateResolver { // review order in a failed or timeout scenario addSubOrderWithFail(REVIEW, DEPLOYED, LOCKED, REVIEWING); + + // rollback + addDeployOrderWithFail(MIGRATION_REVERTING, MIGRATING, LOCKED, SUB_STATE_NONE); + addDeployOrderWithFail(UNDEPLOY, MIGRATION_REVERTING, LOCKED, SUB_STATE_NONE); } private void addDeployOrder(String deployOrder, String deployState, String lockState) { @@ -177,7 +182,7 @@ public class AcInstanceStateResolver { * @param acDeployOrder the Deploy Ordered * @param acLockOrder the Lock Ordered * @param acSubOrder the Sub Ordered - * @param acDeployState then current Deploy State + * @param acDeployState the current Deploy State * @param acLockState the current Lock State * @param acSubState the current Sub State * @param acStateChangeResult the current Result of the State Change diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java index 5d2c4f26c..b5f5efd3d 100644 --- a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java +++ b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProvider.java @@ -266,4 +266,18 @@ public class AutomationCompositionProvider { acRollbackRepository.save(jpaCopy); acRollbackRepository.flush(); } + + /** + * Get the copied automation composition from the RollbackRepository. + * + * @param instanceId the id of the ac instance + * @return the acRollback object + */ + public JpaAutomationCompositionRollback getAutomationCompositionRollback(String instanceId) { + var result = acRollbackRepository.findById(instanceId); + if (result.isEmpty()) { + throw new PfModelRuntimeException(Status.NOT_FOUND, "Instance not found for rollback"); + } + return result.get(); + } } diff --git a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AcInstanceStateResolverTest.java b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AcInstanceStateResolverTest.java index a807a1179..c9c4f0dd4 100644 --- a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AcInstanceStateResolverTest.java +++ b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AcInstanceStateResolverTest.java @@ -75,6 +75,16 @@ class AcInstanceStateResolverTest { result = acTypeStateResolver.resolve(DeployOrder.NONE, LockOrder.NONE, SubOrder.REVIEW, DeployState.DEPLOYED, LockState.LOCKED, SubState.NONE, StateChangeResult.NO_ERROR); assertThat(result).isEqualTo(AcInstanceStateResolver.REVIEW); + + // rollback + result = acTypeStateResolver.resolve(DeployOrder.MIGRATE, LockOrder.NONE, SubOrder.REVIEW, + DeployState.MIGRATION_REVERTING, LockState.LOCKED, SubState.NONE, StateChangeResult.NO_ERROR); + assertThat(result).isEqualTo(AcInstanceStateResolver.NONE); + + result = acTypeStateResolver.resolve(DeployOrder.UNDEPLOY, LockOrder.NONE, SubOrder.NONE, + DeployState.MIGRATION_REVERTING, LockState.LOCKED, SubState.NONE, StateChangeResult.FAILED); + assertThat(result).isEqualTo(AcInstanceStateResolver.UNDEPLOY); + } @Test diff --git a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java index 71134768c..dd24833ca 100644 --- a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java +++ b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java @@ -23,8 +23,11 @@ package org.onap.policy.clamp.models.acm.persistence.provider; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyIterable; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -48,6 +51,7 @@ import org.onap.policy.clamp.models.acm.persistence.repository.AutomationComposi import org.onap.policy.common.utils.coder.Coder; import org.onap.policy.common.utils.coder.StandardCoder; import org.onap.policy.common.utils.resources.ResourceUtils; +import org.onap.policy.models.base.PfModelRuntimeException; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -264,5 +268,28 @@ class AutomationCompositionProviderTest { automationCompositionProvider.copyAcElementsBeforeUpdate(ac); verify(acRollbackRepository).save(any(JpaAutomationCompositionRollback.class)); + assertThrows(PfModelRuntimeException.class, () -> automationCompositionProvider // NOSONAR + .getAutomationCompositionRollback(ac.getInstanceId().toString())); + } + + @Test + void testGetRollbackSuccess() { + var ac = inputAutomationCompositions.getAutomationCompositionList().get(0); + var rollback = new JpaAutomationCompositionRollback(); + rollback.setInstanceId(ac.getInstanceId().toString()); + rollback.setCompositionId(ac.getCompositionId().toString()); + + when(acRollbackRepository.findById(anyString())).thenReturn(Optional.of(rollback)); + + var rbFromDb = automationCompositionProvider.getAutomationCompositionRollback(ac.getInstanceId() + .toString()); + assertNotNull(rbFromDb); + } + + @Test + void testGetRollbackEmpty() { + when(acRollbackRepository.findById(anyString())).thenReturn(Optional.empty()); + assertThrows(PfModelRuntimeException.class, () -> automationCompositionProvider + .getAutomationCompositionRollback("empty")); } } diff --git a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandlerTest.java b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandlerTest.java index b301ef1e8..b60737246 100644 --- a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandlerTest.java +++ b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandlerTest.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2021-2024 Nordix Foundation. + * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -225,6 +225,8 @@ class AutomationCompositionHandlerTest { migrationMsg.setAutomationCompositionId(UUID.randomUUID()); migrationMsg.setCompositionTargetId(UUID.randomUUID()); assertDoesNotThrow(() -> ach.handleAutomationCompositionMigration(migrationMsg)); + migrationMsg.setRollback(true); + assertDoesNotThrow(() -> ach.handleAutomationCompositionMigration(migrationMsg)); } @Test @@ -347,6 +349,7 @@ class AutomationCompositionHandlerTest { migrationMsg.setCompositionId(acMigrate.getCompositionId()); migrationMsg.setAutomationCompositionId(acMigrate.getInstanceId()); migrationMsg.setCompositionTargetId(acMigrate.getCompositionTargetId()); + migrationMsg.setRollback(false); var participantMigrate = CommonTestData.createparticipantDeploy(cacheProvider.getParticipantId(), acMigrate); migrationMsg.setParticipantUpdatesList(List.of(participantMigrate)); var listener = mock(ThreadHandler.class); diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java index 59373e89b..14660a812 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java @@ -23,7 +23,9 @@ package org.onap.policy.clamp.acm.runtime.instantiation; import jakarta.validation.Valid; import jakarta.ws.rs.core.Response.Status; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; import lombok.NonNull; @@ -34,6 +36,7 @@ import org.onap.policy.clamp.acm.runtime.supervision.SupervisionAcHandler; import org.onap.policy.clamp.models.acm.concepts.AcTypeState; import org.onap.policy.clamp.models.acm.concepts.AutomationComposition; import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition; +import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement; import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions; import org.onap.policy.clamp.models.acm.concepts.DeployState; import org.onap.policy.clamp.models.acm.concepts.LockState; @@ -456,6 +459,34 @@ public class AutomationCompositionInstantiationProvider { } } + /** + * Rollback AC Instance. + * + * @param instanceId the instanceId + * @return the instantiation response + */ + public InstantiationResponse rollback(UUID instanceId) { + var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId); + var automationCompositionToRollback = + automationCompositionProvider.getAutomationCompositionRollback(instanceId.toString()); + + if (DeployState.DEPLOYED.equals(automationComposition.getDeployState()) + && SubState.NONE.equals(automationComposition.getSubState()) + && StateChangeResult.NO_ERROR.equals(automationComposition.getStateChangeResult())) { + automationComposition.setCompositionId(UUID.fromString(automationCompositionToRollback.getCompositionId())); + Map elements = new HashMap<>(); + automationCompositionToRollback.getElements().forEach((String uuid, Object acElement) -> + elements.put(UUID.fromString(uuid), (AutomationCompositionElement) acElement)); + automationComposition.setElements(elements); + automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR); + AcmUtils.setCascadedState(automationComposition, DeployState.MIGRATION_REVERTING, LockState.LOCKED); + automationCompositionProvider.updateAutomationComposition(automationComposition); + return createInstantiationResponse(automationComposition); + } else { + throw new PfModelRuntimeException(Status.BAD_REQUEST, "Invalid state for rollback"); + } + } + private List updateElementsProperties(AutomationComposition automationComposition, AutomationComposition acToBeUpdated) { for (var element : automationComposition.getElements().entrySet()) { diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/InstantiationController.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/InstantiationController.java index 386241d02..f20f3f958 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/InstantiationController.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/InstantiationController.java @@ -36,7 +36,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RestController; /** - * Class to provide REST end points for creating, deleting, query and commanding a automation composition definition. + * Class to provide REST end points for creating, deleting, query and commanding an automation composition definition. */ @RestController @RequiredArgsConstructor @@ -119,4 +119,10 @@ public class InstantiationController extends AbstractRestController implements A provider.compositionInstanceState(compositionId, instanceId, body); return ResponseEntity.accepted().build(); } + + @Override + public ResponseEntity rollbackCompositionInstance(UUID instanceId) { + provider.rollback(instanceId); + return ResponseEntity.accepted().build(); + } } diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/stub/InstantiationControllerStub.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/stub/InstantiationControllerStub.java index bbcfddca8..d22fbfea8 100644 --- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/stub/InstantiationControllerStub.java +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/main/rest/stub/InstantiationControllerStub.java @@ -90,4 +90,9 @@ public class InstantiationControllerStub extends AbstractRestController implemen @Valid AcInstanceStateUpdate body, UUID requestId) { return new ResponseEntity<>(HttpStatus.ACCEPTED); } + + @Override + public ResponseEntity rollbackCompositionInstance(UUID instanceId) { + return new ResponseEntity<>(HttpStatus.ACCEPTED); + } } diff --git a/runtime-acm/src/main/resources/openapi/openapi.yaml b/runtime-acm/src/main/resources/openapi/openapi.yaml index 2bbfe3a83..7b60a89c1 100644 --- a/runtime-acm/src/main/resources/openapi/openapi.yaml +++ b/runtime-acm/src/main/resources/openapi/openapi.yaml @@ -1615,7 +1615,126 @@ paths: x-interface info: api-version: 8.2.1 last-mod-release: Paris - + /compositions/{compositionId}/instances/{instanceId}/rollback: + post: + tags: + - Automation Composition Instance + summary: Rollback an automation composition instance to a previous state + description: Triggers a rollback of the specified instance. + operationId: rollbackCompositionInstance + parameters: + - name: instanceId + in: path + description: The UUID of the automation composition definition on which to rollback + required: true + schema: + type: string + format: uuid + requestBody: + description: Parameters for the rollback (e.g., target version) + required: true + content: + application/json: + schema: + application/yaml: + schema: + responses: + 202: + description: Rollback initiated + headers: + X-LatestVersion: + $ref: '#/components/headers/X-LatestVersion' + X-PatchVersion: + $ref: '#/components/headers/X-PatchVersion' + X-MinorVersion: + $ref: '#/components/headers/X-MinorVersion' + X-onap-RequestId: + $ref: '#/components/headers/X-onap-RequestId' + 400: + description: Bad request + headers: + X-LatestVersion: + $ref: '#/components/headers/X-LatestVersion' + X-PatchVersion: + $ref: '#/components/headers/X-PatchVersion' + X-MinorVersion: + $ref: '#/components/headers/X-MinorVersion' + X-onap-RequestId: + $ref: '#/components/headers/X-onap-RequestId' + content: + application/json: + schema: + $ref: '#/components/schemas/SimpleResponse' + 401: + description: Authentication Error, returns an instance of + [SimpleResponse](https://github.com/onap/policy-clamp/blob/master/models/src/main/java/org/onap/policy/clamp/models/acm/messages/rest/SimpleResponse.java) + headers: + X-LatestVersion: + $ref: '#/components/headers/X-LatestVersion' + X-PatchVersion: + $ref: '#/components/headers/X-PatchVersion' + X-MinorVersion: + $ref: '#/components/headers/X-MinorVersion' + X-onap-RequestId: + $ref: '#/components/headers/X-onap-RequestId' + content: + application/json: + schema: + $ref: '#/components/schemas/SimpleResponse' + 404: + description: The specified automation composition definition was not found, returns an instance of + [SimpleResponse](https://github.com/onap/policy-clamp/blob/master/models/src/main/java/org/onap/policy/clamp/models/acm/messages/rest/SimpleResponse.java) + headers: + X-LatestVersion: + $ref: '#/components/headers/X-LatestVersion' + X-PatchVersion: + $ref: '#/components/headers/X-PatchVersion' + X-MinorVersion: + $ref: '#/components/headers/X-MinorVersion' + X-onap-RequestId: + $ref: '#/components/headers/X-onap-RequestId' + content: + application/json: + schema: + $ref: '#/components/schemas/SimpleResponse' + 400: + description: Bad Request, returns an instance of + [SimpleResponse](https://github.com/onap/policy-clamp/blob/master/models/src/main/java/org/onap/policy/clamp/models/acm/messages/rest/SimpleResponse.java) + headers: + X-LatestVersion: + $ref: '#/components/headers/X-LatestVersion' + X-PatchVersion: + $ref: '#/components/headers/X-PatchVersion' + X-MinorVersion: + $ref: '#/components/headers/X-MinorVersion' + X-onap-RequestId: + $ref: '#/components/headers/X-onap-RequestId' + content: + application/json: + schema: + $ref: '#/components/schemas/SimpleResponse' + 500: + description: Internal Server Error, returns an instance of + [SimpleResponse](https://github.com/onap/policy-clamp/blob/master/models/src/main/java/org/onap/policy/clamp/models/acm/messages/rest/SimpleResponse.java) + headers: + X-LatestVersion: + $ref: '#/components/headers/X-LatestVersion' + X-PatchVersion: + $ref: '#/components/headers/X-PatchVersion' + X-MinorVersion: + $ref: '#/components/headers/X-MinorVersion' + X-onap-RequestId: + $ref: '#/components/headers/X-onap-RequestId' + content: + application/json: + schema: + $ref: '#/components/schemas/SimpleResponse' + security: + - basicAuth: [ ] + x-interface info: + api-version: 8.2.1 + last-mod-release: Paris + x-codegen-request-body-name: body components: securitySchemes: basicAuth: diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/contract/InstantiationControllerStubTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/contract/InstantiationControllerStubTest.java index 52dbcf8ac..f64cbbadb 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/contract/InstantiationControllerStubTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/contract/InstantiationControllerStubTest.java @@ -45,6 +45,7 @@ class InstantiationControllerStubTest extends CommonRestController { private static final String INSTANTIATION_ENDPOINT = "instances"; private static final String COMPOSITION_ID = "1aeed185-a98b-45b6-af22-8d5d20485ea3"; private static final String INSTANCE_ID = "709c62b3-8918-41b9-a747-d21eb79c6c23"; + private static final String ROLLBACK = "rollback"; @LocalServerPort private int randomServerPort; @@ -101,4 +102,15 @@ class InstantiationControllerStubTest extends CommonRestController { var respPost = invocationBuilder.delete(); assertThat(Response.Status.OK.getStatusCode()).isEqualTo(respPost.getStatus()); } + + @Test + void testRollback() { + var invocationBuilder = super.sendRequest(COMMISSIONING_ENDPOINT + + "/" + COMPOSITION_ID + + "/" + INSTANTIATION_ENDPOINT + + "/" + INSTANCE_ID + + "/" + ROLLBACK); + var respPost = invocationBuilder.post(Entity.json(new AcInstanceStateUpdate())); + assertThat(Response.Status.ACCEPTED.getStatusCode()).isEqualTo(respPost.getStatus()); + } } diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java index bfaa132f3..d9ada4bf1 100644 --- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java @@ -24,9 +24,13 @@ package org.onap.policy.clamp.acm.runtime.instantiation; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.onap.policy.clamp.acm.runtime.util.CommonTestData.TOSCA_SERVICE_TEMPLATE_YAML; @@ -54,6 +58,7 @@ import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceSt import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder; import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder; import org.onap.policy.clamp.models.acm.messages.rest.instantiation.SubOrder; +import org.onap.policy.clamp.models.acm.persistence.concepts.JpaAutomationCompositionRollback; import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider; import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver; import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider; @@ -61,6 +66,7 @@ import org.onap.policy.clamp.models.acm.persistence.provider.ParticipantProvider import org.onap.policy.clamp.models.acm.persistence.provider.ProviderUtils; import org.onap.policy.clamp.models.acm.utils.AcmUtils; import org.onap.policy.models.base.PfConceptKey; +import org.onap.policy.models.base.PfModelRuntimeException; import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; import org.onap.policy.models.tosca.simple.concepts.JpaToscaServiceTemplate; @@ -562,6 +568,96 @@ class AutomationCompositionInstantiationProviderTest { assertThatDeleteThrownBy(automationComposition, DeployState.DELETING, LockState.NONE); } + @Test + void testRollbackFailure() { + var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED); + var compositionId = acDefinition.getCompositionId(); + var automationComposition = + InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Rollback"); + automationComposition.setCompositionId(compositionId); + automationComposition.setInstanceId(UUID.randomUUID()); + automationComposition.setDeployState(DeployState.MIGRATION_REVERTING); + automationComposition.setCompositionTargetId(UUID.randomUUID()); + + var acProvider = mock(AutomationCompositionProvider.class); + when(acProvider.getAutomationComposition(automationComposition.getInstanceId())) + .thenReturn(automationComposition); + var rollbackRecord = mock(JpaAutomationCompositionRollback.class); + when(acProvider.getAutomationCompositionRollback(anyString())).thenReturn(rollbackRecord); + + final var acDefinitionProvider = mock(AcDefinitionProvider.class); + final var supervisionAcHandler = mock(SupervisionAcHandler.class); + final var participantProvider = mock(ParticipantProvider.class); + final var encryptionUtils = new EncryptionUtils(CommonTestData.getTestParamaterGroup()); + var instantiationProvider = new AutomationCompositionInstantiationProvider(acProvider, acDefinitionProvider, + new AcInstanceStateResolver(), supervisionAcHandler, participantProvider, new AcRuntimeParameterGroup(), + encryptionUtils); + + assertThrows(PfModelRuntimeException.class, () -> instantiationProvider + .rollback(automationComposition.getInstanceId())); + + // DeployState != MIGRATION_REVERTING + when(acProvider.getAutomationComposition(automationComposition.getInstanceId())) + .thenReturn(automationComposition); + when(acProvider.getAutomationCompositionRollback(anyString())).thenReturn(rollbackRecord); + + automationComposition.setDeployState(DeployState.DELETING); + assertThrows(PfModelRuntimeException.class, () -> instantiationProvider + .rollback(automationComposition.getInstanceId())); + + // SubState != NONE + automationComposition.setDeployState(DeployState.DEPLOYED); + automationComposition.setSubState(SubState.PREPARING); + assertThrows(PfModelRuntimeException.class, () -> instantiationProvider + .rollback(automationComposition.getInstanceId())); + + // StateChangeResult != NO_ERROR + automationComposition.setSubState(SubState.NONE); + automationComposition.setStateChangeResult(StateChangeResult.FAILED); + assertThrows(PfModelRuntimeException.class, () -> instantiationProvider + .rollback(automationComposition.getInstanceId())); + + verify(acProvider, never()).updateAutomationComposition(any()); + } + + @Test + void testRollbackSuccess() { + final var acDefinitionProvider = mock(AcDefinitionProvider.class); + final var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED); + final var compositionId = acDefinition.getCompositionId(); + + var automationComposition = + InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Rollback"); + automationComposition.setInstanceId(UUID.randomUUID()); + automationComposition.setCompositionId(compositionId); + automationComposition.setDeployState(DeployState.DEPLOYED); + automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR); + automationComposition.setSubState(SubState.NONE); + automationComposition.setCompositionTargetId(UUID.randomUUID()); + + var acProvider = mock(AutomationCompositionProvider.class); + when(acProvider.getAutomationComposition(automationComposition.getInstanceId())) + .thenReturn(automationComposition); + final var supervisionAcHandler = mock(SupervisionAcHandler.class); + final var participantProvider = mock(ParticipantProvider.class); + final var encryptionUtils = new EncryptionUtils(CommonTestData.getTestParamaterGroup()); + final var instantiationProvider = new AutomationCompositionInstantiationProvider(acProvider, + acDefinitionProvider, new AcInstanceStateResolver(), supervisionAcHandler, + participantProvider, new AcRuntimeParameterGroup(), encryptionUtils); + + var rollbackRecord = new JpaAutomationCompositionRollback(); + rollbackRecord.setCompositionId(automationComposition.getCompositionId().toString()); + + when(acProvider.getAutomationComposition(automationComposition.getInstanceId())) + .thenReturn(automationComposition); + when(acProvider.getAutomationCompositionRollback(anyString())).thenReturn(rollbackRecord); + + instantiationProvider.rollback(automationComposition.getInstanceId()); + + verify(acProvider).updateAutomationComposition(automationComposition); + assertEquals(DeployState.MIGRATION_REVERTING, automationComposition.getDeployState()); + } + private void assertThatDeleteThrownBy(AutomationComposition automationComposition, DeployState deployState, LockState lockState) { automationComposition.setDeployState(deployState); -- 2.16.6