diff options
author | Jozsef Csongvai <jozsef.csongvai@bell.ca> | 2020-06-15 08:42:08 -0400 |
---|---|---|
committer | KAPIL SINGAL <ks220y@att.com> | 2020-06-19 13:18:51 +0000 |
commit | 3579d15b41d9f786650f76c3b6a98d28f0052f1a (patch) | |
tree | 56d00d692f3569c8a705261a9184b06feefb7afe /ms/blueprintsprocessor/modules/services/execution-service | |
parent | 18174bb951a8193e5c310969fb85684b3145fd0e (diff) |
Implement nodetemplate locking feature
Enables locking execution of a nodetemplate using a
lock key and lock acquire timeout.
Issue-ID: CCSDK-2460
Change-Id: I308d4d89dab44b7f7a766d5b62258e67b051eab1
Signed-off-by: Jozsef Csongvai <jozsef.csongvai@bell.ca>
Diffstat (limited to 'ms/blueprintsprocessor/modules/services/execution-service')
2 files changed, 144 insertions, 24 deletions
diff --git a/ms/blueprintsprocessor/modules/services/execution-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/AbstractComponentFunction.kt b/ms/blueprintsprocessor/modules/services/execution-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/AbstractComponentFunction.kt index aa39a1dd0..211bf76fb 100644 --- a/ms/blueprintsprocessor/modules/services/execution-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/AbstractComponentFunction.kt +++ b/ms/blueprintsprocessor/modules/services/execution-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/AbstractComponentFunction.kt @@ -23,9 +23,14 @@ import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInpu import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceOutput import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.Status import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.StepData +import org.onap.ccsdk.cds.blueprintsprocessor.core.cluster.executeWithLock +import org.onap.ccsdk.cds.blueprintsprocessor.core.service.BluePrintClusterService +import org.onap.ccsdk.cds.blueprintsprocessor.core.service.CDS_LOCK_GROUP import org.onap.ccsdk.cds.controllerblueprints.common.api.EventType import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException +import org.onap.ccsdk.cds.controllerblueprints.core.asJsonType +import org.onap.ccsdk.cds.controllerblueprints.core.checkNotBlank import org.onap.ccsdk.cds.controllerblueprints.core.checkNotEmpty import org.onap.ccsdk.cds.controllerblueprints.core.data.Implementation import org.onap.ccsdk.cds.controllerblueprints.core.getAsString @@ -49,6 +54,7 @@ abstract class AbstractComponentFunction : BlueprintFunctionNode<ExecutionServic lateinit var executionServiceInput: ExecutionServiceInput var executionServiceOutput = ExecutionServiceOutput() lateinit var bluePrintRuntimeService: BluePrintRuntimeService<*> + lateinit var bluePrintClusterService: BluePrintClusterService lateinit var implementation: Implementation lateinit var processId: String lateinit var workflowName: String @@ -95,6 +101,22 @@ abstract class AbstractComponentFunction : BlueprintFunctionNode<ExecutionServic implementation = bluePrintRuntimeService.bluePrintContext() .nodeTemplateOperationImplementation(nodeTemplateName, interfaceName, operationName) ?: Implementation() + + /** Resolve and validate lock properties */ + implementation.lock?.apply { + val resolvedValues = bluePrintRuntimeService.resolvePropertyAssignments( + nodeTemplateName, + interfaceName, + mutableMapOf("key" to this.key, "acquireTimeout" to this.acquireTimeout)) + this.key = resolvedValues["key"] ?: "".asJsonType() + this.acquireTimeout = resolvedValues["acquireTimeout"] ?: "".asJsonType() + + checkNotBlank(this.key.textValue()) { "Failed to resolve lock key" } + check(this.acquireTimeout.isInt && this.acquireTimeout.intValue() >= 0) { + "Failed to resolve lock acquireTimeout - must be a positive integer" + } + } + check(this::implementation.isInitialized) { "failed to prepare implementation" } val operationResolvedProperties = bluePrintRuntimeService @@ -131,8 +153,17 @@ abstract class AbstractComponentFunction : BlueprintFunctionNode<ExecutionServic } override suspend fun applyNB(executionServiceInput: ExecutionServiceInput): ExecutionServiceOutput { + prepareRequestNB(executionServiceInput) + return implementation.lock?.let { + bluePrintClusterService.clusterLock("${it.key.textValue()}@$CDS_LOCK_GROUP") + .executeWithLock(it.acquireTimeout.intValue().times(1000).toLong()) { + applyNBWithTimeout(executionServiceInput) + } + } ?: applyNBWithTimeout(executionServiceInput) + } + + private suspend fun applyNBWithTimeout(executionServiceInput: ExecutionServiceInput): ExecutionServiceOutput { try { - prepareRequestNB(executionServiceInput) withTimeout((implementation.timeout * 1000).toLong()) { log.debug("DEBUG::: AbstractComponentFunction.withTimeout section ${implementation.timeout} seconds") processNB(executionServiceInput) diff --git a/ms/blueprintsprocessor/modules/services/execution-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/scripts/AbstractComponentFunctionTest.kt b/ms/blueprintsprocessor/modules/services/execution-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/scripts/AbstractComponentFunctionTest.kt index 3caa061e8..e0b690573 100644 --- a/ms/blueprintsprocessor/modules/services/execution-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/scripts/AbstractComponentFunctionTest.kt +++ b/ms/blueprintsprocessor/modules/services/execution-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/scripts/AbstractComponentFunctionTest.kt @@ -25,6 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.node.ObjectNode import io.mockk.every import io.mockk.mockk +import io.mockk.verify import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.runner.RunWith @@ -32,13 +33,18 @@ import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ActionIdentifiers import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.CommonHeader import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.StepData +import org.onap.ccsdk.cds.blueprintsprocessor.core.service.BluePrintClusterService +import org.onap.ccsdk.cds.blueprintsprocessor.core.service.CDS_LOCK_GROUP +import org.onap.ccsdk.cds.blueprintsprocessor.core.service.ClusterLock import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractComponentFunction import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.ComponentFunctionScriptingService import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.nodeTypeComponentScriptExecutor import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintTypes import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive +import org.onap.ccsdk.cds.controllerblueprints.core.asJsonType import org.onap.ccsdk.cds.controllerblueprints.core.data.Implementation +import org.onap.ccsdk.cds.controllerblueprints.core.data.LockAssignment import org.onap.ccsdk.cds.controllerblueprints.core.normalizedPathName import org.onap.ccsdk.cds.controllerblueprints.core.scripts.BluePrintScriptsServiceImpl import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintContext @@ -61,14 +67,20 @@ import kotlin.test.assertNotNull ) class AbstractComponentFunctionTest { + lateinit var bluePrintRuntimeService: DefaultBluePrintRuntimeService lateinit var blueprintContext: BluePrintContext + lateinit var blueprintClusterService: BluePrintClusterService @Autowired lateinit var compSvc: ComponentFunctionScriptingService @BeforeTest fun init() { - blueprintContext = mockk<BluePrintContext>() + bluePrintRuntimeService = mockk() + blueprintContext = mockk() + blueprintClusterService = mockk() + every { bluePrintRuntimeService.bluePrintContext() } returns blueprintContext + every { blueprintContext.rootPath } returns normalizedPathName("target") every { blueprintContext.nodeTemplateOperationImplementation( @@ -80,15 +92,14 @@ class AbstractComponentFunctionTest { @Test fun testAbstractComponent() { runBlocking { - val bluePrintRuntime = mockk<DefaultBluePrintRuntimeService>("1234") val samp = SampleComponent() val comp = samp as AbstractComponentFunction - comp.bluePrintRuntimeService = bluePrintRuntime + comp.bluePrintRuntimeService = bluePrintRuntimeService comp.stepName = "sample-step" assertNotNull(comp, "failed to get kotlin instance") - val input = getMockedInput(bluePrintRuntime) + val input = getMockedInput(bluePrintRuntimeService) val output = comp.applyNB(input) @@ -115,16 +126,14 @@ class AbstractComponentFunctionTest { @Test fun testAbstractScriptComponent() { runBlocking { - val bluePrintRuntime = mockk<DefaultBluePrintRuntimeService>("1234") val samp = SampleRestconfComponent(compSvc) val comp = samp as AbstractComponentFunction - comp.bluePrintRuntimeService = bluePrintRuntime + comp.bluePrintRuntimeService = bluePrintRuntimeService comp.stepName = "sample-step" assertNotNull(comp, "failed to get kotlin instance") - val input = getMockedInput(bluePrintRuntime) - val inp = getMockedContext() + val input = getMockedInput(bluePrintRuntimeService) val output = comp.applyNB(input) @@ -135,17 +144,103 @@ class AbstractComponentFunctionTest { } } - /** - * Mocked input for abstract function test. - */ - private fun getMockedContext() { - val operationOutputs = hashMapOf<String, JsonNode>() + @Test + fun testComponentScriptExecutorNodeType() { + val componentScriptExecutor = BluePrintTypes.nodeTypeComponentScriptExecutor() + assertNotNull(componentScriptExecutor.interfaces, "failed to get interface operations") + } + + @Test + fun `prepareRequestNB should resolve lock properties`() { + val implementation = Implementation().apply { + this.lock = LockAssignment().apply { + this.key = """ {"get_input": "lock-key"} """.asJsonPrimitive() + } + } every { - blueprintContext.name() - } returns "SampleTest" + blueprintContext.nodeTemplateOperationImplementation(any(), any(), any()) + } returns implementation + every { - blueprintContext.version() - } returns "SampleScriptComponent" + bluePrintRuntimeService.resolvePropertyAssignments(any(), any(), any()) + } returns mutableMapOf( + "key" to "abc-123-def-456".asJsonType(), + "acquireTimeout" to implementation.lock!!.acquireTimeout + ) + + val component: AbstractComponentFunction = SampleComponent() + component.bluePrintRuntimeService = bluePrintRuntimeService + component.bluePrintClusterService = blueprintClusterService + + runBlocking { + component.prepareRequestNB(getMockedInput(bluePrintRuntimeService)) + } + + val resolvedLock = component.implementation.lock!! + + assertEquals("abc-123-def-456", resolvedLock.key.textValue()) + // default value + assertEquals(180, resolvedLock.acquireTimeout.intValue()) + } + + @Test(expected = Exception::class) + fun `prepareRequestNB should throw exception if it fails to resolve lock key`() { + every { + blueprintContext.nodeTemplateOperationImplementation(any(), any(), any()) + } returns Implementation().apply { this.lock = LockAssignment() } + + every { + bluePrintRuntimeService.resolvePropertyAssignments(any(), any(), any()) + } returns mutableMapOf("key" to "".asJsonType(), + "acquireTimeout" to Integer(360).asJsonType()) + + val component: AbstractComponentFunction = SampleComponent() + component.bluePrintRuntimeService = bluePrintRuntimeService + component.bluePrintClusterService = blueprintClusterService + + runBlocking { + component.prepareRequestNB(getMockedInput(bluePrintRuntimeService)) + } + } + + @Test + fun `applyNB - when lock is present use ClusterLock`() { + + val lockName = "testing-lock" + + every { + blueprintContext.nodeTemplateOperationImplementation(any(), any(), any()) + } returns Implementation().apply { + this.lock = LockAssignment().apply { this.key = lockName.asJsonType() } + } + + every { + bluePrintRuntimeService.resolvePropertyAssignments(any(), any(), any()) + } returns mutableMapOf("key" to lockName.asJsonType(), + "acquireTimeout" to Integer(180).asJsonType()) + + val clusterLock: ClusterLock = mockk() + + every { clusterLock.name() } returns lockName + every { runBlocking { clusterLock.tryLock(any()) } } returns true + every { runBlocking { clusterLock.unLock() } } returns Unit + + every { + runBlocking { blueprintClusterService.clusterLock(any()) } + } returns clusterLock + + val component: AbstractComponentFunction = SampleComponent() + component.bluePrintRuntimeService = bluePrintRuntimeService + component.bluePrintClusterService = blueprintClusterService + + runBlocking { + component.applyNB(getMockedInput(bluePrintRuntimeService)) + } + + verify { + runBlocking { blueprintClusterService.clusterLock("$lockName@$CDS_LOCK_GROUP") } + } + verify { runBlocking { clusterLock.unLock() } } } /** @@ -199,10 +294,4 @@ class AbstractComponentFunctionTest { return executionServiceInput } - - @Test - fun testComponentScriptExecutorNodeType() { - val componentScriptExecutor = BluePrintTypes.nodeTypeComponentScriptExecutor() - assertNotNull(componentScriptExecutor.interfaces, "failed to get interface operations") - } } |