Add MigrationState validation for migration 80/142580/4
authorFrancescoFioraEst <francesco.fiora@est.tech>
Thu, 27 Nov 2025 14:20:07 +0000 (14:20 +0000)
committerFrancesco Fiora <francesco.fiora@est.tech>
Tue, 2 Dec 2025 13:41:05 +0000 (13:41 +0000)
Add MigrationState validation for migration.
Add set DEFAULT in rollback completed for REMOVED elements.

Issue-ID: POLICY-5515
Change-Id: Ib77b7bac5d78224d21e4ad15157118107ca88f14
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
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/scanner/AbstractScanner.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/scanner/StageScannerTest.java

index 1a81fab..e92c9b5 100644 (file)
@@ -71,6 +71,9 @@ import org.slf4j.LoggerFactory;
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 public final class AcmUtils {
     public static final String ENTRY = "entry ";
+    private static final String NOT_VALID_INSTANCE =
+            "Instance cannot be deployed; There are elements in an invalid Migration state."
+                    + "(ElementId: %s, MigrationState: %s)";
     private static final StringToMapConverter MAP_CONVERTER = new StringToMapConverter();
 
     private static final Logger LOGGER = LoggerFactory.getLogger(AcmUtils.class);
@@ -408,6 +411,23 @@ public final class AcmUtils {
         return message;
     }
 
+    /**
+     * Check that the AutomationComposition has all elements in MigrationState as DEFAULT.
+     *
+     * @param automationComposition the AutomationComposition
+     */
+    public static void checkMigrationState(AutomationComposition automationComposition) {
+        var result = automationComposition.getElements().values()
+                .stream()
+                .filter(element -> !MigrationState.DEFAULT.equals(element.getMigrationState()))
+                .findAny();
+        // check if elements are in a valid state to be deployed
+        if (result.isPresent()) {
+            var msg = String.format(NOT_VALID_INSTANCE, result.get().getId(), result.get().getMigrationState());
+            throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, msg);
+        }
+    }
+
     /**
      * Recursive Merge.
      *
index da87177..087c511 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.policy.clamp.models.acm.utils;
 
 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.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
@@ -39,8 +40,10 @@ 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.MigrationState;
 import org.onap.policy.clamp.models.acm.document.concepts.DocToscaServiceTemplate;
 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
+import org.onap.policy.models.base.PfModelRuntimeException;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaDataType;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
@@ -194,6 +197,15 @@ class AcmUtilsTest {
         assertEquals(message.substring(0, 255), AcmUtils.validatedMessage(message));
     }
 
+    @Test
+    void testCheckMigrationState() {
+        var automationComposition = getDummyAutomationComposition();
+        assertDoesNotThrow(() -> AcmUtils.checkMigrationState(automationComposition));
+        automationComposition.getElements().values().iterator().next().setMigrationState(MigrationState.REMOVED);
+        assertThatThrownBy(() -> AcmUtils.checkMigrationState(automationComposition))
+                .isInstanceOf(PfModelRuntimeException.class);
+    }
+
     private AutomationComposition getDummyAutomationComposition() {
         var automationComposition = new AutomationComposition();
         automationComposition.setCompositionId(UUID.randomUUID());
index a3b90dd..60fa172 100644 (file)
@@ -132,6 +132,7 @@ public class AutomationCompositionInstantiationProvider {
         AutomationCompositionProvider.validateInstanceEndpoint(compositionId, acFromDb);
         var acDefinition = acDefinitionProvider.getAcDefinition(compositionId);
         AcDefinitionProvider.checkPrimedComposition(acDefinition);
+        AcmUtils.checkMigrationState(acFromDb);
         if (DeployState.UNDEPLOYED.equals(acFromDb.getDeployState())) {
             LOGGER.info("Updating undeployed instance with id {}", instanceId);
             acFromDb.setElements(automationComposition.getElements());
index 5dc13a9..6e782ce 100644 (file)
@@ -22,7 +22,6 @@ package org.onap.policy.clamp.acm.runtime.supervision;
 
 import io.micrometer.core.annotation.Timed;
 import io.opentelemetry.context.Context;
-import jakarta.ws.rs.core.Response;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
@@ -41,7 +40,6 @@ 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.DeployState;
 import org.onap.policy.clamp.models.acm.concepts.LockState;
-import org.onap.policy.clamp.models.acm.concepts.MigrationState;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
 import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck;
@@ -49,8 +47,8 @@ import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositi
 import org.onap.policy.clamp.models.acm.persistence.provider.MessageProvider;
 import org.onap.policy.clamp.models.acm.utils.AcmStageUtils;
 import org.onap.policy.clamp.models.acm.utils.AcmStateUtils;
+import org.onap.policy.clamp.models.acm.utils.AcmUtils;
 import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
-import org.onap.policy.models.base.PfModelRuntimeException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
@@ -86,23 +84,14 @@ public class SupervisionAcHandler {
      */
     public void deploy(AutomationComposition automationComposition, AutomationCompositionDefinition acDefinition) {
         LOGGER.info("Deployment request received for instanceID: {}", automationComposition.getInstanceId());
-
-        var elements = automationComposition.getElements().values();
-        // check if elements are in a valid state to be deployed
-        elements.stream().filter(element -> !MigrationState.DEFAULT.equals(element.getMigrationState()))
-            .findAny().ifPresent(element -> {
-                var msg = String.format("Instance cannot be deployed; There are elements in an invalid Migration state."
-                    + "(ElementId: %s, MigrationState: %s)", element.getId(), element.getMigrationState());
-                LOGGER.warn(msg);
-                throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, msg);
-            });
+        AcmUtils.checkMigrationState(automationComposition);
 
         if (StateChangeResult.FAILED.equals(automationComposition.getStateChangeResult())
                 && DeployState.DEPLOYING.equals(automationComposition.getDeployState())
                 && automationComposition.getElements().size() > 1) {
             automationComposition.setLastMsg(TimestampHelper.now());
 
-            for (var element : elements) {
+            for (var element : automationComposition.getElements().values()) {
                 if (!DeployState.DEPLOYED.equals(element.getDeployState())) {
                     element.setDeployState(DeployState.DEPLOYING);
                     element.setMessage(null);
index 42df834..1df511d 100644 (file)
@@ -69,7 +69,8 @@ public abstract class AbstractScanner {
                 || DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState())) {
             automationComposition.setCompositionTargetId(null);
             for (var acElement : automationComposition.getElements().values()) {
-                if (MigrationState.NEW.equals(acElement.getMigrationState())) {
+                if (MigrationState.NEW.equals(acElement.getMigrationState())
+                        || MigrationState.REMOVED.equals(acElement.getMigrationState())) {
                     acElement.setMigrationState(MigrationState.DEFAULT);
                 }
             }
index 9e63cd2..2d10e2c 100644 (file)
@@ -51,6 +51,7 @@ import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition
 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionRollback;
 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.MigrationState;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
 import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate;
@@ -317,6 +318,8 @@ class AutomationCompositionInstantiationProviderTest {
         automationCompositionTarget.setPrecheck(false);
         AcmStateUtils.setCascadedState(automationComposition, DeployState.DEPLOYED, LockState.LOCKED,
                 SubState.NONE);
+        automationComposition.getElements().values()
+                .forEach(el -> el.setMigrationState(MigrationState.DEFAULT));
         var instantiationResponse = instantiationProvider.updateAutomationComposition(compositionId,
                 automationCompositionTarget);
 
index 42951b1..f392a12 100644 (file)
@@ -44,6 +44,7 @@ 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.DeployState;
 import org.onap.policy.clamp.models.acm.concepts.LockState;
+import org.onap.policy.clamp.models.acm.concepts.MigrationState;
 import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
 import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
@@ -160,9 +161,11 @@ class StageScannerTest {
         // first element is migrated
         clearInvocations(acProvider);
         element.setDeployState(DeployState.DEPLOYED);
+        element.setMigrationState(MigrationState.REMOVED);
         supervisionScanner.scanStage(automationComposition, acDefinition, new UpdateSync(), UUID.randomUUID());
         verify(acProvider).updateAutomationComposition(any(AutomationComposition.class));
 
+        assertEquals(MigrationState.DEFAULT, element.getMigrationState());
         assertEquals(DeployState.DEPLOYED, automationComposition.getDeployState());
     }