rApp instance status sync on all rApp GET request 55/14855/1
authoraravind.est <aravindhan.a@est.tech>
Thu, 28 Aug 2025 12:57:32 +0000 (13:57 +0100)
committeraravind.est <aravindhan.a@est.tech>
Thu, 28 Aug 2025 12:58:48 +0000 (13:58 +0100)
rApp instance status is synchronized on all the rApp/rApp instance get requests

Issue-ID: NONRTRIC-1087
Change-Id: Ib538666f428f876b14bf0f5092e8872c7308bdab
Signed-off-by: aravind.est <aravindhan.a@est.tech>
rapp-manager-acm/src/main/java/org/oransc/rappmanager/acm/service/AcmDeployer.java
rapp-manager-acm/src/test/java/org/oransc/rappmanager/acm/service/AcmDeployerTest.java
rapp-manager-application/src/main/java/org/oransc/rappmanager/rest/RappController.java
rapp-manager-application/src/main/java/org/oransc/rappmanager/rest/RappInstanceController.java
rapp-manager-application/src/main/java/org/oransc/rappmanager/service/RappService.java
rapp-manager-application/src/test/java/org/oransc/rappmanager/rest/RappControllerTest.java
rapp-manager-application/src/test/java/org/oransc/rappmanager/rest/RappInstanceControllerTest.java
sample-rapp-generator/rapp-sample-ics-consumer/Files/Acm/instances/k8s-instance.json
sample-rapp-generator/rapp-sample-ics-producer/Files/Acm/instances/k8s-instance.json

index 9016aae..2eb230d 100644 (file)
@@ -1,7 +1,7 @@
 /*-
  * ============LICENSE_START======================================================================
  * Copyright (C) 2023 Nordix Foundation. All rights reserved.
- * Copyright (C) 2023-2024 OpenInfra Foundation Europe. All rights reserved.
+ * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved.
  * ===============================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -170,6 +170,7 @@ public class AcmDeployer implements RappDeployer {
                         automationComposition.getCompositionId(), automationComposition.getInstanceId(),
                         UUID.randomUUID());
                 rappInstanceStateMachine.sendRappInstanceEvent(rappInstance, RappEvent.ACMUNDEPLOYED);
+                rappInstance.getAcm().setAcmInstanceId(null);
                 return true;
             }
         }
@@ -243,7 +244,7 @@ public class AcmDeployer implements RappDeployer {
     }
 
     public void syncRappInstanceStatus(UUID compositionId, RappInstance rappInstance) {
-        if (rappInstance.getAcm().getAcmInstanceId() != null) {
+        if (rappInstance.getAcm() != null && rappInstance.getAcm().getAcmInstanceId() != null) {
             try {
                 AutomationComposition compositionInstance =
                         automationCompositionInstanceApiClient.getCompositionInstance(compositionId,
index 99f9302..f6a405d 100644 (file)
@@ -267,6 +267,7 @@ class AcmDeployerTest {
         boolean rappUndeployStateActual = acmDeployer.undeployRappInstance(rapp, rappInstance);
         mockServer.verify();
         assertTrue(rappUndeployStateActual);
+        assertNull(rappInstance.getAcm().getAcmInstanceId());
     }
 
     @Test
@@ -354,6 +355,26 @@ class AcmDeployerTest {
         verify(rappInstanceStateMachine, never()).sendRappInstanceEvent(any(), any());
     }
 
+    @Test
+    void testSyncRappStatusFailureAcmNull() {
+        UUID compositionId = UUID.randomUUID();
+        RappInstance rappInstance = rappResourceBuilder.getRappInstance();
+        rappInstance.setAcm(null);
+        rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+        acmDeployer.syncRappInstanceStatus(compositionId, rappInstance);
+        verify(rappInstanceStateMachine, never()).sendRappInstanceEvent(any(), any());
+    }
+
+    @Test
+    void testSyncRappStatusFailureAcmInstanceIdNull() {
+        UUID compositionId = UUID.randomUUID();
+        RappInstance rappInstance = rappResourceBuilder.getRappInstance();
+        rappInstance.getAcm().setAcmInstanceId(null);
+        rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+        acmDeployer.syncRappInstanceStatus(compositionId, rappInstance);
+        verify(rappInstanceStateMachine, never()).sendRappInstanceEvent(any(), any());
+    }
+
     @Test
     void testPrimeRapp() throws JsonProcessingException {
         UUID compositionId = UUID.randomUUID();
index 3af2bd9..44d3083 100644 (file)
@@ -37,8 +37,6 @@ import org.oransc.rappmanager.models.rapp.Rapp;
 import org.oransc.rappmanager.models.rapp.RappPrimeOrder;
 import org.oransc.rappmanager.models.rapp.RappState;
 import org.oransc.rappmanager.service.RappService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -57,7 +55,6 @@ import org.springframework.web.multipart.MultipartFile;
 @RequiredArgsConstructor
 public class RappController {
 
-    Logger logger = LoggerFactory.getLogger(RappController.class);
     private final RappCsarConfigurationHandler rappCsarConfigurationHandler;
     private final RappValidationHandler rappValidationHandler;
     private final RappManagerConfiguration rappManagerConfiguration;
@@ -67,12 +64,12 @@ public class RappController {
 
     @GetMapping
     public ResponseEntity<Collection<Rapp>> getRapps() {
-        return ResponseEntity.ok(rappCacheService.getAllRapp());
+        return ResponseEntity.ok(rappService.syncRappStates(rappCacheService.getAllRapp()));
     }
 
     @GetMapping("{rapp_id}")
     public ResponseEntity<Rapp> getRapp(@PathVariable("rapp_id") String rappId) {
-        return rappCacheService.getRapp(rappId).map(ResponseEntity::ok).orElseThrow(
+        return rappCacheService.getRapp(rappId).map(rappService::syncRappState).map(ResponseEntity::ok).orElseThrow(
                 () -> new RappHandlerException(HttpStatus.NOT_FOUND, String.format(RAPP_NOT_FOUND, rappId)));
     }
 
index c2fc36f..54002ed 100644 (file)
@@ -58,8 +58,9 @@ public class RappInstanceController {
 
     @GetMapping
     public ResponseEntity<Map<UUID, RappInstance>> getAllRappInstances(@PathVariable("rapp_id") String rappId) {
-        return rappCacheService.getRapp(rappId).map(Rapp::getRappInstances).map(ResponseEntity::ok).orElseThrow(
-                () -> new RappHandlerException(HttpStatus.NOT_FOUND, "No instance found for rApp '" + rappId + "'."));
+        return rappCacheService.getRapp(rappId).map(rappService::syncRappState).map(Rapp::getRappInstances)
+                       .map(ResponseEntity::ok).orElseThrow(() -> new RappHandlerException(HttpStatus.NOT_FOUND,
+                        "No instance found for rApp '" + rappId + "'."));
     }
 
     @PostMapping
index 241c95d..8ce68b5 100644 (file)
@@ -1,7 +1,7 @@
 /*-
  * ============LICENSE_START======================================================================
  * Copyright (C) 2023 Nordix Foundation. All rights reserved.
- * Copyright (C) 2023-2024 OpenInfra Foundation Europe. All rights reserved.
+ * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved.
  * ===============================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
 
 package org.oransc.rappmanager.service;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.UUID;
 import lombok.RequiredArgsConstructor;
@@ -145,6 +146,7 @@ public class RappService {
         if (rApp.getRappInstances().get(rappInstanceId).getState().equals(RappInstanceState.UNDEPLOYED)) {
             rappInstanceStateMachine.deleteRappInstance(rApp.getRappInstances().get(rappInstanceId));
             rApp.getRappInstances().remove(rappInstanceId);
+            rappCacheService.putRapp(rApp);
             return ResponseEntity.noContent().build();
         }
         throw new RappHandlerException(HttpStatus.BAD_REQUEST,
@@ -154,4 +156,17 @@ public class RappService {
     public void updateRappInstanceState(Rapp rapp, RappInstance rappInstance) {
         acmDeployer.syncRappInstanceStatus(rapp.getCompositionId(), rappInstance);
     }
+
+    public Collection<Rapp> syncRappStates(Collection<Rapp> rapps) {
+        rapps.forEach(this::syncRappState);
+        return rapps;
+    }
+
+    public Rapp syncRappState(Rapp rapp) {
+        rapp.getRappInstances().forEach((instanceId, instance) -> {
+            updateRappInstanceState(rapp, instance);
+            instance.setState(rappInstanceStateMachine.getRappInstanceState(instanceId));
+        });
+        return rapp;
+    }
 }
index 0cd6de1..c5de92e 100644 (file)
@@ -28,6 +28,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import java.io.File;
 import java.io.FileInputStream;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -41,6 +42,9 @@ import org.oransc.rappmanager.models.rapp.PrimeOrder;
 import org.oransc.rappmanager.models.rapp.Rapp;
 import org.oransc.rappmanager.models.rapp.RappPrimeOrder;
 import org.oransc.rappmanager.models.rapp.RappState;
+import org.oransc.rappmanager.models.rappinstance.RappInstance;
+import org.oransc.rappmanager.models.rappinstance.RappInstanceState;
+import org.oransc.rappmanager.models.statemachine.RappInstanceStateMachine;
 import org.oransc.rappmanager.sme.service.SmeLifecycleManager;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
@@ -61,6 +65,9 @@ class RappControllerTest {
     @Autowired
     private RappCacheService rappCacheService;
 
+    @Autowired
+    RappInstanceStateMachine rappInstanceStateMachine;
+
     @MockitoBean
     AcmDeployer acmDeployer;
 
@@ -80,32 +87,48 @@ class RappControllerTest {
         mockMvc.perform(MockMvcRequestBuilders.get("/rapps")).andExpect(status().isOk())
                 .andExpect(jsonPath("$", hasSize(0)));
         UUID rappId = UUID.randomUUID();
+        UUID instanceId = UUID.randomUUID();
+        RappInstance instance = new RappInstance();
+        instance.setRappInstanceId(instanceId);
+        instance.setState(RappInstanceState.UNDEPLOYED);
+        Map<UUID, RappInstance> instances = Map.of(instanceId, instance);
         Rapp rapp = Rapp.builder().rappId(rappId).name(String.valueOf(rappId)).packageName(validRappFile)
-                            .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build();
+                            .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).rappInstances(instances).build();
         AsdMetadata asdMetadata = new AsdMetadata();
         asdMetadata.setDescriptorId(UUID.randomUUID().toString());
         asdMetadata.setDescriptorInvariantId(UUID.randomUUID().toString());
         asdMetadata.setDeploymentItems(List.of());
         rapp.setAsdMetadata(asdMetadata);
         rappCacheService.putRapp(rapp);
+        rappInstanceStateMachine.onboardRappInstance(instanceId);
         mockMvc.perform(MockMvcRequestBuilders.get("/rapps")).andExpect(status().isOk())
-                .andExpect(jsonPath("$", hasSize(1)));
+                .andExpect(jsonPath("$", hasSize(1)))
+                .andExpect(jsonPath("$[0].rappInstances." + instanceId + ".rappInstanceId").value(instanceId.toString()))
+                .andExpect(jsonPath("$[0].rappInstances." + instanceId + ".state").value(instance.getState().name()));
     }
 
     @Test
     void testGetRapp() throws Exception {
         UUID rappId = UUID.randomUUID();
+        UUID instanceId = UUID.randomUUID();
+        RappInstance instance = new RappInstance();
+        instance.setRappInstanceId(instanceId);
+        instance.setState(RappInstanceState.UNDEPLOYED);
+        Map<UUID, RappInstance> instances = Map.of(instanceId, instance);
         Rapp rapp = Rapp.builder().rappId(rappId).name(String.valueOf(rappId)).packageName(validRappFile)
-                            .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build();
+                            .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).rappInstances(instances).build();
         AsdMetadata asdMetadata = new AsdMetadata();
         asdMetadata.setDescriptorId(UUID.randomUUID().toString());
         asdMetadata.setDescriptorInvariantId(UUID.randomUUID().toString());
         asdMetadata.setDeploymentItems(List.of());
         rapp.setAsdMetadata(asdMetadata);
         rappCacheService.putRapp(rapp);
+        rappInstanceStateMachine.onboardRappInstance(instanceId);
         mockMvc.perform(MockMvcRequestBuilders.get("/rapps/{rapp_id}", rappId)).andExpect(status().isOk())
                 .andExpect(jsonPath("$.rappId").value(rappId.toString()))
-                .andExpect(jsonPath("$.state").value(RappState.COMMISSIONED.name()));
+                .andExpect(jsonPath("$.state").value(RappState.COMMISSIONED.name()))
+                .andExpect(jsonPath("$.rappInstances." + instanceId + ".rappInstanceId").value(instanceId.toString()))
+                .andExpect(jsonPath("$.rappInstances." + instanceId + ".state").value(instance.getState().name()));
     }
 
     @Test
index 0ef098f..474fd09 100644 (file)
@@ -91,6 +91,7 @@ class RappInstanceControllerTest {
         UUID rappInstanceId = UUID.randomUUID();
         Rapp rapp = getRapp(rappId, rappInstanceId);
         rappCacheService.putRapp(rapp);
+        rappInstanceStateMachine.onboardRappInstance(rappInstanceId);
         mockMvc.perform(MockMvcRequestBuilders.get("/rapps/{rapp_id}/instance", rappId)).andExpect(status().isOk())
                 .andExpect(
                         jsonPath("$." + rappInstanceId.toString() + ".rappInstanceId").value(rappInstanceId.toString()))
index 6748b29..87116cd 100644 (file)
@@ -19,7 +19,7 @@
           },
           "namespace": "nonrtric",
           "releaseName": "ics-consumer",
-          "podName": "ics-consumer",
+          "podName": "kafka-consumer",
           "repository": {
             "repoName": "local",
             "address": "UPDATE_THIS_CHART_MUSEUM_GET_CHARTS_URI"
index c04f816..ef9338e 100644 (file)
@@ -19,7 +19,7 @@
           },
           "namespace": "nonrtric",
           "releaseName": "ics-producer",
-          "podName": "ics-producer",
+          "podName": "kafka-producer",
           "repository": {
             "repoName": "local",
             "address": "UPDATE_THIS_CHART_MUSEUM_GET_CHARTS_URI"