From 414949bd6d65a46f296b3c7a0057a6a58a54db22 Mon Sep 17 00:00:00 2001 From: kuldipr Date: Fri, 18 Mar 2022 10:35:56 -0400 Subject: Enable versioned resource resolution by using occurrence By setting occurrence to <= 0 in the CBA, a user indicates that each time resource resolution executes, it should produce a new set of values for a resolution-key or (resourceId, resourceType) pair. For each new execution, the occurrence is incremented to serve as the version number for the new set of values. Issue-ID: CCSDK-3663 Signed-off-by: kuldipr Change-Id: Ib535b20cb775dcbb5b02fe5a5f6904a335fda310 --- .../resolution/ResourceResolutionComponent.kt | 12 +- .../resolution/ResourceResolutionService.kt | 62 ++++++- .../resolution/db/ResourceResolutionDBService.kt | 48 ++++++ .../resolution/db/ResourceResolutionRepository.kt | 22 +++ .../resolution/ResourceResolutionServiceTest.kt | 189 +++++++++++++++++++++ .../sample-resourceresolution-request2.json | 31 ++++ 6 files changed, 356 insertions(+), 8 deletions(-) create mode 100644 ms/blueprintsprocessor/functions/resource-resolution/src/test/resources/payload/requests/sample-resourceresolution-request2.json (limited to 'ms/blueprintsprocessor/functions/resource-resolution') diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionComponent.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionComponent.kt index e060cdcc5..d46f75e41 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionComponent.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionComponent.kt @@ -109,9 +109,13 @@ open class ResourceResolutionComponent(private val resourceResolutionService: Re val artifactPrefixNamesNode = getOperationInput(ResourceResolutionConstants.INPUT_ARTIFACT_PREFIX_NAMES) val artifactPrefixNames = JacksonUtils.getListFromJsonNode(artifactPrefixNamesNode, String::class.java) + val alwaysPerformNewResolution = occurrence <= 0 + val resolutionsToPerform: Int = if (alwaysPerformNewResolution) 1 else occurrence + for (j in 1..resolutionsToPerform) { - for (j in 1..occurrence) { - properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = j + if (!alwaysPerformNewResolution) { + properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = j + } val result = resourceResolutionService.resolveResources( bluePrintRuntimeService, @@ -121,8 +125,8 @@ open class ResourceResolutionComponent(private val resourceResolutionService: Re stepName ) - // provide indexed result in output if we have multiple resolution - if (occurrence != 1) { + // provide indexed result in output if we have multiple resolution. + if (resolutionsToPerform != 1) { jsonResponse.set(j.toString(), result.templateMap.asJsonNode()) assignmentMap.set(j.toString(), result.assignmentMap.asJsonNode()) } else { diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionService.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionService.kt index 8923a1143..a3c137807 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionService.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionService.kt @@ -201,6 +201,7 @@ open class ResourceResolutionServiceImpl( val artifactMapping = "$artifactPrefix-mapping" val forceResolution = isForceResolution(properties) + val propertiesMutableMap = properties.toMutableMap() log.info("Resolving resource with resource assignment artifact($artifactMapping)") val resourceAssignmentContent = @@ -212,7 +213,16 @@ open class ResourceResolutionServiceImpl( ?: throw BluePrintProcessorException("couldn't get Dictionary Definitions") if (isToStore(properties)) { - val existingResourceResolution = isNewResolution(bluePrintRuntimeService, properties, artifactPrefix) + val alwaysPerformNewResolution = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] as Int <= 0 + val existingResourceResolution = if (alwaysPerformNewResolution) { + val occurrence = findNextOccurrence(bluePrintRuntimeService, properties, artifactPrefix) + log.info("Always perform new resolutions - next occurrence: $occurrence") + propertiesMutableMap[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = occurrence + // Since we are performing new resolution, simply pass empty list. + emptyList() + } else { + isNewResolution(bluePrintRuntimeService, properties, artifactPrefix) + } if (existingResourceResolution.isNotEmpty()) { if (forceResolution) { resourceResolutionDBService.deleteResourceResolutionList(existingResourceResolution) @@ -237,7 +247,7 @@ open class ResourceResolutionServiceImpl( resourceDefinitions, resourceAssignments, artifactPrefix, - properties + propertiesMutableMap ) val resolutionSummary = properties.getOrDefault( @@ -271,7 +281,7 @@ open class ResourceResolutionServiceImpl( } if (isToStore(properties)) { - templateResolutionDBService.write(properties, resolvedContent, bluePrintRuntimeService, artifactPrefix) + templateResolutionDBService.write(propertiesMutableMap, resolvedContent, bluePrintRuntimeService, artifactPrefix) log.info("Template resolution saved into database successfully : ($properties)") } @@ -490,7 +500,7 @@ open class ResourceResolutionServiceImpl( } } - // Comparision between what we have in the database vs what we have to assign. + // Comparison between what we have in the database vs what we have to assign. private fun compareOne(resourceResolution: ResourceResolution, resourceAssignment: ResourceAssignment): Boolean { return ( resourceResolution.name == resourceAssignment.name && @@ -509,4 +519,48 @@ open class ResourceResolutionServiceImpl( properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE].asJsonPrimitive() ) } + + /** + * This method returns 'occurrence' required to persist new resource resolution. + * + * @param bluePrintRuntimeService + * @param properties + * @param artifactPrefix + */ + private suspend fun findNextOccurrence( + bluePrintRuntimeService: BluePrintRuntimeService<*>, + properties: Map, + artifactPrefix: String + ): Int { + val metadata = bluePrintRuntimeService.bluePrintContext().metadata!! + val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!! + val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!! + val resolutionKey = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] as String + val resourceId = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] as String + val resourceType = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] as String + + // This should not happen since the request has already been validated but worth to check it here as well. + if (resourceType.isEmpty() && resourceId.isEmpty() && resolutionKey.isEmpty()) { + throw BluePrintProcessorException( + "Can't proceed to get next occurrence: " + + "Either provide a resolution-key OR combination of resource-id and resource-type" + ) + } + + if (resolutionKey.isNotEmpty()) { + return resourceResolutionDBService.findNextOccurrenceByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName( + resolutionKey, + blueprintName, + blueprintVersion, + artifactPrefix + ) + } else { + return resourceResolutionDBService.findNextOccurrenceByBlueprintNameAndBlueprintVersionAndResourceIdAndResourceType( + blueprintName, + blueprintVersion, + resourceId, + resourceType + ) + } + } } diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBService.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBService.kt index b5d4e4515..1f0171f9f 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBService.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBService.kt @@ -229,4 +229,52 @@ class ResourceResolutionDBService(private val resourceResolutionRepository: Reso throw BluePrintException("Failed to batch delete resource resolution", ex) } } + + /** + * This method returns the (highest occurrence + 1) of resource resolutions if present in DB, returns 1 otherwise. + * The 'occurrence' is used to persist new resource resolution in the DB. + * + * @param resolutionKey + * @param blueprintName + * @param blueprintVersion + * @param artifactPrefix + */ + suspend fun findNextOccurrenceByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName( + resolutionKey: String, + blueprintName: String, + blueprintVersion: String, + artifactPrefix: String + ) = withContext(Dispatchers.IO) { + val maxOccurrence = resourceResolutionRepository.findMaxOccurrenceByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName( + resolutionKey, + blueprintName, + blueprintVersion, + artifactPrefix + ) + maxOccurrence?.inc() ?: 1 + } + + /** + * This method returns the (highest occurrence + 1) of resource resolutions if present in DB, returns 1 otherwise. + * The 'occurrence' is used to persist new resource resolution in the DB. + * + * @param blueprintName + * @param blueprintVersion + * @param resourceId + * @param resourceType + */ + suspend fun findNextOccurrenceByBlueprintNameAndBlueprintVersionAndResourceIdAndResourceType( + blueprintName: String, + blueprintVersion: String, + resourceId: String, + resourceType: String + ) = withContext(Dispatchers.IO) { + val maxOccurrence = resourceResolutionRepository.findMaxOccurrenceByBlueprintNameAndBlueprintVersionAndResourceIdAndResourceType( + blueprintName, + blueprintVersion, + resourceId, + resourceType + ) + maxOccurrence?.inc() ?: 1 + } } diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionRepository.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionRepository.kt index 6e0ed3a4b..8513bda88 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionRepository.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionRepository.kt @@ -36,6 +36,28 @@ interface ResourceResolutionRepository : JpaRepository + assertEquals("This is Sample Velocity Template", templateMap) + + val assignmentListForRequest1 = mutableListOf( + "service-instance-id" to "siid_1234", + "vnf-id" to "vnf_1234", + "vnf_name" to "temp_vnf" + ) + val assignmentListForRequest2 = mutableListOf( + "service-instance-id" to "siid_new_resolution", + "vnf-id" to "vnf_new_resolution", + "vnf_name" to "temp_vnf_new_resolution" + ) + assertEquals(assignmentListForRequest1.size, assignmentList.size) + assertEquals(assignmentListForRequest2.size, assignmentList.size) + + // AlwaysPerformNewResolution use case - resolution request #2 should returns the resolution as per + // assignmentListForRequest2 since new resolution is performed. + var areEqual = assignmentListForRequest1.zip(assignmentList).all { (it1, it2) -> + it1.first == it2.name && it1.second == it2.property?.value?.asText() ?: null + } + assertEquals(false, areEqual) + + areEqual = assignmentListForRequest2.zip(assignmentList).all { (it1, it2) -> + it1.first == it2.name && it1.second == it2.property?.value?.asText() ?: null + } + assertEquals(true, areEqual) + } + } + + /** + * Don't perform new resolution in case resolution already exists in the database. + */ + @Test + @Throws(Exception::class) + fun testResolveResourceNoNewResolution() { + runBlocking { + // Occurrence > 0 indicates to not perform new resolution if resolution exists in the database. + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = 1 + Assert.assertNotNull("failed to create ResourceResolutionService", resourceResolutionService) + + val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime( + "1234", + "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration" + ) + + // Request#1 + val executionServiceInput = + JacksonUtils.readValueFromClassPathFile( + "payload/requests/sample-resourceresolution-request.json", + ExecutionServiceInput::class.java + )!! + + val resourceAssignmentRuntimeService = + ResourceAssignmentUtils.transformToRARuntimeService( + bluePrintRuntimeService, + "testResolveResource" + ) + + // Prepare inputs from Request#1 + PayloadUtils.prepareInputsFromWorkflowPayload( + bluePrintRuntimeService, + executionServiceInput.payload, + "resource-assignment" + ) + // Resolve resources as per Request#1 + resourceResolutionService.resolveResources( + resourceAssignmentRuntimeService, + "resource-assignment", + "baseconfig", + props + ) + + // Request#2 + val executionServiceInput2 = + JacksonUtils.readValueFromClassPathFile( + "payload/requests/sample-resourceresolution-request2.json", + ExecutionServiceInput::class.java + )!! + + // Prepare inputs from Request#2 + PayloadUtils.prepareInputsFromWorkflowPayload( + bluePrintRuntimeService, + executionServiceInput2.payload, + "resource-assignment" + ) + + // Resolve resources as per Request#2 + resourceResolutionService.resolveResources( + resourceAssignmentRuntimeService, + "resource-assignment", + "baseconfig", + props + ) + }.let { (templateMap, assignmentList) -> + assertEquals("This is Sample Velocity Template", templateMap) + + val assignmentListForRequest1 = mutableListOf( + "service-instance-id" to "siid_1234", + "vnf-id" to "vnf_1234", + "vnf_name" to "temp_vnf" + ) + val assignmentListForRequest2 = mutableListOf( + "service-instance-id" to "siid_new_resolution", + "vnf-id" to "vnf_new_resolution", + "vnf_name" to "temp_vnf_new_resolution" + ) + assertEquals(assignmentListForRequest1.size, assignmentList.size) + assertEquals(assignmentListForRequest2.size, assignmentList.size) + + // NoNewResolution use case - resolution for request #2 returns the same resolution as + // assignmentListForRequest1 since no new resolution is/was actually performed. + var areEqual = assignmentListForRequest1.zip(assignmentList).all { (it1, it2) -> + it1.first == it2.name && it1.second == it2.property?.value?.asText() ?: null + } + assertEquals(true, areEqual) + + areEqual = assignmentListForRequest2.zip(assignmentList).all { (it1, it2) -> + it1.first == it2.name && it1.second == it2.property?.value?.asText() ?: null + } + assertEquals(false, areEqual) + } + } + @Test @Throws(Exception::class) fun testResolveResources() { diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/test/resources/payload/requests/sample-resourceresolution-request2.json b/ms/blueprintsprocessor/functions/resource-resolution/src/test/resources/payload/requests/sample-resourceresolution-request2.json new file mode 100644 index 000000000..726477515 --- /dev/null +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/test/resources/payload/requests/sample-resourceresolution-request2.json @@ -0,0 +1,31 @@ +{ + "actionIdentifiers": { + "actionName": "sample-action", + "blueprintName": "sample-blueprint", + "blueprintVersion": "1.0.0", + "mode": "sync" + }, + "commonHeader": { + "flags": { + "isForce": true, + "ttl": 3600 + }, + "originatorId": "unit_tests", + "requestId": "123456-1001", + "subRequestId": "sub-123456-1001" + }, + "payload": { + "resource-assignment-request": { + "resource-assignment-properties": { + "request-id": "1234", + "profile_name": "1.0.0", + "service-instance-id": "siid_new_resolution", + "vnf-id": "vnf_new_resolution", + "action-name": "assign-activate", + "scope-type": "vnf-type", + "hostname": "localhost", + "vnf_name": "temp_vnf_new_resolution" + } + } + } +} -- cgit 1.2.3-korg