Add status check between ACM depriming and deletion
Openapi maven version upgraded.
ACM model library version upgraded.
Status check added between depriming and deletion.
Code coverage improvement in ACM.
Issue-ID: NONRTRIC-929
Signed-off-by: aravind.est <aravindhan.a@est.tech>
Change-Id: I44a47145141f50089a92a34276c754ab3e6ab5e6
diff --git a/pom.xml b/pom.xml
index 0a8f1d1..6981e08 100755
--- a/pom.xml
+++ b/pom.xml
@@ -48,10 +48,10 @@
</repositories>
<properties>
<java.version>17</java.version>
- <openapi.maven.version>6.6.0</openapi.maven.version>
+ <openapi.maven.version>7.0.1</openapi.maven.version>
<docker-maven-plugin>0.30.0</docker-maven-plugin>
<jacoco-maven-plugin.version>0.8.10</jacoco-maven-plugin.version>
- <onap.acm.models.version>6.4.2</onap.acm.models.version>
+ <onap.acm.models.version>6.4.3</onap.acm.models.version>
<openapi.jackson.databind.nullable.version>0.2.6</openapi.jackson.databind.nullable.version>
<spring.statemachine.version>3.2.1</spring.statemachine.version>
<spring.context.version>6.0.8</spring.context.version>
diff --git a/rapp-manager-acm/pom.xml b/rapp-manager-acm/pom.xml
index 0e01bb4..9cbf7ab 100755
--- a/rapp-manager-acm/pom.xml
+++ b/rapp-manager-acm/pom.xml
@@ -80,6 +80,11 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
@@ -110,7 +115,7 @@
ToscaNodeTemplate=org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate,
AutomationCompositions=org.onap.policy.clamp.models.acm.concepts.AutomationCompositions,
AutomationComposition=org.onap.policy.clamp.models.acm.concepts.AutomationComposition,
- <!-- AutomationCompositionDefinition=org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition,-->
+ AutomationCompositionDefinition=org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition,
SimpleResponse=org.onap.policy.clamp.models.acm.messages.rest.SimpleResponse,
AcTypeStateUpdate=org.onap.policy.clamp.models.acm.messages.rest.commissioning.AcTypeStateUpdate,
AcInstanceStateUpdate=org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate,
diff --git a/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/configuration/JacksonMessageConverterConfiguration.java b/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/configuration/JacksonMessageConverterConfiguration.java
new file mode 100755
index 0000000..970c70b
--- /dev/null
+++ b/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/configuration/JacksonMessageConverterConfiguration.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2023 Nordix Foundation. 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.
+ * 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.
+ * ============LICENSE_END========================================================================
+ */
+
+package com.oransc.rappmanager.acm.configuration;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaEntityKey;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+
+@Configuration
+public class JacksonMessageConverterConfiguration extends MappingJackson2HttpMessageConverter {
+
+ public JacksonMessageConverterConfiguration(ObjectMapper objectMapper) {
+ super(objectMapper);
+ //This is to fix the AutomationCompositionDefinition deserialization issue via Openapi generator
+ objectMapper.registerModule(
+ new SimpleModule().addKeyDeserializer(ToscaEntityKey.class, StdKeyDeserializer.forType(String.class)));
+ }
+}
diff --git a/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/service/AcmDeployer.java b/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/service/AcmDeployer.java
index 0cfeb9b..26d4418 100755
--- a/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/service/AcmDeployer.java
+++ b/rapp-manager-acm/src/main/java/com/oransc/rappmanager/acm/service/AcmDeployer.java
@@ -31,6 +31,7 @@
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
+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;
@@ -190,9 +191,12 @@
public boolean deprimeRapp(Rapp rapp) {
try {
primeACMComposition(rapp.getCompositionId(), PrimeOrder.DEPRIME);
- CommissioningResponse commissioningResponse = deleteComposition(rapp.getCompositionId());
- if (commissioningResponse != null) {
- return true;
+ if (waitForCompositionTargetState(rapp.getCompositionId(), AcTypeState.COMMISSIONED)) {
+ CommissioningResponse commissioningResponse = deleteComposition(rapp.getCompositionId());
+ if (commissioningResponse != null) {
+ rapp.setCompositionId(null);
+ return true;
+ }
}
} catch (Exception e) {
logger.warn("Failed deprime automation composition", e);
@@ -201,6 +205,31 @@
return false;
}
+ boolean waitForCompositionTargetState(UUID compositionId, AcTypeState acTypeState) {
+ boolean targetCompositionStateTransition = false;
+ try {
+ for (int i = 0; i < acmConfiguration.getMaxRetries(); i++) {
+ logger.debug("Composition state check {}", i + 1);
+ if (isCompositionStateEquals(compositionId, acTypeState)) {
+ logger.debug("Composition {} state is {}", compositionId, acTypeState);
+ targetCompositionStateTransition = true;
+ break;
+ } else {
+ TimeUnit.SECONDS.sleep(acmConfiguration.getRetryInterval());
+ }
+ }
+ } catch (Exception e) {
+ logger.warn("Unable to get composition state for composition {}", compositionId, e);
+ Thread.currentThread().interrupt();
+ }
+ return targetCompositionStateTransition;
+ }
+
+ boolean isCompositionStateEquals(UUID compositionId, AcTypeState acTypeState) {
+ return automationCompositionDefinitionApiClient.getCompositionDefinition(compositionId, UUID.randomUUID())
+ .getState().equals(acTypeState);
+ }
+
public void syncRappInstanceStatus(UUID compositionId, RappInstance rappInstance) {
if (rappInstance.getAcm().getAcmInstanceId() != null) {
try {
diff --git a/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/AcmDeployerTest.java b/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/AcmDeployerTest.java
index b866b5a..5268640 100755
--- a/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/AcmDeployerTest.java
+++ b/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/AcmDeployerTest.java
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withServerError;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -45,16 +46,23 @@
import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachineConfig;
import java.io.IOException;
import java.util.UUID;
+import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+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.DeployState;
import org.onap.policy.clamp.models.acm.concepts.LockState;
import org.onap.policy.clamp.models.acm.messages.rest.commissioning.CommissioningResponse;
import org.onap.policy.clamp.models.acm.messages.rest.commissioning.PrimeOrder;
import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
@@ -84,11 +92,12 @@
RappInstanceStateMachine rappInstanceStateMachine;
@Autowired
RappCsarConfigurationHandler rappCsarConfigurationHandler;
+ @Autowired
+ ObjectMapper objectMapper;
RappResourceBuilder rappResourceBuilder = new RappResourceBuilder();
private final String validRappFile = "valid-rapp-package.csar";
String validCsarFileLocation = "src/test/resources/";
- ObjectMapper objectMapper = new ObjectMapper();
String URI_ACM_COMPOSITIONS, URI_ACM_COMPOSITION, URI_ACM_INSTANCES, URI_ACM_INSTANCE;
@BeforeAll
@@ -258,17 +267,47 @@
}
@Test
- void testSyncRappInstanceStatus() throws JsonProcessingException {
+ void testUndeployRappInstanceACMErrorFailure() throws JsonProcessingException {
+ UUID compositionId = UUID.randomUUID();
+ UUID rappId = UUID.randomUUID();
+ UUID instanceId = UUID.randomUUID();
+ Rapp rapp = Rapp.builder().name(rappId.toString()).packageName(validRappFile).compositionId(compositionId)
+ .state(RappState.PRIMED).build();
+ expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.DEPLOYED, LockState.LOCKED,
+ ExpectedCount.once());
+ mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
+ .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED));
+ mockServer.expect(ExpectedCount.manyTimes(),
+ requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId))).andExpect(method(HttpMethod.GET))
+ .andRespond(withServerError());
+ RappInstance rappInstance = rappResourceBuilder.getRappInstance();
+ rappInstance.getAcm().setAcmInstanceId(instanceId);
+ rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
+ boolean rappUndeployStateActual = acmDeployer.undeployRappInstance(rapp, rappInstance);
+ mockServer.verify();
+ assertFalse(rappUndeployStateActual);
+ }
+
+ @ParameterizedTest
+ @MethodSource("getAcmStatusEventMap")
+ void testSyncRappInstanceStatus(DeployState deployState, LockState lockState, RappEvent rappEvent)
+ throws JsonProcessingException {
UUID compositionId = UUID.randomUUID();
UUID instanceId = UUID.randomUUID();
- expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.UNDEPLOYING, LockState.UNLOCKING,
- ExpectedCount.once());
+ expectAcmGetInstanceToReturnState(compositionId, instanceId, deployState, lockState, ExpectedCount.once());
RappInstance rappInstance = rappResourceBuilder.getRappInstance();
rappInstance.getAcm().setAcmInstanceId(instanceId);
rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
acmDeployer.syncRappInstanceStatus(compositionId, rappInstance);
mockServer.verify();
- verify(rappInstanceStateMachine, times(1)).sendRappInstanceEvent(rappInstance, RappEvent.UNDEPLOYING);
+ verify(rappInstanceStateMachine, times(1)).sendRappInstanceEvent(rappInstance, rappEvent);
+ }
+
+ private static Stream<Arguments> getAcmStatusEventMap() {
+ return Stream.of(Arguments.of(DeployState.UNDEPLOYING, LockState.UNLOCKING, RappEvent.UNDEPLOYING),
+ Arguments.of(DeployState.DEPLOYED, LockState.LOCKED, RappEvent.ACMDEPLOYED),
+ Arguments.of(DeployState.DEPLOYING, LockState.LOCKING, RappEvent.DEPLOYING),
+ Arguments.of(DeployState.UNDEPLOYED, LockState.UNLOCKED, RappEvent.ACMUNDEPLOYED));
}
@Test
@@ -357,6 +396,12 @@
commissioningResponseExpected.setCompositionId(compositionId);
mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
.andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK));
+ AutomationCompositionDefinition automationCompositionDefinition =
+ getAutomationCompositionDefinition(compositionId, AcTypeState.COMMISSIONED);
+ mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
+ .andExpect(method(HttpMethod.GET)).andRespond(
+ withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
+ .body(objectMapper.writeValueAsString(automationCompositionDefinition)));
mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
.andExpect(method(HttpMethod.DELETE)).andRespond(
withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
@@ -368,7 +413,40 @@
}
@Test
- void testDeprimeFailureRapp() {
+ void testDeprimeRappClientRetry() throws JsonProcessingException {
+ UUID compositionId = UUID.randomUUID();
+ RappResources rappResources = rappResourceBuilder.getResources();
+ Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
+ .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
+ .compositionId(compositionId).rappResources(rappResources).build();
+
+ CommissioningResponse commissioningResponseExpected = new CommissioningResponse();
+ commissioningResponseExpected.setCompositionId(compositionId);
+ mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
+ .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK));
+ AutomationCompositionDefinition automationCompositionDefinition =
+ getAutomationCompositionDefinition(compositionId, AcTypeState.DEPRIMING);
+ mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
+ .andExpect(method(HttpMethod.GET)).andRespond(
+ withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
+ .body(objectMapper.writeValueAsString(automationCompositionDefinition)));
+ automationCompositionDefinition = getAutomationCompositionDefinition(compositionId, AcTypeState.COMMISSIONED);
+ mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
+ .andExpect(method(HttpMethod.GET)).andRespond(
+ withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
+ .body(objectMapper.writeValueAsString(automationCompositionDefinition)));
+ mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
+ .andExpect(method(HttpMethod.DELETE)).andRespond(
+ withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
+ .body(objectMapper.writeValueAsString(commissioningResponseExpected)));
+
+ boolean deprimeRapp = acmDeployer.deprimeRapp(rapp);
+ mockServer.verify();
+ assertTrue(deprimeRapp);
+ }
+
+ @Test
+ void testDeprimeFailureRapp() throws JsonProcessingException {
UUID compositionId = UUID.randomUUID();
RappResources rappResources = rappResourceBuilder.getResources();
Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
@@ -377,6 +455,12 @@
mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
.andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK));
+ AutomationCompositionDefinition automationCompositionDefinition =
+ getAutomationCompositionDefinition(compositionId, AcTypeState.COMMISSIONED);
+ mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
+ .andExpect(method(HttpMethod.GET)).andRespond(
+ withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
+ .body(objectMapper.writeValueAsString(automationCompositionDefinition)));
mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
.andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
@@ -386,6 +470,45 @@
}
@Test
+ void testDeprimeACMStatusFailureRapp() throws JsonProcessingException {
+ UUID compositionId = UUID.randomUUID();
+ RappResources rappResources = rappResourceBuilder.getResources();
+ Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
+ .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
+ .compositionId(compositionId).rappResources(rappResources).build();
+
+ mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
+ .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK));
+ AutomationCompositionDefinition automationCompositionDefinition =
+ getAutomationCompositionDefinition(compositionId, AcTypeState.DEPRIMING);
+ mockServer.expect(ExpectedCount.manyTimes(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
+ .andExpect(method(HttpMethod.GET)).andRespond(
+ withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
+ .body(objectMapper.writeValueAsString(automationCompositionDefinition)));
+
+ boolean deprimeRapp = acmDeployer.deprimeRapp(rapp);
+ mockServer.verify();
+ assertFalse(deprimeRapp);
+ }
+
+ @Test
+ void testDeprimeACMStatusErrorRapp() {
+ UUID compositionId = UUID.randomUUID();
+ RappResources rappResources = rappResourceBuilder.getResources();
+ Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
+ .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
+ .compositionId(compositionId).rappResources(rappResources).build();
+
+ mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
+ .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK));
+ mockServer.expect(ExpectedCount.manyTimes(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
+ .andExpect(method(HttpMethod.GET)).andRespond(withServerError());
+ boolean deprimeRapp = acmDeployer.deprimeRapp(rapp);
+ mockServer.verify();
+ assertFalse(deprimeRapp);
+ }
+
+ @Test
void testDeprimeExceptionRapp() {
Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
.packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build();
@@ -432,4 +555,12 @@
withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
.body(objectMapper.writeValueAsString(automationCompositionDeployed)));
}
+
+ AutomationCompositionDefinition getAutomationCompositionDefinition(UUID compositionId, AcTypeState acTypeState) {
+ AutomationCompositionDefinition automationCompositionDefinition = new AutomationCompositionDefinition();
+ automationCompositionDefinition.setCompositionId(compositionId);
+ automationCompositionDefinition.setState(acTypeState);
+ automationCompositionDefinition.setServiceTemplate(new ToscaServiceTemplate());
+ return automationCompositionDefinition;
+ }
}
diff --git a/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/BeanTestConfiguration.java b/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/BeanTestConfiguration.java
index f59f8fb..a2c2f67 100755
--- a/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/BeanTestConfiguration.java
+++ b/rapp-manager-acm/src/test/java/com/oransc/rappmanager/acm/service/BeanTestConfiguration.java
@@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.oransc.rappmanager.acm.ApiClient;
import com.oransc.rappmanager.acm.configuration.ACMConfiguration;
+import com.oransc.rappmanager.acm.configuration.JacksonMessageConverterConfiguration;
import com.oransc.rappmanager.acm.rest.AutomationCompositionDefinitionApiClient;
import com.oransc.rappmanager.acm.rest.AutomationCompositionInstanceApiClient;
import com.oransc.rappmanager.acm.rest.ParticipantMonitoringApiClient;
@@ -40,8 +41,10 @@
}
@Bean
- public RestTemplate restTemplate(RestTemplateBuilder builder) {
- return builder.build();
+ public RestTemplate restTemplate(RestTemplateBuilder builder, ObjectMapper objectMapper) {
+ RestTemplate restTemplate = builder.build();
+ restTemplate.getMessageConverters().add(new JacksonMessageConverterConfiguration(objectMapper));
+ return restTemplate;
}
@Bean("acmApiClient")