From: Aravindhan Ayyanathan Date: Tue, 20 Feb 2024 11:09:38 +0000 (+0000) Subject: Merge "Add support to upload deployment helm artifacts to chartmuseum" X-Git-Tag: 0.1.0~29 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=acb1b85ddb80af9c7243abb949ac49c241ef8f33;hp=5fbc2d141c3166ff1948eca309aafda9a6f66a24;p=nonrtric%2Fplt%2Frappmanager.git Merge "Add support to upload deployment helm artifacts to chartmuseum" --- diff --git a/rapp-manager-application/src/main/java/com/oransc/rappmanager/service/DeploymentArtifactsService.java b/rapp-manager-application/src/main/java/com/oransc/rappmanager/service/DeploymentArtifactsService.java new file mode 100755 index 0000000..aaafdb2 --- /dev/null +++ b/rapp-manager-application/src/main/java/com/oransc/rappmanager/service/DeploymentArtifactsService.java @@ -0,0 +1,78 @@ +/* + * ============LICENSE_START====================================================================== + * Copyright (C) 2024 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. + * 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.service; + +import com.oransc.rappmanager.models.csar.DeploymentItem; +import com.oransc.rappmanager.models.csar.DeploymentItemArtifactType; +import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler; +import com.oransc.rappmanager.models.exception.RappHandlerException; +import com.oransc.rappmanager.models.rapp.Rapp; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; + +@Service +@RequiredArgsConstructor +public class DeploymentArtifactsService { + + private final RestTemplate restTemplate; + private final RappCsarConfigurationHandler rappCsarConfigurationHandler; + + public boolean configureDeploymentArtifacts(Rapp rapp) { + List deploymentItems = rappCsarConfigurationHandler.getDeploymentItems(rapp); + return deploymentItems.stream().filter(deploymentItem -> deploymentItem.getArtifactType() + .equals(DeploymentItemArtifactType.HELMCHART)) + .allMatch(deploymentItem -> uploadHelmChart(rapp, deploymentItem)); + } + + boolean uploadHelmChart(Rapp rApp, DeploymentItem deploymentItem) throws RappHandlerException { + try { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); + HttpEntity requestHttpEntity = + new HttpEntity<>(rappCsarConfigurationHandler.getArtifactPayload(rApp, deploymentItem.getFile()), + httpHeaders); + ResponseEntity responseEntity = + restTemplate.exchange(deploymentItem.getTargetServerUri(), HttpMethod.POST, requestHttpEntity, + String.class); + if (responseEntity.getStatusCode().is2xxSuccessful()) { + return true; + } + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode().equals(HttpStatus.CONFLICT)) { + return true; + } + } catch (Exception e) { + throw new RappHandlerException(HttpStatus.BAD_REQUEST, + String.format("Unable to connect to the chartmuseum server %s to upload helm artifact %s", + deploymentItem.getTargetServerUri(), deploymentItem.getFile())); + } + return false; + } +} diff --git a/rapp-manager-application/src/main/java/com/oransc/rappmanager/service/RappService.java b/rapp-manager-application/src/main/java/com/oransc/rappmanager/service/RappService.java index 8e71d50..23ab2d8 100755 --- a/rapp-manager-application/src/main/java/com/oransc/rappmanager/service/RappService.java +++ b/rapp-manager-application/src/main/java/com/oransc/rappmanager/service/RappService.java @@ -44,13 +44,19 @@ public class RappService { private final List rappDeployers; private final RappInstanceStateMachine rappInstanceStateMachine; private final RappCacheService rappCacheService; + private final DeploymentArtifactsService deploymentArtifactsService; private static final String STATE_TRANSITION_NOT_PERMITTED = "State transition from %s to %s is not permitted."; public ResponseEntity primeRapp(Rapp rapp) { if (rapp.getState().equals(RappState.COMMISSIONED)) { rapp.setState(RappState.PRIMING); rapp.setReason(null); - if (rappDeployers.parallelStream().allMatch(rappDeployer -> rappDeployer.primeRapp(rapp))) { + //Configuring the deployment artifact needs to be done before starting the priming with other components + //If there are additional conditions needs to be checked before priming, This needs handled as part of pre-priming stage. + if (deploymentArtifactsService.configureDeploymentArtifacts(rapp) && rappDeployers.parallelStream() + .allMatch( + rappDeployer -> rappDeployer.primeRapp( + rapp))) { rapp.setState(RappState.PRIMED); return ResponseEntity.ok().build(); } diff --git a/rapp-manager-application/src/test/java/com/oransc/rappmanager/service/DeploymentArtifactsServiceTest.java b/rapp-manager-application/src/test/java/com/oransc/rappmanager/service/DeploymentArtifactsServiceTest.java new file mode 100755 index 0000000..4a3a08a --- /dev/null +++ b/rapp-manager-application/src/test/java/com/oransc/rappmanager/service/DeploymentArtifactsServiceTest.java @@ -0,0 +1,151 @@ +/* + * ============LICENSE_START====================================================================== + * Copyright (C) 2024 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. + * 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.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +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.withException; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withServerError; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withTooManyRequests; + +import com.oransc.rappmanager.models.csar.DeploymentItem; +import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler; +import com.oransc.rappmanager.models.exception.RappHandlerException; +import com.oransc.rappmanager.models.rapp.Rapp; +import com.oransc.rappmanager.models.rapp.RappState; +import com.oransc.rappmanager.sme.service.SmeLifecycleManager; +import java.io.IOException; +import java.util.List; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.test.web.client.ExpectedCount; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.web.client.RestTemplate; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class DeploymentArtifactsServiceTest { + + @SpyBean + DeploymentArtifactsService deploymentArtifactsService; + @Autowired + RestTemplate restTemplate; + @Autowired + RappCsarConfigurationHandler rappCsarConfigurationHandler; + @MockBean + SmeLifecycleManager smeLifecycleManager; + + MockRestServiceServer mockServer; + + String validCsarFileLocation = "src/test/resources/"; + + private final String validRappFile = "valid-rapp-package.csar"; + + @BeforeEach + public void init() { + mockServer = MockRestServiceServer.createServer(restTemplate); + } + + @ParameterizedTest + @EnumSource(value = HttpStatus.class, names = {"CREATED", "CONFLICT"}) + void testChartUpload(HttpStatus status) { + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); + List deploymentItems = rappCsarConfigurationHandler.getDeploymentItems(rapp); + deploymentItems.forEach(deploymentItem -> mockServer.expect(ExpectedCount.once(), + requestTo(deploymentItem.getTargetServerUri())).andExpect(method(HttpMethod.POST)) + .andRespond(withStatus(status))); + assertTrue(deploymentArtifactsService.configureDeploymentArtifacts(rapp)); + mockServer.verify(); + } + + @Test + void testChartUploadNoArtifacts() { + String invalidRappFile = "invalid-rapp-package.csar"; + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(invalidRappFile) + .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); + assertTrue(deploymentArtifactsService.configureDeploymentArtifacts(rapp)); + } + + @Test + void testChartUploadFailure() { + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); + List deploymentItems = rappCsarConfigurationHandler.getDeploymentItems(rapp); + deploymentItems.stream().findFirst().ifPresent(deploymentItem -> mockServer.expect(ExpectedCount.once(), + requestTo(deploymentItem.getTargetServerUri())).andExpect(method(HttpMethod.POST)) + .andRespond(withServerError())); + RappHandlerException exception = assertThrows(RappHandlerException.class, + () -> deploymentArtifactsService.configureDeploymentArtifacts(rapp)); + assertEquals(HttpStatus.BAD_REQUEST, exception.getStatusCode()); + mockServer.verify(); + } + + @Test + void testChartUploadFailureWithNotFound() { + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); + List deploymentItems = rappCsarConfigurationHandler.getDeploymentItems(rapp); + deploymentItems.stream().findFirst().ifPresent(deploymentItem -> mockServer.expect(ExpectedCount.once(), + requestTo(deploymentItem.getTargetServerUri())).andExpect(method(HttpMethod.POST)).andRespond( + withStatus(HttpStatus.NOT_FOUND))); + assertFalse(deploymentArtifactsService.configureDeploymentArtifacts(rapp)); + mockServer.verify(); + } + + @Test + void testChartUploadFailureWithException() { + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); + List deploymentItems = rappCsarConfigurationHandler.getDeploymentItems(rapp); + deploymentItems.stream().findFirst().ifPresent(deploymentItem -> mockServer.expect(ExpectedCount.once(), + requestTo(deploymentItem.getTargetServerUri())).andExpect(method(HttpMethod.POST)).andRespond( + withException(new IOException()))); + RappHandlerException exception = assertThrows(RappHandlerException.class, + () -> deploymentArtifactsService.configureDeploymentArtifacts(rapp)); + assertEquals(HttpStatus.BAD_REQUEST, exception.getStatusCode()); + mockServer.verify(); + } + + @Test + void testChartUploadFailureWithTooManyRequests() { + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); + List deploymentItems = rappCsarConfigurationHandler.getDeploymentItems(rapp); + deploymentItems.stream().findFirst().ifPresent(deploymentItem -> mockServer.expect(ExpectedCount.once(), + requestTo(deploymentItem.getTargetServerUri())).andExpect(method(HttpMethod.POST)) + .andRespond(withTooManyRequests())); + assertFalse(deploymentArtifactsService.configureDeploymentArtifacts(rapp)); + mockServer.verify(); + } +} diff --git a/rapp-manager-application/src/test/java/com/oransc/rappmanager/service/RappServiceTest.java b/rapp-manager-application/src/test/java/com/oransc/rappmanager/service/RappServiceTest.java index 4154b6c..9dc4987 100755 --- a/rapp-manager-application/src/test/java/com/oransc/rappmanager/service/RappServiceTest.java +++ b/rapp-manager-application/src/test/java/com/oransc/rappmanager/service/RappServiceTest.java @@ -60,6 +60,9 @@ class RappServiceTest { @MockBean DmeDeployer dmeDeployer; + @MockBean + DeploymentArtifactsService deploymentArtifactsService; + @MockBean SmeLifecycleManager smeLifecycleManager; @@ -80,6 +83,7 @@ class RappServiceTest { when(acmDeployer.primeRapp(any())).thenReturn(true); when(dmeDeployer.primeRapp(any())).thenReturn(true); when(smeDeployer.primeRapp(any())).thenReturn(true); + when(deploymentArtifactsService.configureDeploymentArtifacts(any())).thenReturn(true); assertEquals(HttpStatus.OK, rappService.primeRapp(rapp).getStatusCode()); assertEquals(RappState.PRIMED, rapp.getState()); } @@ -100,6 +104,7 @@ class RappServiceTest { void testPrimeRappAcmFailure() { Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); + when(deploymentArtifactsService.configureDeploymentArtifacts(any())).thenReturn(true); when(acmDeployer.primeRapp(any())).thenReturn(false); when(dmeDeployer.primeRapp(any())).thenReturn(true); RappHandlerException rappHandlerException = @@ -112,6 +117,7 @@ class RappServiceTest { void testPrimeRappDmeFailure() { Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); + when(deploymentArtifactsService.configureDeploymentArtifacts(any())).thenReturn(true); when(acmDeployer.primeRapp(any())).thenReturn(true); when(dmeDeployer.primeRapp(any())).thenReturn(false); RappHandlerException rappHandlerException = @@ -120,6 +126,16 @@ class RappServiceTest { assertEquals(RappState.COMMISSIONED, rapp.getState()); } + @Test + void testPrimeRappDeployArtifactFailure() { + Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile) + .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build(); + when(deploymentArtifactsService.configureDeploymentArtifacts(any())).thenReturn(false); + RappHandlerException rappHandlerException = + assertThrows(RappHandlerException.class, () -> rappService.primeRapp(rapp)); + assertEquals(HttpStatus.BAD_GATEWAY, rappHandlerException.getStatusCode()); + assertEquals(RappState.COMMISSIONED, rapp.getState()); + } @Test void testDeprimeRapp() { diff --git a/rapp-manager-application/src/test/resources/invalid-rapp-package.csar b/rapp-manager-application/src/test/resources/invalid-rapp-package.csar index dc18b5b..97efcb5 100755 Binary files a/rapp-manager-application/src/test/resources/invalid-rapp-package.csar and b/rapp-manager-application/src/test/resources/invalid-rapp-package.csar differ diff --git a/rapp-manager-application/src/test/resources/valid-rapp-package.csar b/rapp-manager-application/src/test/resources/valid-rapp-package.csar index ffe2f4b..667317e 100755 Binary files a/rapp-manager-application/src/test/resources/valid-rapp-package.csar and b/rapp-manager-application/src/test/resources/valid-rapp-package.csar differ diff --git a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/DeploymentItem.java b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/DeploymentItem.java new file mode 100755 index 0000000..6e4a121 --- /dev/null +++ b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/DeploymentItem.java @@ -0,0 +1,40 @@ +/* + * ============LICENSE_START====================================================================== + * Copyright (C) 2024 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. + * 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.models.csar; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class DeploymentItem { + + String file; + DeploymentItemArtifactType artifactType; + String itemId; + DeploymentItemTargetServer targetServer; + String targetServerUri; +} diff --git a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/DeploymentItemArtifactType.java b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/DeploymentItemArtifactType.java new file mode 100755 index 0000000..46f007d --- /dev/null +++ b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/DeploymentItemArtifactType.java @@ -0,0 +1,26 @@ +/* + * ============LICENSE_START====================================================================== + * Copyright (C) 2024 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. + * 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.models.csar; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public enum DeploymentItemArtifactType { + @JsonProperty("helm_chart") HELMCHART; +} diff --git a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/DeploymentItemTargetServer.java b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/DeploymentItemTargetServer.java new file mode 100755 index 0000000..2bc91e9 --- /dev/null +++ b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/DeploymentItemTargetServer.java @@ -0,0 +1,27 @@ +/* + * ============LICENSE_START====================================================================== + * Copyright (C) 2024 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. + * 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.models.csar; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public enum DeploymentItemTargetServer { + + @JsonProperty("chartmuseum") CHARTMUSEUM; +} diff --git a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandler.java b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandler.java index 1def6e5..9e2f953 100755 --- a/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandler.java +++ b/rapp-manager-models/src/main/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandler.java @@ -22,6 +22,7 @@ package com.oransc.rappmanager.models.csar; import static com.google.common.base.Splitter.on; import static com.google.common.collect.Iterables.filter; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; @@ -35,6 +36,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; @@ -47,6 +49,7 @@ import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.io.ByteArrayResource; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -71,6 +74,8 @@ public class RappCsarConfigurationHandler { private static final String DME_CONSUMER_INFO_TYPES_LOCATION = "Files/Dme/consumerinfotypes"; private static final String DME_INFO_PRODUCERS_LOCATION = "Files/Dme/infoproducers"; private static final String DME_INFO_CONSUMERS_LOCATION = "Files/Dme/infoconsumers"; + private static final String ARTIFACTS_LOCATION_JSON_POINTER = + "/topology_template/node_templates/applicationServiceDescriptor/artifacts"; public boolean isValidRappPackage(MultipartFile multipartFile) { @@ -107,10 +112,18 @@ public class RappCsarConfigurationHandler { "COMPOSITIONID", String.valueOf(compositionId)); } + public ByteArrayResource getArtifactPayload(Rapp rapp, String location) { + return new ByteArrayResource(getByteArrayStreamPayload(rapp, location).toByteArray()); + } + String getPayload(Rapp rapp, String location) { + return getByteArrayStreamPayload(rapp, location).toString(); + } + + ByteArrayOutputStream getByteArrayStreamPayload(Rapp rapp, String location) { logger.debug("Getting payload for {} from {}", rapp.getRappId(), location); File csarFile = getCsarFile(rapp); - return getFileFromCsar(csarFile, location).toString(); + return getFileFromCsar(csarFile, location); } File getCsarFile(Rapp rapp) { @@ -163,11 +176,8 @@ public class RappCsarConfigurationHandler { if (asdLocation != null && !asdLocation.isEmpty() && isFileExistsInCsar(multipartFile, asdLocation)) { try { String asdContent = getFileFromCsar(multipartFile, asdLocation).toString(); - String asdJsonContent = new Gson().toJsonTree(new Yaml().load(asdContent)).toString(); - JsonNode jsonNode = objectMapper.readTree(asdJsonContent); - List artifactFileList = - jsonNode.at("/topology_template/node_templates/applicationServiceDescriptor/artifacts") - .findValuesAsText("file"); + JsonNode jsonNode = getAsdContentNode(asdContent); + List artifactFileList = jsonNode.at(ARTIFACTS_LOCATION_JSON_POINTER).findValuesAsText("file"); return artifactFileList.stream() .allMatch(artifactFile -> isFileExistsInCsar(multipartFile, artifactFile)); } catch (RappHandlerException e) { @@ -180,21 +190,51 @@ public class RappCsarConfigurationHandler { throw new RappHandlerException(HttpStatus.BAD_REQUEST, "ASD definition in rApp package is invalid."); } + JsonNode getAsdContentNode(String asdContent) throws JsonProcessingException { + return objectMapper.readTree(new Gson().toJsonTree(new Yaml().load(asdContent)).toString()); + } + + String getAsdDefinitionLocation(final File csarFile) { + return getAsdDefinitionLocation(getFileFromCsar(csarFile, TOSCA_METADATA_LOCATION).toString()); + } + String getAsdDefinitionLocation(final MultipartFile multipartFile) { + return getAsdDefinitionLocation(getFileFromCsar(multipartFile, TOSCA_METADATA_LOCATION).toString()); + } + + String getAsdDefinitionLocation(final String toscaMetadata) { String asdLocation = ""; - final ByteArrayOutputStream fileContent = getFileFromCsar(multipartFile, TOSCA_METADATA_LOCATION); - if (fileContent != null) { - final String toscaMetadata = fileContent.toString(); - if (!toscaMetadata.isEmpty()) { - final String entry = - filter(on("\n").split(toscaMetadata), line -> line.contains(ENTRY_DEFINITIONS_INDEX)).iterator() - .next(); - asdLocation = entry.replace(ENTRY_DEFINITIONS_INDEX + ":", "").trim(); - } + if (toscaMetadata != null && !toscaMetadata.isEmpty()) { + final String entry = + filter(on("\n").split(toscaMetadata), line -> line.contains(ENTRY_DEFINITIONS_INDEX)).iterator() + .next(); + asdLocation = entry.replace(ENTRY_DEFINITIONS_INDEX + ":", "").trim(); } return asdLocation; } + public List getDeploymentItems(Rapp rApp) { + List deploymentItems = new ArrayList<>(); + File csarFile = getCsarFile(rApp); + String asdDefinitionLocation = getAsdDefinitionLocation(csarFile); + if (asdDefinitionLocation != null && !asdDefinitionLocation.isEmpty()) { + try { + String asdContent = getFileFromCsar(csarFile, asdDefinitionLocation).toString(); + JsonNode jsonNode = getAsdContentNode(asdContent); + JsonNode artifactsJsonNode = jsonNode.at(ARTIFACTS_LOCATION_JSON_POINTER); + artifactsJsonNode.forEach(artifactJsonNode -> { + DeploymentItem deploymentItem = + objectMapper.convertValue(artifactJsonNode.at("/properties"), DeploymentItem.class); + deploymentItem.setFile(artifactJsonNode.at("/file").asText()); + deploymentItems.add(deploymentItem); + }); + } catch (Exception e) { + logger.warn("Unable to get the deployment items", e); + } + } + return deploymentItems; + } + public String getSmeProviderDomainPayload(Rapp rapp, RappSMEInstance rappSMEInstance) { return getPayload(rapp, getResourceUri(SME_PROVIDER_FUNCS_LOCATION, rappSMEInstance.getProviderFunction())); diff --git a/rapp-manager-models/src/test/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandlerTest.java b/rapp-manager-models/src/test/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandlerTest.java index 81468ee..e769b12 100755 --- a/rapp-manager-models/src/test/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandlerTest.java +++ b/rapp-manager-models/src/test/java/com/oransc/rappmanager/models/csar/RappCsarConfigurationHandlerTest.java @@ -27,10 +27,13 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.JsonSyntaxException; import com.oransc.rappmanager.models.exception.RappHandlerException; import com.oransc.rappmanager.models.rapp.Rapp; import com.oransc.rappmanager.models.rapp.RappResources; @@ -41,6 +44,7 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.util.List; import java.util.Set; import java.util.UUID; import java.util.stream.Stream; @@ -51,6 +55,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.http.HttpStatus; @@ -67,11 +72,14 @@ class RappCsarConfigurationHandlerTest { String validCsarFileLocation = "src/test/resources/"; - private final String validRappFile = "valid-rapp-package.csar"; private final String invalidRappFile = "invalid-rapp-package.csar"; + private final String invalidRappNoAsdFile = "invalid-rapp-package-no-asd-yaml.csar"; + + private final String invalidRappEmptyAsdFile = "invalid-rapp-package-empty-asd-yaml.csar"; + @Test void testCsarPackageValidationSuccess() throws IOException { String rappCsarPath = validCsarFileLocation + File.separator + validRappFile; @@ -84,7 +92,6 @@ class RappCsarConfigurationHandlerTest { @ParameterizedTest @MethodSource("getInvalidCsarPackage") void testCsarPackageValidationFailure(MultipartFile multipartFile) { - System.out.println(multipartFile.getOriginalFilename()); RappHandlerException exception = assertThrows(RappHandlerException.class, () -> rappCsarConfigurationHandler.isValidRappPackage(multipartFile)); assertEquals(HttpStatus.BAD_REQUEST, exception.getStatusCode()); @@ -361,4 +368,32 @@ class RappCsarConfigurationHandlerTest { assertNotNull(dmeInfoConsumerPayload); } + @Test + void testListDeploymentItems() { + Rapp rapp = Rapp.builder().name("").packageName(validRappFile).packageLocation(validCsarFileLocation).build(); + List deploymentItems = rappCsarConfigurationHandler.getDeploymentItems(rapp); + assertEquals(2, deploymentItems.size()); + } + + @ParameterizedTest + @ValueSource(strings = {invalidRappNoAsdFile, invalidRappEmptyAsdFile}) + void testListDeploymentItemsNoAsd(String packageName) { + Rapp rapp = Rapp.builder().name("").packageName(packageName).packageLocation(validCsarFileLocation).build(); + assertThat(rappCsarConfigurationHandler.getDeploymentItems(rapp)).isEmpty(); + } + + @Test + void testListDeploymentItemsWithException() throws JsonProcessingException { + Rapp rapp = Rapp.builder().name("").packageName(validRappFile).packageLocation(validCsarFileLocation).build(); + doThrow(new JsonSyntaxException("")).when(rappCsarConfigurationHandler).getAsdContentNode(any()); + assertThat(rappCsarConfigurationHandler.getDeploymentItems(rapp)).isEmpty(); + } + + @Test + void testGetArtifactPayload() { + Rapp rapp = Rapp.builder().name("").packageName(validRappFile).packageLocation(validCsarFileLocation).build(); + assertNotNull(rappCsarConfigurationHandler.getArtifactPayload(rapp, + "Artifacts/Deployment/HELM/ransliceassurance-1.0.0.tgz")); + + } } diff --git a/rapp-manager-models/src/test/resources/invalid-rapp-package-empty-asd-yaml.csar b/rapp-manager-models/src/test/resources/invalid-rapp-package-empty-asd-yaml.csar new file mode 100755 index 0000000..e614a19 Binary files /dev/null and b/rapp-manager-models/src/test/resources/invalid-rapp-package-empty-asd-yaml.csar differ diff --git a/rapp-manager-models/src/test/resources/valid-rapp-package.csar b/rapp-manager-models/src/test/resources/valid-rapp-package.csar index ffe2f4b..667317e 100755 Binary files a/rapp-manager-models/src/test/resources/valid-rapp-package.csar and b/rapp-manager-models/src/test/resources/valid-rapp-package.csar differ diff --git a/sample-rapp-generator/rapp-all/Definitions/asd.yaml b/sample-rapp-generator/rapp-all/Definitions/asd.yaml index e369010..96543b8 100755 --- a/sample-rapp-generator/rapp-all/Definitions/asd.yaml +++ b/sample-rapp-generator/rapp-all/Definitions/asd.yaml @@ -7,7 +7,7 @@ imports: topology_template: node_templates: - applicationServiceDescriptor: + applicationServiceDescriptor: type: tosca.nodes.asd description: "rapp-all" properties: @@ -25,10 +25,14 @@ topology_template: file: "Artifacts/Deployment/HELM/ransliceassurance-1.0.0.tgz" properties: artifact_type: "helm_chart" - itemId: 1 + target_server: "chartmuseum" + target_server_uri: "http://10.101.0.106:8879/charts/api/charts" + item_id: 1 oru-app: type: tosca.artifacts.asd.deploymentItem file: "Artifacts/Deployment/HELM/orufhrecovery-1.0.0.tgz" properties: artifact_type: "helm_chart" - itemId: 2 + target_server: "chartmuseum" + target_server_uri: "http://10.101.0.106:8879/charts/api/charts" + item_id: 2 \ No newline at end of file diff --git a/sample-rapp-generator/rapp-all/Definitions/asd_types.yaml b/sample-rapp-generator/rapp-all/Definitions/asd_types.yaml index e2a788a..7ac8c10 100755 --- a/sample-rapp-generator/rapp-all/Definitions/asd_types.yaml +++ b/sample-rapp-generator/rapp-all/Definitions/asd_types.yaml @@ -43,19 +43,30 @@ node_types: artifact_types: tosca.artifacts.asd.deploymentItem: - version: 0.1 - derived_from: tosca.artifacts.Root - description: "Describes the artifact type of asd deployment item" - file: "Relative path of the artifact in the package" - properties: - item_id: - description: "The identifier of this asd deployment item" - required: true - type: string - artifact_type: - description: > - Specify artifact type. - required: true - type: string - constraints: - - valid_values: ["helm_chart"] + version: 0.1 + derived_from: tosca.artifacts.Root + description: "Describes the artifact type of asd deployment item" + file: "Relative path of the artifact in the package" + properties: + item_id: + description: "The identifier of this asd deployment item" + required: true + type: string + artifact_type: + description: > + Specify artifact type. + required: true + type: string + constraints: + - valid_values: ["helm_chart"] + target_server: + description: > + Specify target server for artifact. + required: true + type: string + constraints: + - valid_values: [ "chartmuseum" ] + target_server_uri: + description: "URI of the target server" + required: true + type: string \ No newline at end of file