Add Failure handling support in the ACM-R 25/134625/1
authorFrancescoFioraEst <francesco.fiora@est.tech>
Wed, 31 May 2023 15:38:22 +0000 (16:38 +0100)
committerLiam Fallon <liam.fallon@est.tech>
Tue, 6 Jun 2023 08:08:19 +0000 (08:08 +0000)
Issue-ID: POLICY-4705
Change-Id: I919b7981cdbe69ac7ce703fceb2e980a6d9a056e
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
(cherry picked from commit ebb4d0cf867d752ae148880dd0109fc3cf6d6025)

14 files changed:
models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AutomationComposition.java
models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AutomationCompositionElement.java
models/src/main/java/org/onap/policy/clamp/models/acm/concepts/StateChangeResult.java [new file with mode: 0644]
models/src/main/java/org/onap/policy/clamp/models/acm/messages/dmaap/participant/ParticipantAckMessage.java
models/src/main/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationComposition.java
models/src/main/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationCompositionElement.java
models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/AcInstanceStateResolver.java
models/src/main/java/org/onap/policy/clamp/models/acm/utils/AcmUtils.java
models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AcInstanceStateResolverTest.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.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/SupervisionScanner.java
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java

index dc8a39b..54c9b61 100644 (file)
@@ -50,6 +50,8 @@ public class AutomationComposition extends ToscaEntity implements Comparable<Aut
 
     private Map<UUID, AutomationCompositionElement> elements;
 
+    private StateChangeResult stateChangeResult;
+
     /**
      * Copy contructor, does a deep copy.
      *
@@ -62,6 +64,7 @@ public class AutomationComposition extends ToscaEntity implements Comparable<Aut
         this.deployState = otherAutomationComposition.deployState;
         this.lockState = otherAutomationComposition.lockState;
         this.elements = PfUtils.mapMap(otherAutomationComposition.elements, AutomationCompositionElement::new);
+        this.stateChangeResult = otherAutomationComposition.stateChangeResult;
     }
 
     @Override
index 8095b5c..ea54076 100644 (file)
@@ -55,10 +55,9 @@ public class AutomationCompositionElement {
     private LockState lockState = LockState.LOCKED;
 
     private String operationalState;
-
     private String useState;
-
     private String description;
+    private String message;
 
     // A map indexed by the property name. Each map entry is the serialized value of the property,
     // which can be deserialized into an instance of the type of the property.
@@ -82,5 +81,6 @@ public class AutomationCompositionElement {
         this.lockState = otherElement.lockState;
         this.operationalState = otherElement.operationalState;
         this.useState = otherElement.useState;
+        this.message = otherElement.message;
     }
 }
diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/StateChangeResult.java b/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/StateChangeResult.java
new file mode 100644 (file)
index 0000000..de4404f
--- /dev/null
@@ -0,0 +1,26 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.models.acm.concepts;
+
+public enum StateChangeResult {
+    NO_ERROR,
+    FAILED
+}
index e73f2e7..3182332 100644 (file)
@@ -26,6 +26,7 @@ import lombok.NonNull;
 import lombok.Setter;
 import lombok.ToString;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantState;
+import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
 
 /**
  * Class to represent participant Ack message.
@@ -38,9 +39,12 @@ public class ParticipantAckMessage {
     // The responseTo field should match the original request id in the request.
     private UUID responseTo;
 
-    // Result: Success/Fail.
+    // Intermediary result: Success/Fail.
     private Boolean result;
 
+    // Indicating participant failure
+    private StateChangeResult stateChangeResult;
+
     // Message indicating reason for failure
     private String message;
 
index 33ec649..3a448f2 100644 (file)
@@ -43,6 +43,7 @@ import org.apache.commons.lang3.ObjectUtils;
 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
 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.StateChangeResult;
 import org.onap.policy.common.parameters.annotations.NotNull;
 import org.onap.policy.common.parameters.annotations.Valid;
 import org.onap.policy.models.base.PfAuthorative;
@@ -87,6 +88,9 @@ public class JpaAutomationComposition extends Validated
     @NotNull
     private LockState lockState;
 
+    @Column
+    private StateChangeResult stateChangeResult;
+
     @Column
     private String description;
 
@@ -100,7 +104,7 @@ public class JpaAutomationComposition extends Validated
      */
     public JpaAutomationComposition() {
         this(UUID.randomUUID().toString(), new PfConceptKey(), UUID.randomUUID().toString(),
-                new ArrayList<>(), DeployState.UNDEPLOYED, LockState.LOCKED);
+                new ArrayList<>(), DeployState.UNDEPLOYED, LockState.NONE);
     }
 
     /**
@@ -139,6 +143,7 @@ public class JpaAutomationComposition extends Validated
         this.deployState = copyConcept.deployState;
         this.lockState = copyConcept.lockState;
         this.description = copyConcept.description;
+        this.stateChangeResult = copyConcept.stateChangeResult;
         this.elements = PfUtils.mapList(copyConcept.elements, JpaAutomationCompositionElement::new);
     }
 
@@ -162,6 +167,7 @@ public class JpaAutomationComposition extends Validated
         automationComposition.setDeployState(deployState);
         automationComposition.setLockState(lockState);
         automationComposition.setDescription(description);
+        automationComposition.setStateChangeResult(stateChangeResult);
         automationComposition.setElements(new LinkedHashMap<>(this.elements.size()));
         for (var element : this.elements) {
             automationComposition.getElements().put(UUID.fromString(element.getElementId()), element.toAuthorative());
@@ -179,7 +185,7 @@ public class JpaAutomationComposition extends Validated
         this.deployState = automationComposition.getDeployState();
         this.lockState = automationComposition.getLockState();
         this.description = automationComposition.getDescription();
-
+        this.stateChangeResult = automationComposition.getStateChangeResult();
         this.elements = new ArrayList<>(automationComposition.getElements().size());
         for (var elementEntry : automationComposition.getElements().entrySet()) {
             var jpaAutomationCompositionElement =
@@ -232,6 +238,11 @@ public class JpaAutomationComposition extends Validated
         if (result != 0) {
             return result;
         }
+
+        result = ObjectUtils.compare(stateChangeResult, other.stateChangeResult);
+        if (result != 0) {
+            return result;
+        }
         return PfUtils.compareObjects(elements, other.elements);
     }
 }
index 27da039..60a911b 100644 (file)
@@ -101,6 +101,9 @@ public class JpaAutomationCompositionElement extends Validated
     @Column
     private String description;
 
+    @Column
+    private String message;
+
     @Lob
     @NotNull
     @Valid
@@ -169,6 +172,7 @@ public class JpaAutomationCompositionElement extends Validated
         this.lockState = copyConcept.lockState;
         this.operationalState = copyConcept.operationalState;
         this.useState = copyConcept.useState;
+        this.message = copyConcept.message;
     }
 
     /**
@@ -194,6 +198,7 @@ public class JpaAutomationCompositionElement extends Validated
         element.setLockState(lockState);
         element.setOperationalState(operationalState);
         element.setUseState(useState);
+        element.setMessage(message);
 
         return element;
     }
@@ -209,6 +214,7 @@ public class JpaAutomationCompositionElement extends Validated
         this.lockState = element.getLockState();
         this.operationalState = element.getOperationalState();
         this.useState = element.getUseState();
+        this.message = element.getMessage();
     }
 
     @Override
@@ -260,6 +266,10 @@ public class JpaAutomationCompositionElement extends Validated
             return result;
         }
 
+        result = ObjectUtils.compare(message, other.message);
+        if (result != 0) {
+            return result;
+        }
         return ObjectUtils.compare(description, other.description);
     }
 }
index 8676b33..659169b 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.policy.clamp.models.acm.persistence.provider;
 
 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.StateChangeResult;
 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.utils.StateDefinition;
@@ -32,15 +33,24 @@ public class AcInstanceStateResolver {
     private final StateDefinition<String> graph;
 
     private static final String DEPLOYED = DeployState.DEPLOYED.name();
+    private static final String DEPLOYING = DeployState.DEPLOYING.name();
     private static final String UNDEPLOYED = DeployState.UNDEPLOYED.name();
+    private static final String UNDEPLOYING = DeployState.UNDEPLOYING.name();
+    private static final String UPDATING = DeployState.UPDATING.name();
+    private static final String DELETING = DeployState.DELETING.name();
 
     private static final String LOCKED = LockState.LOCKED.name();
+    private static final String LOCKING = LockState.LOCKING.name();
     private static final String UNLOCKED = LockState.UNLOCKED.name();
+    private static final String UNLOCKING = LockState.UNLOCKING.name();
     private static final String STATE_LOCKED_NONE = LockState.NONE.name();
 
     private static final String DEPLOY_NONE = DeployOrder.NONE.name();
     private static final String LOCK_NONE = LockOrder.NONE.name();
 
+    private static final String NO_ERROR = StateChangeResult.NO_ERROR.name();
+    private static final String FAILED = StateChangeResult.FAILED.name();
+
     // list of results
     public static final String DEPLOY = DeployOrder.DEPLOY.name();
     public static final String UNDEPLOY = DeployOrder.UNDEPLOY.name();
@@ -53,13 +63,30 @@ public class AcInstanceStateResolver {
      * Construct.
      */
     public AcInstanceStateResolver() {
-        this.graph = new StateDefinition<>(4, NONE);
+        this.graph = new StateDefinition<>(5, NONE);
+
+        // no error
+        this.graph.put(new String[] {DEPLOY, LOCK_NONE, UNDEPLOYED, STATE_LOCKED_NONE, NO_ERROR}, DEPLOY);
+        this.graph.put(new String[] {UNDEPLOY, LOCK_NONE, DEPLOYED, LOCKED, NO_ERROR}, UNDEPLOY);
+        this.graph.put(new String[] {DELETE, LOCK_NONE, UNDEPLOYED, LOCK_NONE, NO_ERROR}, DELETE);
+        this.graph.put(new String[] {DEPLOY_NONE, UNLOCK, DEPLOYED, LOCKED, NO_ERROR}, UNLOCK);
+        this.graph.put(new String[] {DEPLOY_NONE, LOCK, DEPLOYED, UNLOCKED, NO_ERROR}, LOCK);
+
+        // failed
+        this.graph.put(new String[] {DEPLOY, LOCK_NONE, UNDEPLOYING, STATE_LOCKED_NONE, FAILED}, DEPLOY);
+        this.graph.put(new String[] {UNDEPLOY, LOCK_NONE, UNDEPLOYING, STATE_LOCKED_NONE, FAILED}, UNDEPLOY);
+        this.graph.put(new String[] {UNDEPLOY, LOCK_NONE, UPDATING, STATE_LOCKED_NONE, FAILED}, UNDEPLOY);
+
+        this.graph.put(new String[] {DEPLOY, LOCK_NONE, DEPLOYING, STATE_LOCKED_NONE, FAILED}, DEPLOY);
+        this.graph.put(new String[] {UNDEPLOY, LOCK_NONE, DEPLOYING, STATE_LOCKED_NONE, FAILED}, UNDEPLOY);
+
+        this.graph.put(new String[] {DELETE, LOCK_NONE, DELETING, LOCK_NONE, FAILED}, DELETE);
+
+        this.graph.put(new String[] {DEPLOY_NONE, UNLOCK, DEPLOYED, LOCKING, FAILED}, UNLOCK);
+        this.graph.put(new String[] {DEPLOY_NONE, LOCK, DEPLOYED, LOCKING, FAILED}, LOCK);
 
-        this.graph.put(new String[] {DEPLOY, LOCK_NONE, UNDEPLOYED, STATE_LOCKED_NONE}, DEPLOY);
-        this.graph.put(new String[] {UNDEPLOY, LOCK_NONE, DEPLOYED, LOCKED}, UNDEPLOY);
-        this.graph.put(new String[] {DELETE, LOCK_NONE, UNDEPLOYED, LOCK_NONE}, DELETE);
-        this.graph.put(new String[] {DEPLOY_NONE, UNLOCK, DEPLOYED, LOCKED}, UNLOCK);
-        this.graph.put(new String[] {DEPLOY_NONE, LOCK, DEPLOYED, UNLOCKED}, LOCK);
+        this.graph.put(new String[] {DEPLOY_NONE, LOCK, DEPLOYED, UNLOCKING, FAILED}, LOCK);
+        this.graph.put(new String[] {DEPLOY_NONE, UNLOCK, DEPLOYED, UNLOCKING, FAILED}, UNLOCK);
     }
 
     /**
@@ -68,17 +95,19 @@ public class AcInstanceStateResolver {
      * @param acDeployOrder the Deploy Ordered
      * @param acLockOrder the Lock Ordered
      * @param acDeployState then current Deploy State
-     * @param acLockState the  current Lock State
+     * @param acLockState the current Lock State
+     * @param acStateChangeResult the current Result of the State Change
      * @return the order (DEPLOY/UNDEPLOY/LOCK/UNLOCK) to send to participant or NONE if order is not consistent
      */
     public String resolve(DeployOrder acDeployOrder, LockOrder acLockOrder, DeployState acDeployState,
-            LockState acLockState) {
+            LockState acLockState, StateChangeResult acStateChangeResult) {
         var deployOrder = acDeployOrder != null ? acDeployOrder : DeployOrder.NONE;
         var lockOrder = acLockOrder != null ? acLockOrder : LockOrder.NONE;
+        var stateChangeResult = acStateChangeResult != null ? acStateChangeResult : StateChangeResult.NO_ERROR;
 
         var deployState = acDeployState != null ? acDeployState : DeployState.UNDEPLOYED;
         var lockState = acLockState != null ? acLockState : LockState.NONE;
-        return this.graph
-                .get(new String[] {deployOrder.name(), lockOrder.name(), deployState.name(), lockState.name()});
+        return this.graph.get(new String[] {deployOrder.name(), lockOrder.name(), deployState.name(), lockState.name(),
+                stateChangeResult.name()});
     }
 }
index 7de4720..671aca6 100644 (file)
@@ -285,7 +285,7 @@ public final class AcmUtils {
     public static boolean isInTransitionalState(DeployState deployState, LockState lockState) {
         return DeployState.DEPLOYING.equals(deployState) || DeployState.UNDEPLOYING.equals(deployState)
                 || LockState.LOCKING.equals(lockState) || LockState.UNLOCKING.equals(lockState)
-                || DeployState.DELETING.equals(deployState);
+                || DeployState.DELETING.equals(deployState) || DeployState.UPDATING.equals(deployState);
     }
 
     /**
@@ -339,6 +339,7 @@ public final class AcmUtils {
     public static DeployState deployCompleted(DeployState deployState) {
         DeployState result = null;
         switch (deployState) {
+            case UPDATING:
             case DEPLOYING:
                 result = DeployState.DEPLOYED;
                 break;
index 103bca2..7f6cb2f 100644 (file)
@@ -25,6 +25,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 import org.junit.jupiter.api.Test;
 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.StateChangeResult;
 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
 
@@ -33,32 +34,33 @@ class AcInstanceStateResolverTest {
     @Test
     void testResolve() {
         var acTypeStateResolver = new AcInstanceStateResolver();
-        var result =
-                acTypeStateResolver.resolve(DeployOrder.DEPLOY, LockOrder.NONE, DeployState.UNDEPLOYED, LockState.NONE);
+        var result = acTypeStateResolver.resolve(DeployOrder.DEPLOY, LockOrder.NONE, DeployState.UNDEPLOYED,
+                LockState.NONE, StateChangeResult.NO_ERROR);
         assertThat(result).isEqualTo(AcInstanceStateResolver.DEPLOY);
         result = acTypeStateResolver.resolve(DeployOrder.UNDEPLOY, LockOrder.NONE, DeployState.DEPLOYED,
-                LockState.LOCKED);
+                LockState.LOCKED, StateChangeResult.NO_ERROR);
         assertThat(result).isEqualTo(AcInstanceStateResolver.UNDEPLOY);
-        result = acTypeStateResolver.resolve(DeployOrder.NONE, LockOrder.UNLOCK, DeployState.DEPLOYED,
-                LockState.LOCKED);
+        result = acTypeStateResolver.resolve(DeployOrder.NONE, LockOrder.UNLOCK, DeployState.DEPLOYED, LockState.LOCKED,
+                StateChangeResult.NO_ERROR);
         assertThat(result).isEqualTo(AcInstanceStateResolver.UNLOCK);
-        result = acTypeStateResolver.resolve(DeployOrder.NONE, LockOrder.LOCK, DeployState.DEPLOYED,
-                LockState.UNLOCKED);
+        result = acTypeStateResolver.resolve(DeployOrder.NONE, LockOrder.LOCK, DeployState.DEPLOYED, LockState.UNLOCKED,
+                StateChangeResult.NO_ERROR);
         assertThat(result).isEqualTo(AcInstanceStateResolver.LOCK);
 
-        result = acTypeStateResolver.resolve(DeployOrder.NONE, LockOrder.NONE, DeployState.UNDEPLOYED, LockState.NONE);
+        result = acTypeStateResolver.resolve(DeployOrder.NONE, LockOrder.NONE, DeployState.UNDEPLOYED, LockState.NONE,
+                StateChangeResult.NO_ERROR);
         assertThat(result).isEqualTo(AcInstanceStateResolver.NONE);
         result = acTypeStateResolver.resolve(DeployOrder.UNDEPLOY, LockOrder.UNLOCK, DeployState.DEPLOYED,
-                LockState.LOCKED);
+                LockState.LOCKED, StateChangeResult.NO_ERROR);
         assertThat(result).isEqualTo(AcInstanceStateResolver.NONE);
-        result = acTypeStateResolver.resolve(DeployOrder.NONE, LockOrder.UNLOCK, DeployState.UNDEPLOYED,
-                LockState.NONE);
+        result = acTypeStateResolver.resolve(DeployOrder.NONE, LockOrder.UNLOCK, DeployState.UNDEPLOYED, LockState.NONE,
+                StateChangeResult.NO_ERROR);
         assertThat(result).isEqualTo(AcInstanceStateResolver.NONE);
         result = acTypeStateResolver.resolve(DeployOrder.UNDEPLOY, LockOrder.NONE, DeployState.DEPLOYING,
-                LockState.NONE);
+                LockState.NONE, StateChangeResult.NO_ERROR);
         assertThat(result).isEqualTo(AcInstanceStateResolver.NONE);
 
-        result = acTypeStateResolver.resolve(null, null, null, null);
+        result = acTypeStateResolver.resolve(null, null, null, null, null);
         assertThat(result).isEqualTo(AcInstanceStateResolver.NONE);
     }
 
index 9087054..514bc65 100644 (file)
@@ -209,8 +209,11 @@ public class AutomationCompositionHandler {
 
     private boolean checkConsistantOrderState(AutomationComposition automationComposition, DeployOrder deployOrder,
             LockOrder lockOrder) {
+        if (DeployOrder.UPDATE.equals(deployOrder)) {
+            return true;
+        }
         return acInstanceStateResolver.resolve(deployOrder, lockOrder, automationComposition.getDeployState(),
-                automationComposition.getLockState()) != null;
+                automationComposition.getLockState(), automationComposition.getStateChangeResult()) != null;
     }
 
     /**
index 7875478..eb0c9b7 100644 (file)
@@ -21,8 +21,6 @@
 
 package org.onap.policy.clamp.acm.runtime.instantiation;
 
-
-import java.util.Map;
 import java.util.UUID;
 import javax.validation.Valid;
 import javax.ws.rs.core.Response;
@@ -31,7 +29,6 @@ import lombok.AllArgsConstructor;
 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.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;
@@ -126,12 +123,13 @@ public class AutomationCompositionInstantiationProvider {
             response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
             return response;
 
-        } else if (DeployState.DEPLOYED.equals(acToUpdate.getDeployState())
+        } else if ((DeployState.DEPLOYED.equals(acToUpdate.getDeployState())
+                || DeployState.UPDATING.equals(acToUpdate.getDeployState()))
                 && LockState.LOCKED.equals(acToUpdate.getLockState())) {
             return updateDeployedAutomationComposition(compositionId, automationComposition, acToUpdate);
         }
         throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
-                    "Not allowed to update in the state " + acToUpdate.getDeployState());
+                "Not allowed to update in the state " + acToUpdate.getDeployState());
     }
 
     /**
@@ -143,24 +141,24 @@ public class AutomationCompositionInstantiationProvider {
      * @return the result of the update
      */
     public InstantiationResponse updateDeployedAutomationComposition(UUID compositionId,
-                                                                     AutomationComposition automationComposition,
-                                                                     AutomationComposition acToBeUpdated) {
+            AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
 
         // Iterate and update the element property values
-        for (Map.Entry<UUID, AutomationCompositionElement> dbAcElement : acToBeUpdated.getElements().entrySet()) {
+        for (var dbAcElement : acToBeUpdated.getElements().entrySet()) {
             var elementId = dbAcElement.getKey();
             if (automationComposition.getElements().containsKey(elementId)) {
-                dbAcElement.getValue().getProperties().putAll(automationComposition.getElements().get(elementId)
-                        .getProperties());
+                dbAcElement.getValue().getProperties()
+                        .putAll(automationComposition.getElements().get(elementId).getProperties());
             }
         }
-        // Publish property update event to the participants
-        supervisionAcHandler.update(acToBeUpdated);
-
         var validationResult = validateAutomationComposition(acToBeUpdated);
         if (!validationResult.isValid()) {
             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, validationResult.getResult());
         }
+
+        // Publish property update event to the participants
+        supervisionAcHandler.update(acToBeUpdated);
+
         automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
         var response = new InstantiationResponse();
         var instanceId = automationComposition.getInstanceId();
@@ -169,7 +167,6 @@ public class AutomationCompositionInstantiationProvider {
         return response;
     }
 
-
     /**
      * Validate AutomationComposition.
      *
@@ -234,7 +231,8 @@ public class AutomationCompositionInstantiationProvider {
             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
                     automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
         }
-        if (!DeployState.UNDEPLOYED.equals(automationComposition.getDeployState())) {
+        if (!DeployState.UNDEPLOYED.equals(automationComposition.getDeployState())
+                && !DeployState.DELETING.equals(automationComposition.getDeployState())) {
             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
                     "Automation composition state is still " + automationComposition.getDeployState());
         }
@@ -279,7 +277,7 @@ public class AutomationCompositionInstantiationProvider {
         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
         var result = acInstanceStateResolver.resolve(acInstanceStateUpdate.getDeployOrder(),
                 acInstanceStateUpdate.getLockOrder(), automationComposition.getDeployState(),
-                automationComposition.getLockState());
+                automationComposition.getLockState(), automationComposition.getStateChangeResult());
         switch (result) {
             case "DEPLOY":
                 supervisionAcHandler.deploy(automationComposition, acDefinition);
index da3cbc3..e9f8d6c 100644 (file)
@@ -34,6 +34,7 @@ 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.ParticipantUtils;
+import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionDeployAck;
 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
@@ -55,7 +56,6 @@ public class SupervisionAcHandler {
     // Publishers for participant communication
     private final AutomationCompositionDeployPublisher automationCompositionDeployPublisher;
     private final AutomationCompositionStateChangePublisher automationCompositionStateChangePublisher;
-
     private final AcElementPropertiesPublisher acElementPropertiesPublisher;
 
     /**
@@ -65,7 +65,17 @@ public class SupervisionAcHandler {
      * @param acDefinition the AutomationCompositionDefinition
      */
     public void deploy(AutomationComposition automationComposition, AutomationCompositionDefinition acDefinition) {
-        AcmUtils.setCascadedState(automationComposition, DeployState.DEPLOYING, LockState.NONE);
+        if (StateChangeResult.FAILED.equals(automationComposition.getStateChangeResult())) {
+            automationComposition.setDeployState(DeployState.DEPLOYING);
+            for (var element : automationComposition.getElements().values()) {
+                if (!DeployState.DEPLOYED.equals(element.getDeployState())) {
+                    element.setDeployState(DeployState.DEPLOYING);
+                }
+            }
+        } else {
+            AcmUtils.setCascadedState(automationComposition, DeployState.DEPLOYING, LockState.NONE);
+        }
+        automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
         automationCompositionProvider.updateAutomationComposition(automationComposition);
         var startPhase = ParticipantUtils.getFirstStartPhase(automationComposition, acDefinition.getServiceTemplate());
         automationCompositionDeployPublisher.send(automationComposition, acDefinition.getServiceTemplate(), startPhase,
@@ -79,7 +89,17 @@ public class SupervisionAcHandler {
      * @param acDefinition the AutomationCompositionDefinition
      */
     public void undeploy(AutomationComposition automationComposition, AutomationCompositionDefinition acDefinition) {
-        AcmUtils.setCascadedState(automationComposition, DeployState.UNDEPLOYING, LockState.NONE);
+        if (StateChangeResult.FAILED.equals(automationComposition.getStateChangeResult())) {
+            automationComposition.setDeployState(DeployState.UNDEPLOYING);
+            for (var element : automationComposition.getElements().values()) {
+                if (!DeployState.UNDEPLOYED.equals(element.getDeployState())) {
+                    element.setDeployState(DeployState.UNDEPLOYING);
+                }
+            }
+        } else {
+            AcmUtils.setCascadedState(automationComposition, DeployState.UNDEPLOYING, LockState.NONE);
+        }
+        automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
         automationCompositionProvider.updateAutomationComposition(automationComposition);
         var startPhase = ParticipantUtils.getFirstStartPhase(automationComposition, acDefinition.getServiceTemplate());
         automationCompositionStateChangePublisher.send(automationComposition, startPhase, true);
@@ -92,7 +112,17 @@ public class SupervisionAcHandler {
      * @param acDefinition the AutomationCompositionDefinition
      */
     public void unlock(AutomationComposition automationComposition, AutomationCompositionDefinition acDefinition) {
-        AcmUtils.setCascadedState(automationComposition, DeployState.DEPLOYED, LockState.UNLOCKING);
+        if (StateChangeResult.FAILED.equals(automationComposition.getStateChangeResult())) {
+            automationComposition.setLockState(LockState.UNLOCKING);
+            for (var element : automationComposition.getElements().values()) {
+                if (!LockState.UNLOCKED.equals(element.getLockState())) {
+                    element.setLockState(LockState.UNLOCKING);
+                }
+            }
+        } else {
+            AcmUtils.setCascadedState(automationComposition, DeployState.DEPLOYED, LockState.UNLOCKING);
+        }
+        automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
         automationCompositionProvider.updateAutomationComposition(automationComposition);
         var startPhase = ParticipantUtils.getFirstStartPhase(automationComposition, acDefinition.getServiceTemplate());
         automationCompositionStateChangePublisher.send(automationComposition, startPhase, true);
@@ -105,7 +135,17 @@ public class SupervisionAcHandler {
      * @param acDefinition the AutomationCompositionDefinition
      */
     public void lock(AutomationComposition automationComposition, AutomationCompositionDefinition acDefinition) {
-        AcmUtils.setCascadedState(automationComposition, DeployState.DEPLOYED, LockState.LOCKING);
+        if (StateChangeResult.FAILED.equals(automationComposition.getStateChangeResult())) {
+            automationComposition.setLockState(LockState.LOCKING);
+            for (var element : automationComposition.getElements().values()) {
+                if (!LockState.LOCKED.equals(element.getLockState())) {
+                    element.setLockState(LockState.LOCKING);
+                }
+            }
+        } else {
+            AcmUtils.setCascadedState(automationComposition, DeployState.DEPLOYED, LockState.LOCKING);
+        }
+        automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
         automationCompositionProvider.updateAutomationComposition(automationComposition);
         var startPhase = ParticipantUtils.getFirstStartPhase(automationComposition, acDefinition.getServiceTemplate());
         automationCompositionStateChangePublisher.send(automationComposition, startPhase, true);
@@ -113,10 +153,12 @@ public class SupervisionAcHandler {
 
     /**
      * Handle Element property update on a deployed instance.
+     *
      * @param automationComposition the AutomationComposition
      */
     public void update(AutomationComposition automationComposition) {
-        AcmUtils.setCascadedState(automationComposition, DeployState.UPDATING, LockState.NONE);
+        AcmUtils.setCascadedState(automationComposition, DeployState.UPDATING, automationComposition.getLockState());
+        automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
         acElementPropertiesPublisher.send(automationComposition);
     }
 
@@ -128,6 +170,7 @@ public class SupervisionAcHandler {
      */
     public void delete(AutomationComposition automationComposition, AutomationCompositionDefinition acDefinition) {
         AcmUtils.setCascadedState(automationComposition, DeployState.DELETING, LockState.NONE);
+        automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
         automationCompositionProvider.updateAutomationComposition(automationComposition);
         var startPhase = ParticipantUtils.getFirstStartPhase(automationComposition, acDefinition.getServiceTemplate());
         automationCompositionStateChangePublisher.send(automationComposition, startPhase, true);
@@ -176,8 +219,11 @@ public class SupervisionAcHandler {
                 || automationCompositionAckMessage.getAutomationCompositionResultMap().isEmpty()) {
             if (DeployState.DELETING.equals(automationComposition.get().getDeployState())) {
                 // scenario when Automation Composition instance has never been deployed
-                automationComposition.get().getElements().values()
-                        .forEach(element -> element.setDeployState(DeployState.DELETED));
+                for (var element : automationComposition.get().getElements().values()) {
+                    if (element.getParticipantId().equals(automationCompositionAckMessage.getParticipantId())) {
+                        element.setDeployState(DeployState.DELETED);
+                    }
+                }
                 automationCompositionProvider.updateAutomationComposition(automationComposition.get());
             } else {
                 LOGGER.warn("Empty AutomationCompositionResultMap  {} {}",
@@ -188,26 +234,37 @@ public class SupervisionAcHandler {
         }
 
         var updated = updateState(automationComposition.get(),
-                automationCompositionAckMessage.getAutomationCompositionResultMap().entrySet());
+                automationCompositionAckMessage.getAutomationCompositionResultMap().entrySet(),
+                automationCompositionAckMessage.getStateChangeResult());
         if (updated) {
             automationCompositionProvider.updateAutomationComposition(automationComposition.get());
         }
     }
 
     private boolean updateState(AutomationComposition automationComposition,
-            Set<Map.Entry<UUID, AcElementDeployAck>> automationCompositionResultSet) {
+            Set<Map.Entry<UUID, AcElementDeployAck>> automationCompositionResultSet,
+            StateChangeResult stateChangeResult) {
         var updated = false;
+        var elementInErrors = StateChangeResult.FAILED.equals(stateChangeResult);
+        boolean inProgress = StateChangeResult.NO_ERROR.equals(automationComposition.getStateChangeResult());
+
         for (var acElementAck : automationCompositionResultSet) {
             var element = automationComposition.getElements().get(acElementAck.getKey());
             if (element != null) {
-                element.setDeployState(acElementAck.getValue().getDeployState());
-                element.setLockState(acElementAck.getValue().getLockState());
+                element.setMessage(acElementAck.getValue().getMessage());
+                element.setOutProperties(acElementAck.getValue().getOutProperties());
                 element.setOperationalState(acElementAck.getValue().getOperationalState());
                 element.setUseState(acElementAck.getValue().getUseState());
-                element.setOutProperties(acElementAck.getValue().getOutProperties());
                 updated = true;
+                if (!elementInErrors || inProgress) {
+                    element.setDeployState(acElementAck.getValue().getDeployState());
+                    element.setLockState(acElementAck.getValue().getLockState());
+                }
             }
         }
+        if (elementInErrors && inProgress) {
+            automationComposition.setStateChangeResult(stateChangeResult.FAILED);
+        }
         return updated;
     }
 }
index e23d79d..803bc86 100644 (file)
@@ -138,6 +138,11 @@ public class SupervisionScanner {
             LOGGER.debug("automation composition scan: transition from state {} to {} not completed",
                     automationComposition.getDeployState(), automationComposition.getLockState());
 
+            if (DeployState.UPDATING.equals(automationComposition.getDeployState())) {
+                // UPDATING do not need phases
+                return;
+            }
+
             var isForward =
                     AcmUtils.isForward(automationComposition.getDeployState(), automationComposition.getLockState());
 
index e763127..6988cd3 100644 (file)
@@ -42,6 +42,7 @@ 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.DeployState;
 import org.onap.policy.clamp.models.acm.concepts.LockState;
+import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionDeployAck;
 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantMessageType;
 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
@@ -59,8 +60,7 @@ class SupervisionAcHandlerTest {
                 .thenReturn(Optional.of(automationComposition));
 
         var handler = new SupervisionAcHandler(automationCompositionProvider,
-                mock(AutomationCompositionDeployPublisher.class),
-                mock(AutomationCompositionStateChangePublisher.class),
+                mock(AutomationCompositionDeployPublisher.class), mock(AutomationCompositionStateChangePublisher.class),
                 mock(AcElementPropertiesPublisher.class));
 
         var automationCompositionAckMessage =
@@ -98,8 +98,7 @@ class SupervisionAcHandlerTest {
         automationCompositionAckMessage.setAutomationCompositionId(IDENTIFIER);
 
         var handler = new SupervisionAcHandler(automationCompositionProvider,
-                mock(AutomationCompositionDeployPublisher.class),
-                mock(AutomationCompositionStateChangePublisher.class),
+                mock(AutomationCompositionDeployPublisher.class), mock(AutomationCompositionStateChangePublisher.class),
                 mock(AcElementPropertiesPublisher.class));
 
         handler.handleAutomationCompositionUpdateAckMessage(automationCompositionAckMessage);
@@ -107,13 +106,66 @@ class SupervisionAcHandlerTest {
         verify(automationCompositionProvider).updateAutomationComposition(any(AutomationComposition.class));
     }
 
+    @Test
+    void testHandleAcUpdateAckFailedMessage() {
+        var automationCompositionProvider = mock(AutomationCompositionProvider.class);
+        var automationComposition =
+                InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Crud");
+        automationComposition.setDeployState(DeployState.DEPLOYING);
+        automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
+        when(automationCompositionProvider.findAutomationComposition(IDENTIFIER))
+                .thenReturn(Optional.of(automationComposition));
+
+        var automationCompositionAckMessage =
+                new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_DEPLOY_ACK);
+        for (var element : automationComposition.getElements().values()) {
+            element.setDeployState(DeployState.DEPLOYED);
+        }
+        var elementEntry = automationComposition.getElements().entrySet().iterator().next();
+        elementEntry.getValue().setDeployState(DeployState.DEPLOYING);
+        var acElementDeployAck =
+                new AcElementDeployAck(DeployState.UNDEPLOYED, LockState.NONE, "", "", Map.of(), true, "Error");
+        automationCompositionAckMessage
+                .setAutomationCompositionResultMap(Map.of(elementEntry.getKey(), acElementDeployAck));
+
+        automationCompositionAckMessage.setParticipantId(CommonTestData.getParticipantId());
+        automationCompositionAckMessage.setAutomationCompositionId(IDENTIFIER);
+
+        var automationCompositionStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
+
+        var handler = new SupervisionAcHandler(automationCompositionProvider,
+                mock(AutomationCompositionDeployPublisher.class), automationCompositionStateChangePublisher, null);
+
+        handler.handleAutomationCompositionUpdateAckMessage(automationCompositionAckMessage);
+
+        verify(automationCompositionProvider).updateAutomationComposition(any(AutomationComposition.class));
+    }
+
+    @Test
+    void testDeployFailed() {
+        var automationCompositionDeployPublisher = mock(AutomationCompositionDeployPublisher.class);
+        var automationCompositionProvider = mock(AutomationCompositionProvider.class);
+        var handler = new SupervisionAcHandler(automationCompositionProvider, automationCompositionDeployPublisher,
+                mock(AutomationCompositionStateChangePublisher.class), mock(AcElementPropertiesPublisher.class));
+
+        var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+        var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED);
+        var automationComposition =
+                InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Deploy");
+        automationComposition.setStateChangeResult(StateChangeResult.FAILED);
+        handler.deploy(automationComposition, acDefinition);
+        verify(automationCompositionProvider).updateAutomationComposition(automationComposition);
+        verify(automationCompositionDeployPublisher).send(automationComposition, acDefinition.getServiceTemplate(), 0,
+                true);
+    }
+
     @Test
     void testUndeploy() {
         var automationCompositionProvider = mock(AutomationCompositionProvider.class);
         var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
         var handler = new SupervisionAcHandler(automationCompositionProvider,
-                mock(AutomationCompositionDeployPublisher.class),
-                acStateChangePublisher, mock(AcElementPropertiesPublisher.class));
+                mock(AutomationCompositionDeployPublisher.class), acStateChangePublisher,
+                mock(AcElementPropertiesPublisher.class));
         var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
         var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED);
         var automationComposition =
@@ -124,17 +176,56 @@ class SupervisionAcHandlerTest {
         verify(acStateChangePublisher).send(any(AutomationComposition.class), anyInt(), anyBoolean());
     }
 
+    @Test
+    void testUndeployFailed() {
+        var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
+        var automationCompositionProvider = mock(AutomationCompositionProvider.class);
+        var handler = new SupervisionAcHandler(automationCompositionProvider,
+                mock(AutomationCompositionDeployPublisher.class), acStateChangePublisher,
+                mock(AcElementPropertiesPublisher.class));
+
+        var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+        var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED);
+        var automationComposition =
+                InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "UnDeploy");
+        automationComposition.setStateChangeResult(StateChangeResult.FAILED);
+        automationComposition.getElements().values()
+                .forEach(element -> element.setDeployState(DeployState.UNDEPLOYING));
+        handler.undeploy(automationComposition, acDefinition);
+        verify(automationCompositionProvider).updateAutomationComposition(automationComposition);
+        verify(acStateChangePublisher).send(any(AutomationComposition.class), anyInt(), anyBoolean());
+    }
+
     @Test
     void testUnlock() {
         var automationCompositionProvider = mock(AutomationCompositionProvider.class);
         var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
         var handler = new SupervisionAcHandler(automationCompositionProvider,
-                mock(AutomationCompositionDeployPublisher.class),
-                acStateChangePublisher, mock(AcElementPropertiesPublisher.class));
+                mock(AutomationCompositionDeployPublisher.class), acStateChangePublisher,
+                mock(AcElementPropertiesPublisher.class));
+        var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+        var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED);
+        var automationComposition =
+                InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "UnLock");
+        handler.unlock(automationComposition, acDefinition);
+
+        verify(automationCompositionProvider).updateAutomationComposition(any(AutomationComposition.class));
+        verify(acStateChangePublisher).send(any(AutomationComposition.class), anyInt(), anyBoolean());
+    }
+
+    @Test
+    void testUnlockFailed() {
+        var automationCompositionProvider = mock(AutomationCompositionProvider.class);
+        var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
+        var handler = new SupervisionAcHandler(automationCompositionProvider,
+                mock(AutomationCompositionDeployPublisher.class), acStateChangePublisher,
+                mock(AcElementPropertiesPublisher.class));
         var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
         var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED);
         var automationComposition =
                 InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "UnLock");
+        automationComposition.setStateChangeResult(StateChangeResult.FAILED);
+        automationComposition.getElements().values().forEach(element -> element.setLockState(LockState.UNLOCKING));
         handler.unlock(automationComposition, acDefinition);
 
         verify(automationCompositionProvider).updateAutomationComposition(any(AutomationComposition.class));
@@ -146,15 +237,70 @@ class SupervisionAcHandlerTest {
         var automationCompositionProvider = mock(AutomationCompositionProvider.class);
         var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
         var handler = new SupervisionAcHandler(automationCompositionProvider,
-                mock(AutomationCompositionDeployPublisher.class),
-                acStateChangePublisher, mock(AcElementPropertiesPublisher.class));
+                mock(AutomationCompositionDeployPublisher.class), acStateChangePublisher,
+                mock(AcElementPropertiesPublisher.class));
+        var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
+        var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED);
+        var automationComposition =
+                InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Lock");
+        handler.lock(automationComposition, acDefinition);
+
+        verify(automationCompositionProvider).updateAutomationComposition(any(AutomationComposition.class));
+        verify(acStateChangePublisher).send(any(AutomationComposition.class), anyInt(), anyBoolean());
+    }
+
+    @Test
+    void testLockFailed() {
+        var automationCompositionProvider = mock(AutomationCompositionProvider.class);
+        var acStateChangePublisher = mock(AutomationCompositionStateChangePublisher.class);
+        var handler = new SupervisionAcHandler(automationCompositionProvider,
+                mock(AutomationCompositionDeployPublisher.class), acStateChangePublisher,
+                mock(AcElementPropertiesPublisher.class));
         var serviceTemplate = InstantiationUtils.getToscaServiceTemplate(TOSCA_SERVICE_TEMPLATE_YAML);
         var acDefinition = CommonTestData.createAcDefinition(serviceTemplate, AcTypeState.PRIMED);
         var automationComposition =
                 InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Lock");
+        automationComposition.setStateChangeResult(StateChangeResult.FAILED);
+        automationComposition.getElements().values().forEach(element -> element.setLockState(LockState.LOCKING));
         handler.lock(automationComposition, acDefinition);
 
         verify(automationCompositionProvider).updateAutomationComposition(any(AutomationComposition.class));
         verify(acStateChangePublisher).send(any(AutomationComposition.class), anyInt(), anyBoolean());
     }
+
+    @Test
+    void testDeleteAck() {
+        var automationCompositionProvider = mock(AutomationCompositionProvider.class);
+        var automationComposition =
+                InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Crud");
+        automationComposition.setDeployState(DeployState.DELETING);
+        when(automationCompositionProvider.findAutomationComposition(IDENTIFIER))
+                .thenReturn(Optional.of(automationComposition));
+
+        var automationCompositionAckMessage =
+                new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_DEPLOY_ACK);
+        automationCompositionAckMessage
+                .setParticipantId(automationComposition.getElements().values().iterator().next().getParticipantId());
+        automationCompositionAckMessage.setAutomationCompositionId(IDENTIFIER);
+
+        var handler = new SupervisionAcHandler(automationCompositionProvider,
+                mock(AutomationCompositionDeployPublisher.class), mock(AutomationCompositionStateChangePublisher.class),
+                mock(AcElementPropertiesPublisher.class));
+
+        handler.handleAutomationCompositionUpdateAckMessage(automationCompositionAckMessage);
+
+        verify(automationCompositionProvider).updateAutomationComposition(any(AutomationComposition.class));
+    }
+
+    @Test
+    void testUpdate() {
+        var acElementPropertiesPublisher = mock(AcElementPropertiesPublisher.class);
+        var handler = new SupervisionAcHandler(mock(AutomationCompositionProvider.class),
+                mock(AutomationCompositionDeployPublisher.class), mock(AutomationCompositionStateChangePublisher.class),
+                acElementPropertiesPublisher);
+        var automationComposition =
+                InstantiationUtils.getAutomationCompositionFromResource(AC_INSTANTIATION_CREATE_JSON, "Lock");
+        handler.update(automationComposition);
+        verify(acElementPropertiesPublisher).send(any(AutomationComposition.class));
+    }
 }