From: aravind.est Date: Wed, 14 Feb 2024 14:26:41 +0000 (+0000) Subject: Add support to upload deployment helm artifacts to chartmuseum X-Git-Tag: 0.1.0~29^2 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;ds=sidebyside;h=a46c269e0972357c54378520a4bba531bf7bb12d;hp=4d1a6cb9ec0dc66cda0cdd2249c5117542f839c7;p=nonrtric%2Fplt%2Frappmanager.git Add support to upload deployment helm artifacts to chartmuseum HELM artifacts can be defined as part of the rApp package. These artifacts will be uploaded to the chartmuseum server as part of the rApp priming process. Issue-ID: NONRTRIC-975 Change-Id: Ia65e7756cc76763d25362ac1f5261785e7a65d18 Signed-off-by: aravind.est --- 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