From 3579d15b41d9f786650f76c3b6a98d28f0052f1a Mon Sep 17 00:00:00 2001 From: Jozsef Csongvai Date: Mon, 15 Jun 2020 08:42:08 -0400 Subject: 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 --- .../execution/AbstractComponentFunction.kt | 33 ++++- .../scripts/AbstractComponentFunctionTest.kt | 135 +++++++++++++++++---- 2 files changed, 144 insertions(+), 24 deletions(-) (limited to 'ms/blueprintsprocessor/modules/services/execution-service/src') 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 + lateinit var bluePrintClusterService: BluePrintClusterService lateinit var implementation: Implementation lateinit var processId: String lateinit var workflowName: String @@ -95,6 +101,22 @@ abstract class AbstractComponentFunction : BlueprintFunctionNode= 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() + 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("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("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() + @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") - } } -- cgit 1.2.3-korg