[vFW_CNF_CDS] Add workflow health-check and K8sHealthCheck.kt script 48/120048/39
authorj.blixt <j.blixt@partner.samsung.com>
Wed, 31 Mar 2021 09:48:49 +0000 (11:48 +0200)
committerGrzegorz Wielgosinski <g.wielgosins@samsung.com>
Mon, 24 May 2021 14:18:31 +0000 (16:18 +0200)
Issue-ID: INT-1899
Signed-off-by: j.blixt <j.blixt@partner.samsung.com>
Signed-off-by: Grzegorz Wielgosinski <g.wielgosins@samsung.com>
Signed-off-by: Konrad Bańka <k.banka@samsung.com>
Change-Id: I7d52c49bd0e40d30a560b2012362d38488392be6

heat/vFW_CNF_CDS/templates/cba-dd.json
heat/vFW_CNF_CDS/templates/cba/Definitions/artifact_types.json
heat/vFW_CNF_CDS/templates/cba/Definitions/data_types.json
heat/vFW_CNF_CDS/templates/cba/Definitions/node_types.json
heat/vFW_CNF_CDS/templates/cba/Definitions/vFW_CNF_CDS.json
heat/vFW_CNF_CDS/templates/cba/Scripts/kotlin/K8sHealthCheck.kt [new file with mode: 0644]
heat/vFW_CNF_CDS/templates/cba/Scripts/kotlin/SimpleStatusCheck.kt
heat/vFW_CNF_CDS/templates/cba/pom.xml

index f4ba082..157d8b3 100644 (file)
             }
         }
     },
             }
         }
     },
+    {
+        "name": "k8s-rb-instance-release-name",
+        "tags": "k8s, cnf, profile, k8s-rb-instance-release-name",
+        "data_type": "string",
+        "description": "Name of the release for the helm package instance in k8s",
+        "entry_schema": "string",
+        "updatedBy": "Rajewski, Lukasz <lukasz.rajewski@orange.com>",
+        "definition": {
+            "tags": "k8s, cnf, profile, k8s-rb-instance-release-name",
+            "name": "k8s-rb-instance-release-name",
+            "property": {
+                "description": "Name of the release for the helm package instance in k8s",
+                "type": "string"
+            },
+            "group": "default",
+            "updated-by": "Rajewski, Lukasz <lukasz.rajewski@orange.com>",
+            "sources": {
+                "input": {
+                    "type": "source-input"
+                },
+                "default": {
+                    "type": "source-default",
+                    "properties": {}
+                }
+            }
+        }
+    },
     {
         "name": "k8s-rb-profile-namespace",
         "tags": "k8s, cnf, profile, namespace, k8s-rb-profile-namespace",
     {
         "name": "k8s-rb-profile-namespace",
         "tags": "k8s, cnf, profile, namespace, k8s-rb-profile-namespace",
index 778394d..70935b3 100644 (file)
@@ -23,8 +23,7 @@
             "description": " Velocity Template used for Configuration",
             "file_ext": [
                 "vtl"
             "description": " Velocity Template used for Configuration",
             "file_ext": [
                 "vtl"
-            ],
-            "version": "1.0.0"
+            ]
         }
     }
 }
         }
     }
 }
index f4d9266..e797e79 100644 (file)
@@ -5,16 +5,32 @@
             "description": "Dynamic DataType definition for workflow(config-assign).",
             "properties": {
                 "config-deploy-setup": {
             "description": "Dynamic DataType definition for workflow(config-assign).",
             "properties": {
                 "config-deploy-setup": {
+                    "description": "configuration for config value setup",
                     "type": "json"
                 },
                 "service-instance-id": {
                     "type": "json"
                 },
                 "service-instance-id": {
+                    "constraints": [
+                        {}
+                    ],
                     "description": "",
                     "description": "",
+                    "entry_schema": {
+                        "type": ""
+                    },
                     "required": false,
                     "required": false,
+                    "status": "",
                     "type": "string"
                 },
                 "vf-modules-list": {
                     "type": "json"
                 },
                     "type": "string"
                 },
                 "vf-modules-list": {
                     "type": "json"
                 },
+                "vf-modules-list-aai": {
+                    "description": "list of modules associated with vnf from AAI",
+                    "type": "json"
+                },
+                "vf-modules-list-sdnc": {
+                    "description": "list of modules associated with vnf from MDSAL",
+                    "type": "json"
+                },
                 "vnf-id": {
                     "description": "",
                     "required": false,
                 "vnf-id": {
                     "description": "",
                     "required": false,
             "description": "Dynamic DataType definition for workflow(config-deploy).",
             "properties": {
                 "config-deploy-setup": {
             "description": "Dynamic DataType definition for workflow(config-deploy).",
             "properties": {
                 "config-deploy-setup": {
+                    "description": "configuration for config value setup",
                     "type": "json"
                 },
                 "service-instance-id": {
                     "type": "json"
                 },
                 "service-instance-id": {
+                    "constraints": [
+                        {}
+                    ],
                     "description": "",
                     "description": "",
+                    "entry_schema": {
+                        "type": ""
+                    },
                     "required": false,
                     "required": false,
+                    "status": "",
                     "type": "string"
                 },
                     "type": "string"
                 },
-                "vf-modules-list": {
+                "vf-modules-list-aai": {
+                    "description": "list of modules associated with vnf from AAI",
+                    "type": "json"
+                },
+                "vf-modules-list-sdnc": {
+                    "description": "list of modules associated with vnf from MDSAL",
+                    "type": "json"
+                },
+                "vnf-id": {
+                    "description": "",
+                    "required": false,
+                    "type": "string"
+                }
+            }
+        },
+        "dt-health-check-properties": {
+            "description": "Dynamic DataType definition for workflow(health-check).",
+            "properties": {
+                "config-deploy-setup": {
+                    "description": "configuration for config value setup",
+                    "type": "json"
+                },
+                "service-instance-id": {
+                    "constraints": [
+                        {}
+                    ],
+                    "description": "",
+                    "entry_schema": {
+                        "type": ""
+                    },
+                    "required": false,
+                    "status": "",
+                    "type": "string"
+                },
+                "vf-modules-list-aai": {
+                    "description": "list of modules associated with vnf from AAI",
+                    "type": "json"
+                },
+                "vf-modules-list-sdnc": {
+                    "description": "list of modules associated with vnf from MDSAL",
                     "type": "json"
                 },
                 "vnf-id": {
                     "type": "json"
                 },
                 "vnf-id": {
index 98ed5d2..5ad4476 100644 (file)
                                 "dynamic-properties": {
                                     "description": "Dynamic Json Content or DSL Json reference.",
                                     "required": false,
                                 "dynamic-properties": {
                                     "description": "Dynamic Json Content or DSL Json reference.",
                                     "required": false,
-                                    "type": "json"
+                                    "type": "boolean"
                                 },
                                 "occurrence": {
                                     "default": 1,
                                 },
                                 "occurrence": {
                                     "default": 1,
             "derived_from": "tosca.nodes.Root",
             "description": "TOSCA base type for Resource Sources",
             "version": "1.0.0"
             "derived_from": "tosca.nodes.Root",
             "description": "TOSCA base type for Resource Sources",
             "version": "1.0.0"
-        },
-        "tosca.nodes.Vnf": {
-            "derived_from": "tosca.nodes.Root",
-            "description": "This is VNF Node Type",
-            "version": "1.0.0"
-        },
-        "tosca.nodes.Workflow": {
-            "derived_from": "tosca.nodes.Root",
-            "description": "This is Directed Graph Node Type",
-            "version": "1.0.0"
-        },
-        "vnf-netconf-device": {
-            "capabilities": {
-                "netconf": {
-                    "properties": {
-                        "connection-time-out": {
-                            "default": 30,
-                            "required": false,
-                            "type": "integer"
-                        },
-                        "login-account": {
-                            "default": "sdnc-tacacs",
-                            "required": true,
-                            "type": "string"
-                        },
-                        "login-key": {
-                            "default": "sdnc",
-                            "required": true,
-                            "type": "string"
-                        },
-                        "port-number": {
-                            "default": 830,
-                            "required": true,
-                            "type": "integer"
-                        },
-                        "source": {
-                            "default": "npm",
-                            "required": false,
-                            "type": "string"
-                        },
-                        "target-ip-address": {
-                            "required": true,
-                            "type": "string"
-                        }
-                    },
-                    "type": "tosca.capabilities.Netconf"
-                },
-                "restconf": {
-                    "properties": {
-                        "connection-time-out": {
-                            "default": 30,
-                            "required": false,
-                            "type": "integer"
-                        },
-                        "login-account": {
-                            "required": true,
-                            "type": "string"
-                        },
-                        "login-key": {
-                            "required": true,
-                            "type": "string"
-                        },
-                        "port-number": {
-                            "required": true,
-                            "type": "integer"
-                        },
-                        "target-ip-address": {
-                            "required": true,
-                            "type": "string"
-                        }
-                    },
-                    "type": "tosca.capabilities.Restconf"
-                }
-            },
-            "derived_from": "tosca.nodes.Vnf",
-            "description": "This is VNF Device with Netconf  Capability",
-            "version": "1.0.0"
         }
     }
 }
         }
     }
 }
index c17caca..22f5749 100644 (file)
                         "type": "dt-config-deploy-properties"
                     }
                 }
                         "type": "dt-config-deploy-properties"
                     }
                 }
+            },
+            "health-check": {
+                "steps": {
+                    "config-setup": {
+                        "description": "Gather necessary input for config init and status verification",
+                        "target": "config-setup-process",
+                        "activities": [
+                            {
+                                "call_operation": "ResourceResolutionComponent.process"
+                            }
+                        ],
+                        "on_success": [
+                            "config-apply"
+                        ],
+                        "on_failure": [
+                            "handle_error"
+                        ]
+                    },
+                    "config-apply": {
+                        "description": "Activate K8s config template",
+                        "target": "k8s-config-apply",
+                        "activities": [
+                            {
+                                "call_operation": "K8sConfigTemplateComponent.process"
+                            }
+                        ],
+                        "on_success": [
+                            "status-verification-script"
+                        ]
+                    },
+                    "status-verification-script": {
+                        "description": "Simple status verification script",
+                        "target": "simple-status-check",
+                        "activities": [
+                            {
+                                "call_operation": "ComponentScriptExecutor.process"
+                            }
+                        ],
+                        "on_success": [
+                            "health-check-process"
+                        ],
+                        "on_failure": [
+                            "handle_error"
+                        ]
+                    },
+                    "health-check-process": {
+                        "description": "Start health check script",
+                        "target": "health-check-script",
+                        "activities": [
+                            {
+                                "call_operation": "ComponentScriptExecutor.process"
+                            }
+                        ],
+                        "on_success": [
+                            "collect-results"
+                        ],
+                        "on_failure": [
+                            "handle_error"
+                        ]
+                    },
+                    "handle_error": {
+                        "description": "Simple error verification script",
+                        "target": "simple-error-check",
+                        "activities": [
+                            {
+                                "call_operation": "ComponentScriptExecutor.process"
+                            }
+                        ],
+                        "on_success": [
+                            "collect-results"
+                        ]
+                    },
+                    "collect-results": {
+                        "description": "Final collection of results",
+                        "target": "collect-results"
+                    }
+                },
+                "inputs": {
+                    "resolution-key": {
+                        "required": true,
+                        "type": "string"
+                    },
+                    "config-deploy-properties": {
+                        "description": "Dynamic PropertyDefinition for workflow(config-deploy).",
+                        "required": true,
+                        "type": "dt-config-deploy-properties"
+                    }
+                }
             }
         },
         "node_templates": {
             }
         },
         "node_templates": {
                     }
                 }
             },
                     }
                 }
             },
+            "health-check-script": {
+                "type": "component-script-executor",
+                "interfaces": {
+                    "ComponentScriptExecutor": {
+                        "operations": {
+                            "process": {
+                                "inputs": {
+                                    "script-type": "kotlin",
+                                    "script-class-reference": "org.onap.ccsdk.cds.blueprintsprocessor.services.execution.scripts.K8sHealthCheck",
+                                    "instance-dependencies": [
+                                        "blueprintPropertiesService"
+                                    ],
+                                    "dynamic-properties": "*simple-status-properties"
+                                }
+                            }
+                        }
+                    }
+                }
+            },
             "config-setup-process": {
                 "type": "component-resource-resolution",
                 "interfaces": {
             "config-setup-process": {
                 "type": "component-resource-resolution",
                 "interfaces": {
diff --git a/heat/vFW_CNF_CDS/templates/cba/Scripts/kotlin/K8sHealthCheck.kt b/heat/vFW_CNF_CDS/templates/cba/Scripts/kotlin/K8sHealthCheck.kt
new file mode 100644 (file)
index 0000000..6c45b4e
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 Samsung Electronics
+ * 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
+ */
+package org.onap.ccsdk.cds.blueprintsprocessor.services.execution.scripts
+
+import com.fasterxml.jackson.databind.node.ObjectNode
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.joinAll
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import org.onap.ccsdk.cds.blueprintsprocessor.core.BlueprintPropertiesService
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.K8sConnectionPluginConfiguration
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.instance.K8sPluginInstanceApi
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.instance.healthcheck.K8sRbInstanceHealthCheck
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.instance.healthcheck.K8sRbInstanceHealthCheckSimple
+import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction
+import org.slf4j.LoggerFactory
+
+open class K8sHealthCheck : AbstractScriptComponentFunction() {
+
+    private val log = LoggerFactory.getLogger(K8sHealthCheck::class.java)!!
+
+    override fun getName(): String {
+        return "K8sHealthCheck"
+    }
+
+    private fun initPluginApi(): K8sPluginInstanceApi {
+        val bluePrintPropertiesService: BlueprintPropertiesService = this.functionDependencyInstanceAsType("blueprintPropertiesService")!!
+        val k8sConfiguration = K8sConnectionPluginConfiguration(bluePrintPropertiesService)
+
+        return K8sPluginInstanceApi(k8sConfiguration)
+    }
+
+    override suspend fun processNB(executionRequest: ExecutionServiceInput) {
+        val instanceApi = initPluginApi()
+
+        log.info("Health check script execution - START")
+        val configValueSetup: ObjectNode = getDynamicProperties("config-deploy-setup") as ObjectNode
+        log.info("Config Value Setup: $configValueSetup")
+
+        val instanceHealthCheckList = startInstanceHealthCheck(configValueSetup, instanceApi)
+        val statuses = getStatuses(instanceHealthCheckList, instanceApi)
+        log.info("Health check script execution - END")
+    }
+
+    private fun startInstanceHealthCheck(configValueSetup: ObjectNode, instanceApi: K8sPluginInstanceApi): List<HealthCheckInstance> {
+        val healthCheckInstanceList = arrayListOf<HealthCheckInstance>()
+
+        configValueSetup.fields().forEach {
+            val instanceName = it.value.get("k8s-instance-id").asText()
+            val response: K8sRbInstanceHealthCheckSimple? = instanceApi.startInstanceHealthCheck(instanceName)
+            log.debug("K8sRbInstanceHealthCheckSimple response: $$response")
+            healthCheckInstanceList.add(HealthCheckInstance(instanceName, response?.id))
+        }
+        log.info("healthCheckInstanceList: $healthCheckInstanceList")
+
+        return healthCheckInstanceList
+    }
+
+    private fun getStatuses(instanceHealthCheckList: List<HealthCheckInstance>, instanceApi: K8sPluginInstanceApi): Map<String, String> {
+        val statuses = hashMapOf<String, String>()
+        runBlocking {
+            val jobs: List<Job> = instanceHealthCheckList.map {
+                launch {
+                    log.info("Thread started: ${Thread.currentThread().name} for $it")
+                    // WAIT APPROX 5 MINUTES
+                    repeat(30) { _ ->
+                        val response: K8sRbInstanceHealthCheck = instanceApi.getInstanceHealthCheck(it.heatStackId, it.healthCheckInstance!!)!!
+                        log.debug("Response for $it: $response")
+                        val status = response.status!!
+                        if (!"RUNNING".equals(status, true)) {
+                            statuses[it.heatStackId] = status
+                            log.info("Poll status: $status for $it")
+                            instanceApi.deleteInstanceHealthCheck(it.heatStackId, it.healthCheckInstance)
+                            cancel()
+                        }
+                        delay(10_000L)
+                    }
+                    statuses[it.heatStackId] = "Timeout"
+                    log.warn("Send delete hc request")
+                    instanceApi.deleteInstanceHealthCheck(it.heatStackId, it.healthCheckInstance!!)
+                }
+            }
+            jobs.joinAll()
+        }
+        log.info("Get statuses finished:")
+        log.info("$statuses")
+        return statuses
+    }
+
+    data class HealthCheckInstance(val heatStackId: String, val healthCheckInstance: String?) {
+        override fun toString(): String {
+            return "HealthCheckInstance(heatStackId='$heatStackId', healthCheckInstance='$healthCheckInstance')"
+        }
+    }
+
+    override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
+        log.info("Executing Recovery")
+        bluePrintRuntimeService.getBlueprintError().addError("${runtimeException.message}", getName())
+    }
+}
index 9033064..e2d1053 100644 (file)
@@ -40,7 +40,7 @@ open class SimpleStatusCheck : AbstractScriptComponentFunction() {
         val configValueSetup: ObjectNode = getDynamicProperties("config-deploy-setup") as ObjectNode
 
         val bluePrintPropertiesService: BlueprintPropertiesService =
         val configValueSetup: ObjectNode = getDynamicProperties("config-deploy-setup") as ObjectNode
 
         val bluePrintPropertiesService: BlueprintPropertiesService =
-                this.functionDependencyInstanceAsType("blueprintPropertiesService")
+            this.functionDependencyInstanceAsType("blueprintPropertiesService")
 
         val k8sConfiguration = K8sConnectionPluginConfiguration(bluePrintPropertiesService)
 
 
         val k8sConfiguration = K8sConnectionPluginConfiguration(bluePrintPropertiesService)
 
@@ -54,7 +54,10 @@ open class SimpleStatusCheck : AbstractScriptComponentFunction() {
                 val instanceName = it.value.get("k8s-instance-id").asText()
 
                 val instanceStatus: K8sRbInstanceStatus? = instanceApi.getInstanceStatus(instanceName)
                 val instanceName = it.value.get("k8s-instance-id").asText()
 
                 val instanceStatus: K8sRbInstanceStatus? = instanceApi.getInstanceStatus(instanceName)
+                log.debug("Get status for $instanceName")
+                var status = ""
                 instanceStatus?.resourcesStatus?.forEach {
                 instanceStatus?.resourcesStatus?.forEach {
+                    log.debug("Resource: name=$it.name kind=$it.gvk.kind group=$it.gvk.group version=$it.gvk.version")
                     if (it.gvk?.kind == "Pod") {
                         var version = it.gvk?.version!!
                         if (it.gvk?.group!! != "")
                     if (it.gvk?.kind == "Pod") {
                         var version = it.gvk?.version!!
                         if (it.gvk?.group!! != "")
@@ -62,10 +65,12 @@ open class SimpleStatusCheck : AbstractScriptComponentFunction() {
                         // val podStatus = instanceApi.queryInstanceStatus(instanceName, it.gvk?.kind!!, version, it.name, null)
                         // log.info(podStatus.toString())
                         val podState = it.status?.get("status") as Map<String, Object>
                         // val podStatus = instanceApi.queryInstanceStatus(instanceName, it.gvk?.kind!!, version, it.name, null)
                         // log.info(podStatus.toString())
                         val podState = it.status?.get("status") as Map<String, Object>
-
-                        if ((podState["phase"] as String) != "Running") {
+                        status = podState["phase"] as String
+                        if (status != "Running") {
                             continueCheck = true
                             continueCheck = true
-                            log.info("Pod ${it.name} [$vfModuleName] has invalid state ${(podState["phase"])}")
+                            log.info("Pod ${it.name} [$vfModuleName] has INVALID state ${(podState["phase"])}")
+                        } else {
+                            log.info("Pod ${it.name} [$vfModuleName] has VALID state ${(podState["phase"])}")
                         }
                     }
                 }
                         }
                     }
                 }
@@ -79,7 +84,7 @@ open class SimpleStatusCheck : AbstractScriptComponentFunction() {
                 checkCount = 0
         }
 
                 checkCount = 0
         }
 
-        log.info("SIMPLE STATUS CHECK - END")
+        log.info("SIMPLE STATUS CHECK - END SUCCESS")
     }
 
     override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
     }
 
     override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
index fdb149e..f03f4dc 100644 (file)
 
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
-    <dependencies>
-        <dependency>
-            <groupId>org.onap.ccsdk.cds.blueprintsprocessor.modules</groupId>
-            <artifactId>processor-core</artifactId>
-            <version>1.1.1-SNAPSHOT</version>
-            <scope>compile</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onap.ccsdk.cds.blueprintsprocessor.functions</groupId>
-            <artifactId>k8s-connection-plugin</artifactId>
-            <version>1.1.1-SNAPSHOT</version>
-            <scope>compile</scope>
-        </dependency>
-    </dependencies>
 
     <parent>
 
     <parent>
-        <groupId>org.onap.ccsdk.cds.blueprintsprocessor</groupId>
+        <groupId>org.onap.ccsdk.cds.components.cba</groupId>
         <artifactId>test-blueprint-kotlin-parent</artifactId>
         <artifactId>test-blueprint-kotlin-parent</artifactId>
-        <version>1.0.0-SNAPSHOT</version>
+        <version>1.2.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>vFW_CNF_CDS</artifactId>
     </parent>
 
     <artifactId>vFW_CNF_CDS</artifactId>
-    <version>1.0.0-SNAPSHOT</version>
+    <version>1.2.0-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <properties>
     <packaging>pom</packaging>
 
     <properties>
         <cds.publish.endpoint>api/v1/blueprint-model/publish</cds.publish.endpoint>
         <ca></ca-->
     </properties>
         <cds.publish.endpoint>api/v1/blueprint-model/publish</cds.publish.endpoint>
         <ca></ca-->
     </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onap.ccsdk.cds.blueprintsprocessor.modules</groupId>
+            <artifactId>processor-core</artifactId>
+            <version>1.1.2-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.ccsdk.cds.blueprintsprocessor.functions</groupId>
+            <artifactId>k8s-connection-plugin</artifactId>
+            <version>1.1.2-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
 </project>
 </project>