Fix issue in Migration remove elements 58/141658/2 master
authorrameshiyer27 <ramesh.murugan.iyer@est.tech>
Wed, 6 Aug 2025 13:56:33 +0000 (14:56 +0100)
committerrameshiyer27 <ramesh.murugan.iyer@est.tech>
Thu, 7 Aug 2025 15:13:44 +0000 (16:13 +0100)
Problem: If the only element associated with a participant is removed
in migration, Acmr ignores the participant Id in the migrate event.

Issue-ID: POLICY-5438
Signed-off-by: rameshiyer27 <ramesh.murugan.iyer@est.tech>
Change-Id: Ib44efb9d62185b38153896277185f26ad1b71307

models/src/main/java/org/onap/policy/clamp/models/acm/utils/AcmUtils.java
models/src/test/java/org/onap/policy/clamp/models/acm/utils/AcmUtilsTest.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProvider.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/comm/AcElementPropertiesPublisher.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/comm/AcPreparePublisher.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/comm/AutomationCompositionMigrationPublisher.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/StageScanner.java
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/AutomationCompositionInstantiationProviderTest.java
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/comm/SupervisionMessagesTest.java

index 5bcac7e..cb99613 100644 (file)
@@ -429,7 +429,7 @@ public final class AcmUtils {
      * @param deployOrder the DeployOrder
      */
     public static List<ParticipantDeploy> createParticipantDeployList(AutomationComposition automationComposition,
-            DeployOrder deployOrder) {
+            DeployOrder deployOrder, List<AutomationCompositionElement> removedElements) {
         Map<UUID, List<AcElementDeploy>> map = new HashMap<>();
         for (var element : automationComposition.getElements().values()) {
             var acElementDeploy = createAcElementDeploy(element, deployOrder);
@@ -443,6 +443,16 @@ public final class AcmUtils {
             participantDeploy.setAcElementList(entry.getValue());
             participantDeploys.add(participantDeploy);
         }
+        // Include the participantIds for the removed elements
+        for (var element : removedElements) {
+            if (map.get(element.getParticipantId()) == null) {
+                var participantDeploy = new ParticipantDeploy();
+                participantDeploy.setParticipantId(element.getParticipantId());
+                participantDeploys.add(participantDeploy);
+                map.put(element.getParticipantId(), new ArrayList<>());
+            }
+
+        }
         return participantDeploys;
     }
 
index fb0f683..f7f379e 100644 (file)
@@ -41,6 +41,7 @@ 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.DeployState;
 import org.onap.policy.clamp.models.acm.concepts.LockState;
+import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
 import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.clamp.models.acm.document.concepts.DocToscaServiceTemplate;
 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
@@ -212,7 +213,7 @@ class AcmUtilsTest {
     @Test
     void testCreateAcElementDeployList() {
         var automationComposition = getDummyAutomationComposition();
-        var result = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.DEPLOY);
+        var result = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.DEPLOY, List.of());
         assertThat(result).hasSameSizeAs(automationComposition.getElements().values());
         for (var participantDeploy : result) {
             for (var element : participantDeploy.getAcElementList()) {
@@ -221,6 +222,26 @@ class AcmUtilsTest {
         }
     }
 
+    @Test
+    void testAcDeployListWithRemovedElements() {
+        var removedElement1 = CommonTestData.getJsonObject(
+                "src/test/resources/json/AutomationCompositionElementNoOrderedState.json",
+                AutomationCompositionElement.class);
+        var participantId1 = UUID.randomUUID();
+        var participantId2 = UUID.randomUUID();
+        assert removedElement1 != null;
+        removedElement1.setParticipantId(participantId1);
+        var removedElement2 = new AutomationCompositionElement(removedElement1);
+        removedElement2.setParticipantId(participantId2);
+
+        var automationComposition = getDummyAutomationComposition();
+        var result = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.DEPLOY,
+                List.of(removedElement1, removedElement2));
+        assertThat(result).hasSize(automationComposition.getElements().values().size() + 2);
+        var participantIds = result.stream().map(ParticipantDeploy::getParticipantId).toList();
+        assertThat(participantIds).containsAll(List.of(participantId1, participantId2));
+    }
+
     @Test
     void testCreateAcElementRestart() {
         var element = getDummyAutomationComposition().getElements().values().iterator().next();
index 9a79549..c619015 100644 (file)
@@ -51,14 +51,12 @@ import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvide
 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
 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.common.parameters.BeanValidationResult;
 import org.onap.policy.models.base.PfModelRuntimeException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -232,10 +230,13 @@ public class AutomationCompositionInstantiationProvider {
         encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionTargetId());
 
         var ac = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
-        elementsRemoved.forEach(automationCompositionProvider::deleteAutomationCompositionElement);
+        for (var element : elementsRemoved) {
+            automationCompositionProvider.deleteAutomationCompositionElement(element.getId());
+        }
 
         // Publish migrate event to the participants
-        supervisionAcHandler.migrate(acToPublish, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId());
+        supervisionAcHandler.migrate(acToPublish, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId(),
+                elementsRemoved);
         return createInstantiationResponse(ac);
     }
 
@@ -252,9 +253,10 @@ public class AutomationCompositionInstantiationProvider {
         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
     }
 
-    private List<UUID> getElementRemoved(AutomationComposition acFromDb, AutomationComposition acFromMigration) {
-        return acFromDb.getElements().keySet().stream()
-            .filter(id -> acFromMigration.getElements().get(id) == null).toList();
+    private List<AutomationCompositionElement> getElementRemoved(AutomationComposition acFromDb,
+                                                                 AutomationComposition acFromMigration) {
+        return acFromDb.getElements().values().stream()
+            .filter(element -> acFromMigration.getElements().get(element.getId()) == null).toList();
     }
 
 
@@ -267,10 +269,11 @@ public class AutomationCompositionInstantiationProvider {
         var acDefinitionTarget = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionTargetId());
         AcDefinitionProvider.checkPrimedComposition(acDefinitionTarget);
         // Iterate and update the element property values
-        updateElementsProperties(automationComposition, copyAc, acDefinitionTarget);
+        var removedElements = updateElementsProperties(automationComposition, copyAc, acDefinitionTarget);
 
         // Publish migrate event to the participants
-        supervisionAcHandler.migratePrecheck(copyAc, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId());
+        supervisionAcHandler.migratePrecheck(copyAc, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId(),
+                removedElements);
 
         AcmUtils.setCascadedState(acToBeUpdated, DeployState.DEPLOYED, LockState.LOCKED,
             SubState.MIGRATION_PRECHECKING);
@@ -458,12 +461,15 @@ public class AutomationCompositionInstantiationProvider {
         updateAcForMigration(acToBeUpdated, acDefinitionTarget, DeployState.MIGRATION_REVERTING);
         var elementsRemoved = getElementRemoved(automationComposition, acToBeUpdated);
         automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
-        elementsRemoved.forEach(automationCompositionProvider::deleteAutomationCompositionElement);
+        for (var element : elementsRemoved) {
+            automationCompositionProvider.deleteAutomationCompositionElement(element.getId());
+        }
         var acDefinition = acDefinitionProvider.getAcDefinition(acToBeUpdated.getCompositionId());
-        supervisionAcHandler.migrate(acToBeUpdated, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId());
+        supervisionAcHandler.migrate(acToBeUpdated, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId(),
+                elementsRemoved);
     }
 
-    private List<UUID> updateElementsProperties(AutomationComposition automationComposition,
+    private List<AutomationCompositionElement> updateElementsProperties(AutomationComposition automationComposition,
             AutomationComposition acToBeUpdated, AutomationCompositionDefinition acDefinitionTarget) {
         for (var element : automationComposition.getElements().entrySet()) {
             var elementId = element.getKey();
@@ -483,7 +489,7 @@ public class AutomationCompositionInstantiationProvider {
         }
         // Remove element which is not present in the new Ac instance
         var elementsRemoved = getElementRemoved(acToBeUpdated, automationComposition);
-        elementsRemoved.forEach(uuid -> acToBeUpdated.getElements().remove(uuid));
+        elementsRemoved.forEach(element -> acToBeUpdated.getElements().remove(element.getId()));
 
         var validationResult = validateAutomationComposition(acToBeUpdated, acDefinitionTarget);
         if (!validationResult.isValid()) {
index 37c3866..9d6529d 100644 (file)
@@ -23,6 +23,7 @@ package org.onap.policy.clamp.acm.runtime.supervision;
 import io.micrometer.core.annotation.Timed;
 import io.opentelemetry.context.Context;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.ExecutorService;
@@ -37,6 +38,7 @@ import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionS
 import org.onap.policy.clamp.models.acm.concepts.AcElementDeployAck;
 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.DeployState;
 import org.onap.policy.clamp.models.acm.concepts.LockState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
@@ -345,11 +347,11 @@ public class SupervisionAcHandler {
      * @param revisionIdCompositionTarget the last Update from Composition Target
      */
     public void migrate(AutomationComposition automationComposition, UUID revisionIdComposition,
-            UUID revisionIdCompositionTarget) {
+                        UUID revisionIdCompositionTarget, List<AutomationCompositionElement> removedElements) {
         executor.execute(() -> {
             encryptionUtils.decryptInstanceProperties(automationComposition);
             acCompositionMigrationPublisher.send(automationComposition, automationComposition.getPhase(),
-                    revisionIdComposition, revisionIdCompositionTarget);
+                    revisionIdComposition, revisionIdCompositionTarget, removedElements);
         });
     }
 
@@ -361,8 +363,8 @@ public class SupervisionAcHandler {
      * @param revisionIdCompositionTarget the last Update from Composition Target
      */
     public void migratePrecheck(AutomationComposition automationComposition, UUID revisionIdComposition,
-            UUID revisionIdCompositionTarget) {
+            UUID revisionIdCompositionTarget, List<AutomationCompositionElement> removedElements) {
         executor.execute(() -> acCompositionMigrationPublisher.send(automationComposition, 0,
-                revisionIdComposition, revisionIdCompositionTarget));
+                revisionIdComposition, revisionIdCompositionTarget, removedElements));
     }
 }
index 68a70fa..3f7704f 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.policy.clamp.acm.runtime.supervision.comm;
 
 import io.micrometer.core.annotation.Timed;
 import java.time.Instant;
+import java.util.List;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import lombok.AllArgsConstructor;
@@ -58,7 +59,8 @@ public class AcElementPropertiesPublisher extends AbstractParticipantPublisher<P
         propertiesUpdate.setTimestamp(Instant.now());
         propertiesUpdate.setRevisionIdInstance(automationComposition.getRevisionId());
         propertiesUpdate.setRevisionIdComposition(revisionIdComposition);
-        var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.UPDATE);
+        var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.UPDATE,
+                List.of());
         propertiesUpdate.setParticipantUpdatesList(participantUpdatesList);
         propertiesUpdate.setParticipantIdList(participantUpdatesList.stream()
                 .map(ParticipantDeploy::getParticipantId).collect(Collectors.toSet()));
index 2f1eaf7..a1ebbf3 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.policy.clamp.acm.runtime.supervision.comm;
 
 import io.micrometer.core.annotation.Timed;
 import java.time.Instant;
+import java.util.List;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import lombok.AllArgsConstructor;
@@ -53,7 +54,8 @@ public class AcPreparePublisher extends AbstractParticipantPublisher<AutomationC
         var acPrepare = createAutomationCompositionPrepare(automationComposition.getCompositionId(),
             automationComposition.getInstanceId());
         acPrepare.setStage(stage);
-        var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.NONE);
+        var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.NONE,
+                List.of());
         acPrepare.setParticipantList(participantUpdatesList);
         acPrepare.setParticipantIdList(participantUpdatesList.stream()
                 .map(ParticipantDeploy::getParticipantId).collect(Collectors.toSet()));
index f19473f..4917813 100644 (file)
 package org.onap.policy.clamp.acm.runtime.supervision.comm;
 
 import io.micrometer.core.annotation.Timed;
+import java.util.List;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
+import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
 import org.onap.policy.clamp.models.acm.concepts.DeployState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
@@ -47,7 +49,7 @@ public class AutomationCompositionMigrationPublisher
             value = "publisher.automation_composition_migration",
             description = "AUTOMATION_COMPOSITION_MIGRATION messages published")
     public void send(AutomationComposition automationComposition, int stage, UUID revisionIdComposition,
-            UUID revisionIdCompositionTarget) {
+                     UUID revisionIdCompositionTarget, List<AutomationCompositionElement> removedElements) {
         var acMigration = new AutomationCompositionMigration();
         acMigration.setRollback(DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState()));
         acMigration.setPrecheck(Boolean.TRUE.equals(automationComposition.getPrecheck()));
@@ -59,7 +61,8 @@ public class AutomationCompositionMigrationPublisher
         acMigration.setRevisionIdInstance(automationComposition.getRevisionId());
         acMigration.setRevisionIdComposition(revisionIdComposition);
         acMigration.setRevisionIdCompositionTarget(revisionIdCompositionTarget);
-        var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.MIGRATE);
+        var participantUpdatesList = AcmUtils.createParticipantDeployList(automationComposition, DeployOrder.MIGRATE,
+                removedElements);
         acMigration.setParticipantUpdatesList(participantUpdatesList);
         acMigration.setParticipantIdList(participantUpdatesList.stream()
                 .map(ParticipantDeploy::getParticipantId).collect(Collectors.toSet()));
index 9791d5e..777130f 100644 (file)
@@ -21,6 +21,7 @@
 package org.onap.policy.clamp.acm.runtime.supervision.scanner;
 
 import java.util.Comparator;
+import java.util.List;
 import java.util.UUID;
 import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
 import org.onap.policy.clamp.acm.runtime.main.utils.EncryptionUtils;
@@ -118,7 +119,7 @@ public class StageScanner extends AbstractScanner {
             LOGGER.debug("retry message AutomationCompositionMigration");
             // acDefinition for migration is the Composition target
             acMigrationPublisher.send(automationComposition, minStageNotCompleted, revisionIdComposition,
-                    acDefinition.getRevisionId());
+                    acDefinition.getRevisionId(), List.of());
         }
         if (SubState.PREPARING.equals(automationComposition.getSubState())) {
             LOGGER.debug("retry message AutomationCompositionPrepare");
index 325f3a9..ee74568 100644 (file)
@@ -307,7 +307,7 @@ class AutomationCompositionInstantiationProviderTest {
         automationCompositionTarget.setPrecheck(true);
         var preCheckResponse = instantiationProvider.updateAutomationComposition(compositionId,
                 automationCompositionTarget);
-        verify(supervisionAcHandler).migratePrecheck(any(), any(), any());
+        verify(supervisionAcHandler).migratePrecheck(any(), any(), any(), any());
         InstantiationUtils.assertInstantiationResponse(preCheckResponse, automationCompositionTarget);
 
         automationCompositionTarget.setPrecheck(false);
@@ -316,7 +316,7 @@ class AutomationCompositionInstantiationProviderTest {
         var instantiationResponse = instantiationProvider.updateAutomationComposition(compositionId,
                 automationCompositionTarget);
 
-        verify(supervisionAcHandler).migrate(any(), any(), any());
+        verify(supervisionAcHandler).migrate(any(), any(), any(), any());
         InstantiationUtils.assertInstantiationResponse(instantiationResponse, automationCompositionTarget);
     }
 
@@ -362,7 +362,7 @@ class AutomationCompositionInstantiationProviderTest {
         var instantiationResponse = instantiationProvider
                 .updateAutomationComposition(automationComposition.getCompositionId(), automationComposition);
 
-        verify(supervisionAcHandler).migrate(any(), any(), any());
+        verify(supervisionAcHandler).migrate(any(), any(), any(), any());
         verify(acProvider).updateAutomationComposition(automationComposition);
         InstantiationUtils.assertInstantiationResponse(instantiationResponse, automationComposition);
     }
@@ -401,7 +401,7 @@ class AutomationCompositionInstantiationProviderTest {
         var instantiationResponse = instantiationProvider
                 .updateAutomationComposition(automationComposition.getCompositionId(), automationComposition);
 
-        verify(supervisionAcHandler).migratePrecheck(any(), any(), any());
+        verify(supervisionAcHandler).migratePrecheck(any(), any(), any(), any());
         verify(acProvider).updateAutomationComposition(automationComposition);
         InstantiationUtils.assertInstantiationResponse(instantiationResponse, automationComposition);
 
index d9ffb0a..b434a01 100644 (file)
@@ -30,6 +30,7 @@ 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;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.UUID;
@@ -513,9 +514,9 @@ class SupervisionAcHandlerTest {
                 InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Migrate");
         assert automationComposition != null;
         automationComposition.setPhase(0);
-        handler.migrate(automationComposition, UUID.randomUUID(), UUID.randomUUID());
+        handler.migrate(automationComposition, UUID.randomUUID(), UUID.randomUUID(), List.of());
         verify(acCompositionMigrationPublisher, timeout(1000))
-                .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class));
+                .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class), any());
     }
 
     @Test
@@ -528,9 +529,9 @@ class SupervisionAcHandlerTest {
                 mock(AcPreparePublisher.class), mock(MessageProvider.class), mock(EncryptionUtils.class));
         var automationComposition =
                 InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Migrate");
-        handler.migratePrecheck(automationComposition, UUID.randomUUID(), UUID.randomUUID());
+        handler.migratePrecheck(automationComposition, UUID.randomUUID(), UUID.randomUUID(), List.of());
         verify(acCompositionMigrationPublisher, timeout(1000))
-                .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class));
+                .send(any(AutomationComposition.class), anyInt(), any(UUID.class), any(UUID.class), any());
     }
 
     @Test
index 84b80ae..54c3ded 100644 (file)
@@ -235,7 +235,7 @@ class SupervisionMessagesTest {
         publisher.active(topicSink);
         var automationComposition =
                 InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_UPDATE_JSON, "Crud");
-        publisher.send(automationComposition, 0, UUID.randomUUID(), UUID.randomUUID());
+        publisher.send(automationComposition, 0, UUID.randomUUID(), UUID.randomUUID(), List.of());
         verify(topicSink).send(anyString());
     }