Add rollback support to update data in memory for participant intermediary 69/141369/7
authorakenihan <adam.kenihan@est.tech>
Tue, 17 Jun 2025 09:30:45 +0000 (10:30 +0100)
committerakenihan <adam.kenihan@est.tech>
Wed, 25 Jun 2025 09:18:20 +0000 (10:18 +0100)
Issue-ID: POLICY-5395
Change-Id: I8db012b00112d61b51e6627a6ffb74fe1ce23833
Signed-off-by: akenihan <adam.kenihan@est.tech>
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandler.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandlerTest.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandlerTest.java

index 920f39f..b281ece 100644 (file)
@@ -307,8 +307,13 @@ public class AutomationCompositionHandler {
                 migrateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(),
                         migrationMsg.getCompositionTargetId(), participantDeploy, migrationMsg.getStage());
 
-                callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
-                    acCopy, migrationMsg.getCompositionTargetId(), migrationMsg.getStage());
+                if (Boolean.TRUE.equals(migrationMsg.getRollback())) {
+                    callParticipantRollback(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
+                            acCopy, migrationMsg.getCompositionTargetId(), migrationMsg.getStage());
+                } else {
+                    callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
+                            acCopy, migrationMsg.getCompositionTargetId(), migrationMsg.getStage());
+                }
             }
         }
     }
@@ -339,13 +344,13 @@ public class AutomationCompositionHandler {
                     var instanceElementMigrateDto = CacheProvider
                             .changeStateToNew(instanceElementMigrateMap.get(acElement.getId()));
 
-                    listener.migrate(messageId, compositionElementDto, compositionElementTargetDto, instanceElementDto,
-                            instanceElementMigrateDto, stage);
+                    listener.migrate(messageId, compositionElementDto, compositionElementTargetDto,
+                            instanceElementDto, instanceElementMigrateDto, stage);
                 } else {
                     listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
                             compositionElementTargetMap.get(acElement.getId()),
-                            instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()),
-                            stage);
+                            instanceElementMap.get(acElement.getId()), instanceElementMigrateMap
+                                    .get(acElement.getId()), stage);
                 }
             }
         }
@@ -358,10 +363,44 @@ public class AutomationCompositionHandler {
                                 Map.of(), Map.of(), ElementState.REMOVED);
                 var instanceDtoTarget = new InstanceElementDto(acCopy.getInstanceId(), elementId, Map.of(),
                                 Map.of(), ElementState.REMOVED);
-
                 listener.migrate(messageId, compositionElementMap.get(elementId), compositionDtoTarget,
                         instanceElementMap.get(elementId), instanceDtoTarget, 0);
             }
         }
     }
+
+    private void callParticipantRollback(UUID messageId, List<AcElementDeploy> acElements,
+                                         AutomationComposition acCopy, UUID compositionTargetId, int stage) {
+        var compositionElementMap = cacheProvider.getCompositionElementDtoMap(acCopy);
+        var instanceElementMap = cacheProvider.getInstanceElementDtoMap(acCopy);
+
+        for (var acElement : acElements) {
+            var compositionInProperties = cacheProvider
+                    .getCommonProperties(compositionTargetId, acElement.getDefinition());
+            var stageSet = ParticipantUtils.findStageSetMigrate(compositionInProperties);
+            if (stageSet.contains(stage)) {
+                if (instanceElementMap.get(acElement.getId()) == null) {
+                    var compositionElementDto =
+                            new CompositionElementDto(acCopy.getCompositionId(), acElement.getDefinition(),
+                                    Map.of(), Map.of(), ElementState.NOT_PRESENT);
+                    var instanceElementDto = new InstanceElementDto(acCopy.getInstanceId(), acElement.getId(),
+                            Map.of(), Map.of(), ElementState.NOT_PRESENT);
+
+                    listener.rollback(messageId, compositionElementDto,
+                            instanceElementDto, stage);
+                } else {
+                    listener.rollback(messageId, compositionElementMap.get(acElement.getId()),
+                            instanceElementMap.get(acElement.getId()), stage);
+                }
+            }
+        }
+        if (stage == 0) {
+            // Call rollback for removed elements
+            List<UUID> removedElements = findElementsToRemove(acElements, acCopy.getElements());
+            for (var elementId : removedElements) {
+                listener.rollback(messageId, compositionElementMap.get(elementId),
+                        instanceElementMap.get(elementId), 0);
+            }
+        }
+    }
 }
index 590d45f..fa69442 100644 (file)
@@ -434,4 +434,36 @@ public class ThreadHandler implements Closeable {
         }
         executionMap.remove(instanceElement.elementId());
     }
+
+    /**
+     * Handles AutomationComposition Rollback.
+     *
+     * @param messageId the messageId
+     * @param compositionElement the information of the Automation Composition Definition Element
+     * @param instanceElement the information of the Automation Composition Instance Element
+     * @param stage the stage
+     */
+    public void rollback(UUID messageId, CompositionElementDto compositionElement,
+                         InstanceElementDto instanceElement,
+                         int stage) {
+        cleanExecution(instanceElement.elementId(), messageId);
+        var result = executor.submit(() ->
+                this.rollbackProcess(compositionElement,
+                        instanceElement, stage));
+        executionMap.put(instanceElement.elementId(), result);
+    }
+
+    private void rollbackProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement,
+                                 int stage) {
+        try {
+            listener.rollbackMigration(compositionElement, instanceElement, stage);
+        } catch (PfModelException e) {
+            LOGGER.error("Automation composition element rollback failed {} {}",
+                    instanceElement.elementId(), e.getMessage());
+            intermediaryApi.updateAutomationCompositionElementState(
+                    instanceElement.instanceId(), instanceElement.elementId(), DeployState.DEPLOYED,
+                    null, StateChangeResult.FAILED, "Automation composition rollback migrate failed");
+        }
+        executionMap.remove(instanceElement.elementId());
+    }
 }
index b607372..b9a499b 100644 (file)
@@ -220,13 +220,20 @@ class AutomationCompositionHandlerTest {
         var ach = new AutomationCompositionHandler(
                 mock(CacheProvider.class), mock(ParticipantMessagePublisher.class), mock(ThreadHandler.class));
         var migrationMsg = new AutomationCompositionMigration();
+        var rollbackMsg = new AutomationCompositionMigration();
+        rollbackMsg.setRollback(true);
+
         migrationMsg.setStage(0);
         assertDoesNotThrow(() -> ach.handleAutomationCompositionMigration(migrationMsg));
         migrationMsg.setAutomationCompositionId(UUID.randomUUID());
         migrationMsg.setCompositionTargetId(UUID.randomUUID());
         assertDoesNotThrow(() -> ach.handleAutomationCompositionMigration(migrationMsg));
-        migrationMsg.setRollback(true);
-        assertDoesNotThrow(() -> ach.handleAutomationCompositionMigration(migrationMsg));
+
+        rollbackMsg.setStage(0);
+        assertDoesNotThrow(() -> ach.handleAutomationCompositionMigration(rollbackMsg));
+        rollbackMsg.setAutomationCompositionId(UUID.randomUUID());
+        rollbackMsg.setCompositionTargetId(UUID.randomUUID());
+        assertDoesNotThrow(() -> ach.handleAutomationCompositionMigration(rollbackMsg));
     }
 
     @Test
@@ -244,7 +251,14 @@ class AutomationCompositionHandlerTest {
                 automationComposition.getInstanceId(), definitions,
                 automationComposition.getCompositionTargetId(), definitions);
 
-        testMigration(cacheProvider, automationComposition, 0, automationComposition.getElements().size());
+        var cacheProviderRollback = createCacheProvider(participantDeploy, automationComposition.getCompositionId(),
+                automationComposition.getInstanceId(), definitions,
+                automationComposition.getCompositionTargetId(), definitions);
+
+        testMigration(cacheProvider, automationComposition, 0,
+                automationComposition.getElements().size(), false);
+        testMigration(cacheProviderRollback, automationComposition, 0,
+                automationComposition.getElements().size(), true);
     }
 
     @Test
@@ -271,8 +285,12 @@ class AutomationCompositionHandlerTest {
         var cacheProvider = createCacheProvider(participantDeploy, automationComposition.getCompositionId(),
                 automationComposition.getInstanceId(), definitions,
                 acMigrate.getCompositionTargetId(), migrateDefinitions);
+        var cacheProviderRollback = createCacheProvider(participantDeploy, automationComposition.getCompositionId(),
+                automationComposition.getInstanceId(), definitions,
+                acMigrate.getCompositionTargetId(), migrateDefinitions);
 
-        testMigration(cacheProvider, acMigrate, 0, acMigrate.getElements().size() + 1);
+        testMigration(cacheProvider, acMigrate, 0, acMigrate.getElements().size() + 1, false);
+        testMigration(cacheProviderRollback, acMigrate, 0, acMigrate.getElements().size() + 1, true);
     }
 
     @Test
@@ -306,30 +324,41 @@ class AutomationCompositionHandlerTest {
         var cacheProvider = createCacheProvider(participantDeploy, automationComposition.getCompositionId(),
                 automationComposition.getInstanceId(), definitions,
                 acMigrate.getCompositionTargetId(), migrateDefinitions);
+        var cacheProviderRollback = createCacheProvider(participantDeploy, automationComposition.getCompositionId(),
+                automationComposition.getInstanceId(), definitions,
+                acMigrate.getCompositionTargetId(), migrateDefinitions);
+
 
         // scenario 1,2
         migrateDefinitions.forEach(el -> el.getAutomationCompositionElementToscaNodeTemplate()
                 .setProperties(Map.of("stage", List.of(1, 2))));
 
         // expected the element deleted
-        testMigration(cacheProvider, acMigrate, 0, 1);
+        testMigration(cacheProvider, acMigrate, 0, 1, false);
+        testMigration(cacheProviderRollback, acMigrate, 0, 1, true);
 
         // expected 4 elements from stage 1
-        testMigration(cacheProvider, acMigrate, 1, 4);
+        testMigration(cacheProvider, acMigrate, 1, 4, false);
+        testMigration(cacheProviderRollback, acMigrate, 1, 4, true);
 
         // scenario 0,2
         cacheProvider = createCacheProvider(participantDeploy, automationComposition.getCompositionId(),
                 automationComposition.getInstanceId(), definitions,
                 acMigrate.getCompositionTargetId(), migrateDefinitions);
+        cacheProviderRollback = createCacheProvider(participantDeploy, automationComposition.getCompositionId(),
+                automationComposition.getInstanceId(), definitions,
+                acMigrate.getCompositionTargetId(), migrateDefinitions);
 
         migrateDefinitions.forEach(el -> el.getAutomationCompositionElementToscaNodeTemplate()
                 .setProperties(Map.of("stage", List.of(0, 2))));
 
         // expected the element deleted + 4 elements from stage 0
-        testMigration(cacheProvider, acMigrate, 0, 5);
+        testMigration(cacheProvider, acMigrate, 0, 5, false);
+        testMigration(cacheProviderRollback, acMigrate, 0, 5, true);
 
         // expected 0 elements
-        testMigration(cacheProvider, acMigrate, 1, 0);
+        testMigration(cacheProvider, acMigrate, 1, 0, false);
+        testMigration(cacheProviderRollback, acMigrate, 1, 0, true);
     }
 
     private CacheProvider createCacheProvider(ParticipantDeploy participantDeploy,
@@ -343,20 +372,28 @@ class AutomationCompositionHandlerTest {
     }
 
     private void testMigration(CacheProvider cacheProvider, AutomationComposition acMigrate,
-            int stage, int expectedMigrated) {
+            int stage, int expectedMigrated, boolean rollback) {
         var migrationMsg = new AutomationCompositionMigration();
         migrationMsg.setStage(stage);
         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);
 
         clearInvocations();
-        var ach = new AutomationCompositionHandler(cacheProvider, mock(ParticipantMessagePublisher.class), listener);
+        var ach = new AutomationCompositionHandler(cacheProvider,
+                mock(ParticipantMessagePublisher.class), listener);
+
+        clearInvocations();
+        migrationMsg.setRollback(rollback);
         ach.handleAutomationCompositionMigration(migrationMsg);
-        verify(listener, times(expectedMigrated)).migrate(any(), any(), any(), any(), any(), anyInt());
+
+        if (!rollback) {
+            verify(listener, times(expectedMigrated)).migrate(any(), any(), any(), any(), any(), anyInt());
+        } else {
+            verify(listener, times(expectedMigrated)).rollback(any(), any(), any(), anyInt());
+        }
     }
 }
index 957f6e7..f03990a 100644 (file)
@@ -128,6 +128,11 @@ class ThreadHandlerTest {
             verify(listener, timeout(TIMEOUT)).migrate(compositionElement, compositionElementTarget,
                 instanceElement, instanceElementUpdated, 0);
 
+            clearInvocations(listener);
+            threadHandler.rollback(messageId, compositionElement, instanceElement, 0);
+            verify(listener, timeout(TIMEOUT)).rollbackMigration(compositionElement, instanceElement, 0);
+
+
             clearInvocations(listener);
             threadHandler.undeploy(messageId, compositionElement, instanceElement);
             verify(listener, timeout(TIMEOUT)).undeploy(compositionElement, instanceElement);
@@ -330,6 +335,14 @@ class ThreadHandlerTest {
             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
                 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
                 "Automation composition element migrate precheck failed");
+
+            clearInvocations(listener);
+            doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
+                    .rollbackMigration(compositionElement, instanceElement, 0);
+            threadHandler.rollback(compositionElement.compositionId(), compositionElement, instanceElement, 0);
+            verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
+                    DeployState.DEPLOYED, null, StateChangeResult.FAILED,
+                    "Automation composition element migrate precheck failed");
         }
     }
 }