aboutsummaryrefslogtreecommitdiffstats
path: root/ms/blueprintsprocessor/modules/services/execution-service/src
diff options
context:
space:
mode:
authorJozsef Csongvai <jozsef.csongvai@bell.ca>2020-06-15 08:42:08 -0400
committerKAPIL SINGAL <ks220y@att.com>2020-06-19 13:18:51 +0000
commit3579d15b41d9f786650f76c3b6a98d28f0052f1a (patch)
tree56d00d692f3569c8a705261a9184b06feefb7afe /ms/blueprintsprocessor/modules/services/execution-service/src
parent18174bb951a8193e5c310969fb85684b3145fd0e (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/src')
-rw-r--r--ms/blueprintsprocessor/modules/services/execution-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/AbstractComponentFunction.kt33
-rw-r--r--ms/blueprintsprocessor/modules/services/execution-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/scripts/AbstractComponentFunctionTest.kt135
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")
- }
}