diff --git a/qa/integration/src/test/java/org/eclipse/kapua/integration/service/jobEngine/RunJobEngineServiceI9nTest.java b/qa/integration/src/test/java/org/eclipse/kapua/integration/service/jobEngine/RunJobEngineServiceI9nTest.java index 408d12e1cee..fa4758d8275 100644 --- a/qa/integration/src/test/java/org/eclipse/kapua/integration/service/jobEngine/RunJobEngineServiceI9nTest.java +++ b/qa/integration/src/test/java/org/eclipse/kapua/integration/service/jobEngine/RunJobEngineServiceI9nTest.java @@ -25,6 +25,7 @@ "classpath:features/jobEngine/JobEngineServiceProcessorCommandI9n.feature", "classpath:features/jobEngine/JobEngineServiceProcessorConfigurationI9n.feature", "classpath:features/jobEngine/JobEngineServiceProcessorKeystoreI9n.feature", + "classpath:features/jobEngine/JobEngineServiceProcessorPackagesI9n.feature", }, glue = { "org.eclipse.kapua.service.job.steps", diff --git a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceProcessorPackagesI9n.feature b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceProcessorPackagesI9n.feature new file mode 100644 index 00000000000..5e1cb019314 --- /dev/null +++ b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceProcessorPackagesI9n.feature @@ -0,0 +1,137 @@ +############################################################################### +# Copyright (c) 2024, 2024 Eurotech and/or its affiliates and others +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Eurotech - initial API and implementation +############################################################################### +@env_docker +@it +@jobEngine +@jobEngineTargetProcessors + +Feature: Job Engine Service - Packages Step Processors + Tests for Device Management Packages Processor + + @setup + Scenario: Setup test resources + Given Init Security Context + And Start Docker environment with resources + | db | + | events-broker | + | job-engine | + | message-broker | + | broker-auth-service | + | consumer-lifecycle | + + Scenario: Package Install - XML + + Given I login as user with name "kapua-sys" and password "kapua-password" + And I start the Kura Mock + And Device birth message is sent + And Device "rpione3" is connected within 10s + And I create a job with the name "TestJob" + And I add device targets to job + | rpione3 | + And I search for step definition with the name + | Package Download / Install | + And I add job step to job with name "Test Step - Package Download and Install - XML" and with selected job step definition and properties + | name | type | value | + | packageDownloadRequest | org.eclipse.kapua.service.device.management.packages.model.download.DevicePackageDownloadRequest | http://download.eclipse.org/kura/releases/3.2.0/org.eclipse.kura.demo.heater_1.0.300.dpheater1.0.300true | + | timeout | java.lang.Long | 5000 | + When I start a job + And I wait job to finish its execution up to 5s + Then I confirm that job has 1 job execution + And I confirm that job target in job has step index 0 and status "AWAITING_COMPLETION" + Then I wait job target to finish processing and notify completion up to 10s + And I wait for another job start up to 10s + And I wait job to finish its execution up to 5s + And I confirm that job has 2 job execution + And I confirm that job target in job has step index 0 and status "PROCESS_OK" + + Scenario: Package Install - JSON + + Given I login as user with name "kapua-sys" and password "kapua-password" + And I start the Kura Mock + And Device birth message is sent + And Device "rpione3" is connected within 10s + And I create a job with the name "TestJob" + And I add device targets to job + | rpione3 | + And I search for step definition with the name + | Package Download / Install | + And I add job step to job with name "Test Step - Package Download and Install - JSON" and with selected job step definition and properties + | name | type | value | + | packageDownloadRequest | org.eclipse.kapua.service.device.management.packages.model.download.DevicePackageDownloadRequest | { "uri": "http://download.eclipse.org/kura/releases/3.2.0/org.eclipse.kura.demo.heater_1.0.300.dp", "name": "heater", "version": "1.0.300", "install": true } | + | timeout | java.lang.Long | 5000 | + When I start a job + And I wait job to finish its execution up to 5s + Then I confirm that job has 1 job execution + And I confirm that job target in job has step index 0 and status "AWAITING_COMPLETION" + Then I wait job target to finish processing and notify completion up to 10s + And I wait for another job start up to 10s + And I wait job to finish its execution up to 5s + And I confirm that job has 2 job execution + And I confirm that job target in job has step index 0 and status "PROCESS_OK" + + Scenario: Package Uninstall - XML + + Given I login as user with name "kapua-sys" and password "kapua-password" + And I start the Kura Mock + And Device birth message is sent + And Device "rpione3" is connected within 10s + And I create a job with the name "TestJob" + And I add device targets to job + | rpione3 | + And I search for step definition with the name + | Package Uninstall | + And I add job step to job with name "Test Step - Package Uninstall - XML" and with selected job step definition and properties + | name | type | value | + | packageUninstallRequest | org.eclipse.kapua.service.device.management.packages.model.uninstall.DevicePackageUninstallRequest | heater1.0.300false30000 | + | timeout | java.lang.Long | 5000 | + When I start a job + And I wait job to finish its execution up to 5s + Then I confirm that job has 1 job execution + And I confirm that job target in job has step index 0 and status "AWAITING_COMPLETION" + Then I wait job target to finish processing and notify completion up to 10s + And I wait for another job start up to 10s + And I wait job to finish its execution up to 5s + And I confirm that job has 2 job execution + And I confirm that job target in job has step index 0 and status "PROCESS_OK" + + Scenario: Package Uninstall - JSON + + Given I login as user with name "kapua-sys" and password "kapua-password" + And I start the Kura Mock + And Device birth message is sent + And Device "rpione3" is connected within 10s + And I create a job with the name "TestJob" + And I add device targets to job + | rpione3 | + And I search for step definition with the name + | Package Uninstall | + And I add job step to job with name "Test Step - Package Uninstall - JSON" and with selected job step definition and properties + | name | type | value | + | packageUninstallRequest | org.eclipse.kapua.service.device.management.packages.model.uninstall.DevicePackageUninstallRequest | { "name": "org.eclipse.kura.demo.heater", "version": "1.0.500", "reboot": false, "rebootDelay": 30000 } | + | timeout | java.lang.Long | 5000 | + When I start a job + And I wait job to finish its execution up to 5s + Then I confirm that job has 1 job execution + And I confirm that job target in job has step index 0 and status "AWAITING_COMPLETION" + Then I wait job target to finish processing and notify completion up to 10s + And I wait for another job start up to 10s + And I wait job to finish its execution up to 5s + And I confirm that job has 2 job execution + And I confirm that job target in job has step index 0 and status "PROCESS_OK" + + @teardown + Scenario: Tear down test resources + Given I logout + And KuraMock is disconnected + And Stop Docker environment + And Clean Locator Instance \ No newline at end of file diff --git a/service/device/registry/test-steps/src/main/java/org/eclipse/kapua/service/device/registry/steps/KuraDevice.java b/service/device/registry/test-steps/src/main/java/org/eclipse/kapua/service/device/registry/steps/KuraDevice.java index 9c30988b186..65076a8f0d3 100644 --- a/service/device/registry/test-steps/src/main/java/org/eclipse/kapua/service/device/registry/steps/KuraDevice.java +++ b/service/device/registry/test-steps/src/main/java/org/eclipse/kapua/service/device/registry/steps/KuraDevice.java @@ -453,79 +453,106 @@ else if (topic.equals(deployGetPackages)) { mqttClient.publish(responseTopic, responsePayload, 0, false); } else if (topic.equals(deployExecDownload)) { callbackParam = extractCallback(requestPayload); - KuraPayload kuraPayloadInitial = new KuraPayload(); - kuraPayloadInitial.readFromByteArray(requestPayload); + KuraPayload kuraRequestPayload = new KuraPayload(); + kuraRequestPayload.readFromByteArray(requestPayload); + + // Reply responseTopic = $EDC + CLIENT_ACCOUNT + "/" + callbackParam.getClientId() + DEPLOY_V2_REPLY + callbackParam.getRequestId(); - KuraPayload customKuraPayload1 = new KuraPayload(); - customKuraPayload1.setTimestamp(new Date()); - customKuraPayload1.getMetrics().put("response.code", 200); - responsePayload = customKuraPayload1.toByteArray(); - mqttClient.publish(responseTopic, responsePayload, 0, false); - Thread.sleep(100); - - responseTopic = $EDC + CLIENT_ACCOUNT + "/" + callbackParam.getClientId() + DEPLOY_V2_NOTIFY + clientId + "/download"; - KuraPayload customKuraPayload2 = new KuraPayload(); - customKuraPayload2.setTimestamp(new Date()); - customKuraPayload2.getMetrics().put(JOB_ID, kuraPayloadInitial.getMetrics().get(JOB_ID)); - customKuraPayload2.getMetrics().put(CLIENT_ID, clientId); - customKuraPayload2.getMetrics().put("dp.download.progress", 50); - customKuraPayload2.getMetrics().put("dp.download.size", 20409); - customKuraPayload2.getMetrics().put("dp.download.status", "IN_PROGRESS"); - customKuraPayload2.getMetrics().put("dp.download.index", 0); - responsePayload = customKuraPayload2.toByteArray(); - mqttClient.publish(responseTopic, responsePayload, 0, false); - Thread.sleep(100); - - responseTopic = $EDC + CLIENT_ACCOUNT + "/" + callbackParam.getClientId() + DEPLOY_V2_NOTIFY + clientId + "/download"; - KuraPayload customKuraPayload3 = new KuraPayload(); - customKuraPayload3.setTimestamp(new Date()); - customKuraPayload3.getMetrics().put(JOB_ID, kuraPayloadInitial.getMetrics().get(JOB_ID)); - customKuraPayload3.getMetrics().put(CLIENT_ID, clientId); - customKuraPayload3.getMetrics().put("dp.download.progress", 100); - customKuraPayload3.getMetrics().put("dp.download.size", 20409); - customKuraPayload3.getMetrics().put("dp.download.status", COMPLETED); - customKuraPayload3.getMetrics().put("dp.download.index", 0); - responsePayload = customKuraPayload3.toByteArray(); - mqttClient.publish(responseTopic, responsePayload, 0, false); - Thread.sleep(100); - - responseTopic = $EDC + CLIENT_ACCOUNT + "/" + callbackParam.getClientId() + DEPLOY_V2_NOTIFY + clientId + "/install"; - KuraPayload customKuraPayload4 = new KuraPayload(); - customKuraPayload4.setTimestamp(new Date()); - customKuraPayload4.getMetrics().put("dp.name", "Example Publisher-1.0.300.dp"); - customKuraPayload4.getMetrics().put(JOB_ID, kuraPayloadInitial.getMetrics().get(JOB_ID)); - customKuraPayload4.getMetrics().put("dp.install.progress", 100); - customKuraPayload4.getMetrics().put("dp.install.status", COMPLETED); - customKuraPayload4.getMetrics().put(CLIENT_ID, clientId); - responsePayload = customKuraPayload4.toByteArray(); + + KuraPayload replyKuraResponsePayload = new KuraPayload(); + replyKuraResponsePayload.setTimestamp(new Date()); + replyKuraResponsePayload.getMetrics().put("response.code", 200); + responsePayload = replyKuraResponsePayload.toByteArray(); + mqttClient.publish(responseTopic, responsePayload, 0, false); + Thread.sleep(1000); + + // Download Notification 25% + String downloadNotifyTopic = $EDC + CLIENT_ACCOUNT + "/" + callbackParam.getClientId() + DEPLOY_V2_NOTIFY + clientId + "/download"; + + KuraPayload downloadNotifyPayload = new KuraPayload(); + downloadNotifyPayload.setTimestamp(new Date()); + downloadNotifyPayload.getMetrics().put(CLIENT_ID, clientId); + downloadNotifyPayload.getMetrics().put(JOB_ID, kuraRequestPayload.getMetrics().get(JOB_ID)); + downloadNotifyPayload.getMetrics().put("dp.download.progress", 25); + downloadNotifyPayload.getMetrics().put("dp.download.size", 20409); + downloadNotifyPayload.getMetrics().put("dp.download.status", "IN_PROGRESS"); + downloadNotifyPayload.getMetrics().put("dp.download.index", 0); + responsePayload = downloadNotifyPayload.toByteArray(); + + mqttClient.publish(downloadNotifyTopic, responsePayload, 0, false); + Thread.sleep(1000); + + // Download Notification 50% + downloadNotifyPayload.setTimestamp(new Date()); + downloadNotifyPayload.getMetrics().put("dp.download.progress", 50); + responsePayload = downloadNotifyPayload.toByteArray(); + + mqttClient.publish(downloadNotifyTopic, responsePayload, 0, false); + Thread.sleep(1000); + + // Download Notification 75% + downloadNotifyPayload.setTimestamp(new Date()); + downloadNotifyPayload.getMetrics().put("dp.download.progress", 75); + responsePayload = downloadNotifyPayload.toByteArray(); + + mqttClient.publish(downloadNotifyTopic, responsePayload, 0, false); + Thread.sleep(1000); + + // Download Notification 100% + downloadNotifyPayload.setTimestamp(new Date()); + downloadNotifyPayload.getMetrics().put("dp.download.progress", 100); + downloadNotifyPayload.getMetrics().put("dp.download.status", COMPLETED); + responsePayload = downloadNotifyPayload.toByteArray(); + + mqttClient.publish(downloadNotifyTopic, responsePayload, 0, false); + Thread.sleep(1000); + + // Install Notification 100% + String installNotifyTopic = $EDC + CLIENT_ACCOUNT + "/" + callbackParam.getClientId() + DEPLOY_V2_NOTIFY + clientId + "/install"; + + KuraPayload installNotifyPayload = new KuraPayload(); + installNotifyPayload.setTimestamp(new Date()); + installNotifyPayload.getMetrics().put(CLIENT_ID, clientId); + installNotifyPayload.getMetrics().put(JOB_ID, kuraRequestPayload.getMetrics().get(JOB_ID)); + installNotifyPayload.getMetrics().put("dp.name", "heater-1.0.300.dp"); + installNotifyPayload.getMetrics().put("dp.install.progress", 100); + installNotifyPayload.getMetrics().put("dp.install.status", COMPLETED); + responsePayload = installNotifyPayload.toByteArray(); + + mqttClient.publish(installNotifyTopic, responsePayload, 0, false); packageListChanged = true; } else if (topic.equals(deployExecUninstall)) { callbackParam = extractCallback(requestPayload); - KuraPayload kuraPayloadInitial = new KuraPayload(); - kuraPayloadInitial.readFromByteArray(requestPayload); + KuraPayload kuraRequestPayload = new KuraPayload(); + kuraRequestPayload.readFromByteArray(requestPayload); + + // Reply topic responseTopic = $EDC + CLIENT_ACCOUNT + "/" + callbackParam.getClientId() + DEPLOY_V2_REPLY + callbackParam.getRequestId(); - KuraPayload customKuraPayload = new KuraPayload(); - customKuraPayload.setTimestamp(new Date()); - customKuraPayload.getMetrics().put("response.code", 200); - responsePayload = customKuraPayload.toByteArray(); + KuraPayload replyResponsePayload = new KuraPayload(); + replyResponsePayload.setTimestamp(new Date()); + replyResponsePayload.getMetrics().put("response.code", 200); + responsePayload = replyResponsePayload.toByteArray(); + mqttClient.publish(responseTopic, responsePayload, 0, false); - Thread.sleep(5000); + Thread.sleep(3000); + // Uninstall notification 100% responseTopic = $EDC + CLIENT_ACCOUNT + "/" + callbackParam.getClientId() + DEPLOY_V2_NOTIFY + clientId + "/uninstall"; - KuraPayload customKuraPayload2 = new KuraPayload(); - - customKuraPayload2.setTimestamp(new Date()); - customKuraPayload2.getMetrics().put(JOB_ID, kuraPayloadInitial.getMetrics().get(JOB_ID)); - customKuraPayload2.getMetrics().put("dp.name", "org.eclipse.kura.example.beacon"); - customKuraPayload2.getMetrics().put("dp.uninstall.progress", 100); - customKuraPayload2.getMetrics().put("dp.uninstall.status", COMPLETED); - customKuraPayload2.getMetrics().put(CLIENT_ID, clientId); - responsePayload = customKuraPayload2.toByteArray(); + + KuraPayload uninstallNotifyPayload = new KuraPayload(); + uninstallNotifyPayload.setTimestamp(new Date()); + uninstallNotifyPayload.getMetrics().put(CLIENT_ID, clientId); + uninstallNotifyPayload.getMetrics().put(JOB_ID, kuraRequestPayload.getMetrics().get(JOB_ID)); + uninstallNotifyPayload.getMetrics().put("dp.name", "org.eclipse.kura.demo.heater"); + uninstallNotifyPayload.getMetrics().put("dp.uninstall.progress", 100); + uninstallNotifyPayload.getMetrics().put("dp.uninstall.status", COMPLETED); + responsePayload = uninstallNotifyPayload.toByteArray(); + mqttClient.publish(responseTopic, responsePayload, 0, false); packageListChangedAfterUninstall = true; diff --git a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobEngineSteps.java b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobEngineSteps.java index 17d735bfce0..267b6aed576 100644 --- a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobEngineSteps.java +++ b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobEngineSteps.java @@ -70,6 +70,32 @@ public void startJob() throws Exception { // Wait Job Running + /** + * Waits the {@link Job} in context to start. + * + * @param waitSeconds The max time to wait + * @throws Exception + * @since 2.1.0 + */ + @And("I wait for another job start up to {int}s") + public void waitJobInContextToStart(int waitSeconds) throws Exception { + Job job = (Job) stepData.get(JOB); + + long now = System.currentTimeMillis(); + while ((System.currentTimeMillis() - now) < (waitSeconds * 1000L)) { + if (jobEngineService.isRunning(job.getScopeId(), job.getId())) { + return; + } + + // Check frequently! + TimeUnit.MILLISECONDS.sleep(25); + } + + Assert.fail("Job " + job.getName() + " did not start an execution within " + waitSeconds + "s"); + } + + // Wait Job Finish Run + /** * Waits the last {@link Job} in context to finish it execution up the given wait time * @@ -117,7 +143,7 @@ private void waitJobUpTo(Job job, int waitSeconds) throws Exception { TimeUnit.MILLISECONDS.sleep(100); } - Assert.fail("Job " + job.getName() + "did not completed its execution within " + waitSeconds + "s"); + Assert.fail("Job " + job.getName() + " did not completed its execution within " + waitSeconds + "s"); } // Check Job Running diff --git a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobTargetServiceSteps.java b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobTargetServiceSteps.java index ea7e630c583..9921b83de09 100644 --- a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobTargetServiceSteps.java +++ b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobTargetServiceSteps.java @@ -27,6 +27,7 @@ import org.eclipse.kapua.model.id.KapuaId; import org.eclipse.kapua.model.query.predicate.AttributePredicate; import org.eclipse.kapua.qa.common.StepData; +import org.eclipse.kapua.service.device.management.registry.operation.notification.ManagementOperationNotification; import org.eclipse.kapua.service.device.registry.Device; import org.eclipse.kapua.service.device.registry.DeviceAttributes; import org.eclipse.kapua.service.device.registry.DeviceFactory; @@ -49,11 +50,14 @@ import javax.inject.Inject; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Singleton public class JobTargetServiceSteps extends JobServiceTestBase { + private final static Logger LOG = LoggerFactory.getLogger(JobTargetServiceSteps.class); + private static final String DEVICE = "Device"; private DeviceFactory deviceFactory; @@ -61,7 +65,6 @@ public class JobTargetServiceSteps extends JobServiceTestBase { private JobTargetService jobTargetService; private JobTargetFactory jobTargetFactory; - final Logger logger = LoggerFactory.getLogger(this.getClass()); @Inject public JobTargetServiceSteps(StepData stepData) { @@ -214,7 +217,7 @@ public void findLastJobTarget() throws Exception { public void checkStepIndexAndStatus(int stepIndex, String status) throws KapuaException { JobTarget jobTarget = (JobTarget) stepData.get(JOB_TARGET); JobTarget target = jobTargetService.find(jobTarget.getScopeId(), jobTarget.getId()); - logger.error("step: {}, status: {}", target.getStepIndex(), target.getStatus().name()); + LOG.error("step: {}, status: {}", target.getStepIndex(), target.getStatus().name()); Assert.assertEquals(stepIndex, target.getStepIndex()); Assert.assertEquals(status, target.getStatus().toString()); } @@ -448,6 +451,35 @@ private void checkJobTargetForJobHas(JobTarget jobTarget, int expectedStepIndex, stepData.put(JOB_TARGET, updatedJobTarget); } + /** + * Waits the {@link JobTarget} in context to finish its processing and to have {@link JobTarget#getStatus()} set to {@link JobTargetStatus#NOTIFIED_COMPLETION} + *

+ * It also takes as a valid {@link JobTarget#getStatus()} {@link JobTargetStatus#PROCESS_OK} because the {@link ManagementOperationNotification} can be processed fast and + * {@link JobTarget} can switch from {@link JobTargetStatus#NOTIFIED_COMPLETION} to {@link JobTargetStatus#PROCESS_OK} while waiting for the next check. + * + * @param waitSeconds The max time to wait + * @throws Exception + * @since 2.1.0 + */ + @Then("I wait job target to finish processing and notify completion up to {int}s") + public void waitJobTargetInContextToNotifyCompletion(int waitSeconds) throws Exception { + JobTarget jobTarget = (JobTarget) stepData.get(JOB_TARGET); + + long now = System.currentTimeMillis(); + while ((System.currentTimeMillis() - now) < (waitSeconds * 1000L)) { + JobTarget updatedJobTarget = jobTargetService.find(jobTarget.getScopeId(), jobTarget.getId()); + if (JobTargetStatus.NOTIFIED_COMPLETION.equals(updatedJobTarget.getStatus()) || + // Processing of notification is fast so we accept also PROCESS_OK as a valid status + JobTargetStatus.PROCESS_OK.equals(updatedJobTarget.getStatus())) { + return; + } + + TimeUnit.MILLISECONDS.sleep(100); + } + + Assert.fail("Job Target" + jobTarget.getId() + " did not notified completion of processing within " + waitSeconds + "s"); + } + // // Private methods //