/** * Cancel waiting job * * @param $jobId * @throws \Keboola\OrchestratorBundle\Exception\OrchestratorException * @return Response */ public function deleteAction($jobId) { $job = $this->jobEsManager->findJobById($jobId); if (!$job) { $exception = new OrchestratorException(404, sprintf('Job %s not found', $jobId)); $exception->setExceptionCode('JOB_NOT_FOUND'); throw $exception; } if (!$job->isWaiting()) { $exception = new OrchestratorException(403, 'Only waiting job can be cancelled'); $exception->setExceptionCode('INVALID_JOB_STATUS'); throw $exception; } $client = new GuzzleHttp\Client([]); try { $response = $client->post(sprintf($this->killUrl, $job->getId()), array('config' => array('curl' => array(CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_0)), 'headers' => array('X-StorageApi-Token' => $this->token, 'X-KBC-RunId' => $this->storageApi->getRunId(), 'X-User-Agent', KeboolaOrchestratorBundle::SYRUP_COMPONENT_NAME . " - API cancel"), 'timeout' => 60)); if ($response->getStatusCode() == 202) { $this->logger->info(sprintf('Orchestration job %s cancelled', $job->getId())); $this->logger->debug(sprintf('Orchestration job %s cancelled - old api', $job->getId()), array('orchestrationId' => $job->getOrchestrationId(), 'orchestration' => $job->getOrchestrationName(), 'projectId' => $job->getProjectId(), 'project' => $job->getTokenOwnerName(), 'tokenId' => $this->token->getId(), 'token' => $this->token->getDescription())); } else { throw new ApplicationException('Job cancel via syrup api failed', null, array('statusCode' => $response->getStatusCode(), 'body' => ResponseDecoder::decode($response))); } } catch (GuzzleHttp\Exception\RequestException $e) { throw new ApplicationException('Job cancel via syrup api failed', $e); } $jsonResponse = $this->createJsonResponse(array(), 204); return $jsonResponse; }
/** * @param Elasticsearch\Job $job * @param Orchestration $orchestration * @param Elasticsearch\JobManager $jobManager * @param KbcComponentsList $components */ public function sendJobWarningMessage(Elasticsearch\Job $job, Orchestration $orchestration, Elasticsearch\JobManager $jobManager, KbcComponentsList $components) { /** * @var Notification[] $notifications */ $notifications = array_filter($orchestration->getNotifications(), function ($row) { /** * @var Notification $row */ if (!\Swift_Validate::email($row->getEmail())) { return false; } return $row->getChannel() === Job::STATUS_WARNING; }); $notificationsEmails = $jobManager->getWarningNotificationsEmails($job); if (!$notificationsEmails) { $notificationsEmails = array(); } // validating emails foreach ($notificationsEmails as $key => $notificationsEmail) { if (!\Swift_Validate::email($notificationsEmail)) { unset($notificationsEmails[$key]); } } if (!count($notifications) && !count($notificationsEmails)) { return; } $message = \Swift_Message::newInstance(); $message->setSubject(sprintf("[KBC] %s orchestrator %s warning", $job->getTokenOwnerName(), $job->getOrchestrationName())); $message->setFrom(self::MAIL_SENDER); foreach ($notificationsEmails as $notificationsEmail) { $message->addTo($notificationsEmail); } foreach ($notifications as $notification) { if ($job->getInitializedBy() !== 'manually') { if (!in_array($notification->getEmail(), $notificationsEmails)) { $message->addTo($notification->getEmail()); } } } $schedule = null; try { $cronSchedule = CronSchedule::fromCronString($orchestration->getCrontabRecord(), 'en'); $schedule = $cronSchedule->asNaturalLanguage(); } catch (\Exception $e) { } $tasks = $job->getResults(); if (empty($tasks->tasks)) { $tasks = array(); } else { $tasks = $tasks->tasks; } $jobUrl = $components->getJobUriTemplate(KeboolaOrchestratorBundle::SYRUP_COMPONENT_NAME); $jobUrl = str_replace('&&projectId&&', $job->getProjectId(), $jobUrl); $jobUrl = str_replace('&&orchestrationId&&', $job->getOrchestrationId(), $jobUrl); $jobUrl = str_replace('&&jobId&&', $job->getId(), $jobUrl); $message->setBody($this->templating->render('KeboolaOrchestratorBundle:Email:jobWarning.email.html.twig', array('schedule' => $schedule, 'tasks' => $tasks, 'componentsIcons' => $components->getComponentsIcons(), 'componentsNames' => $components->getComponentsNames(), 'componentsTypes' => $this->filterComponentsTypes($components->getComponentsTypes()), 'job' => $job, 'jobUrl' => $jobUrl)), 'text/html'); $this->mailer->send($message); /** * @var \Swift_Spool $spool */ $spool = $this->mailer->getTransport()->getSpool(); $spool->flushQueue($this->mailerTransport); }
private function handlePostJobs($orchestrationId, Request $request, $async = false) { $orchestration = $this->dbOrchestrationManager->findOrchestrationById($orchestrationId, $this->token, true); if (!$orchestration) { $exception = new OrchestratorException(404, sprintf('Orchestration %s not found', $orchestrationId)); $exception->setExceptionCode('ORCHESTRATION_NOT_FOUND'); throw $exception; } // waiting jobs limit $jobsStats = $this->jobEsManager->getWaitingStats(); // skip waiting if (array_key_exists($orchestration->getId(), $jobsStats) && $jobsStats[$orchestration->getId()] >= AppConfiguration::MAX_WAITING_JOBS_COUNT) { $count = $jobsStats[$orchestration->getId()]; if ($count > 1) { $exception = new OrchestratorException(409, sprintf('Orchestration %s has %d waiting jobs. Current limit is %d.', $orchestrationId, $count, AppConfiguration::MAX_WAITING_JOBS_COUNT)); } else { $exception = new OrchestratorException(409, sprintf('Orchestration %s has %d waiting job. Current limit is %d.', $orchestrationId, $count, AppConfiguration::MAX_WAITING_JOBS_COUNT)); } $exception->setExceptionCode('ORCHESTRATION_VALIDATION'); throw $exception; } // skip jobs depth $jobsDepth = $this->jobEsManager->getDepthFromRunId($this->storageApi->getRunId()); if ($jobsDepth >= AppConfiguration::MAX_ORCHESTRATION_RUN_DEPTH) { $this->logger->info('scheduler.orchestration.skipped', array('depth' => $jobsDepth, 'limit' => AppConfiguration::MAX_ORCHESTRATION_RUN_DEPTH, 'orchestrationId' => $orchestration->getId(), 'orchestrationName' => $orchestration->getName(), 'projectId' => $this->token->getProjectId(), 'projectName' => $this->token->getOwnerName())); $exception = new OrchestratorException(409, sprintf('Orchestrations can be started only %d times for current id.', AppConfiguration::MAX_ORCHESTRATION_RUN_DEPTH)); $exception->setExceptionCode('RUNTIME_VALIDATION'); throw $exception; } try { $form = $this->createForm(new OrchestrationRunType($this->storageApi, $this->dbOrchestrationManager, $this->token, $orchestration)); $handler = $this->createRunFormHandler($form, $request, $orchestration); if ($handler->process()) { if ($handler->getTaskList()) { $tasks = array_map(function ($task) { /** @var StorageApi\OrchestrationTask $task */ $task->setId($this->storageApi->generateId()); return $task->toApiArray(); }, $handler->getTaskList()); } else { $tasks = array_map(function ($task) { /** * @var StorageApi\OrchestrationTask $task */ return $task->toApiArray(); }, $orchestration->getTasks()); } } } catch (HttpException $e) { $exception = new OrchestratorException($e->getStatusCode(), $e->getMessage()); $exception->setExceptionCode('JOB_VALIDATION'); throw $exception; } $form = $this->createForm(new ScheduleJobType($this->storageApi, $this->dbOrchestrationManager, $this->token)); $handler = parent::createFormHandler($form, $request); try { $notificationsEmails = array(); if ($handler->process()) { $notificationsEmails = $handler->getPost('notificationsEmails', array()); } if (!$notificationsEmails && \Swift_Validate::email($this->token->getDescription())) { $notificationsEmails = array($this->token->getDescription()); } } catch (HttpException $e) { $exception = new OrchestratorException(400, $e->getMessage()); $exception->setExceptionCode('JOB_VALIDATION'); throw $exception; } $this->initSqsQueue(); $job = new Elasticsearch\Job(); $job->setOrchestrationId($orchestration->getId())->setConfig($orchestration->getId())->setOrchestrationName($orchestration->getName())->setToken($orchestration->getToken())->setTokenId($orchestration->getTokenId())->setTokenDesc($orchestration->getTokenDesc())->setTokenOwnerName($this->token->getOwnerName())->setProjectId($this->token->getProjectId())->setInitializedBy('manually')->setTaks($tasks)->setInitiatorTokenId($this->token->getId())->setInitiatorTokenDesc($this->token->getDescription())->setInitiatorUserAgent($this->getRequestUserAgent($request))->setNotificationsEmails($notificationsEmails); $job = $this->jobEsManager->saveJob($job, new StorageApi\UniqueManager($this->storageApi)); $this->queue->enqueue($job->getId(), array('jobId' => $job->getId(), 'component' => KeboolaOrchestratorBundle::SYRUP_COMPONENT_NAME)); // log event $this->logger->info(sprintf('Orchestration job %s created manually', $job->getId())); if ($async) { return $this->createJsonResponse($job->toApiArray(), 202); } else { $this->logger->debug(sprintf('Orchestration job %s created manually - sync', $job->getId()), array('orchestrationId' => $orchestration->getId(), 'orchestration' => $orchestration->getName(), 'projectId' => $this->token->getProjectId(), 'projectName' => $this->token->getOwnerName(), 'tokenId' => $this->token->getId(), 'token' => $this->token->getDescription())); return $this->createJsonResponse($job->toOldApiArray(), 201); } }
/** * @param JobResult $jobResult */ protected function saveJobResult(JobResult $jobResult) { $this->jobEsManager->updateJobResult($this->job, $jobResult); }