/**
  * @param Elasticsearch\Job $job
  * @param Notification $notification
  * @param Client $sapi
  * @return array
  */
 public static function longProcessingEvents(Elasticsearch\Job $job, Notification $notification, Client $sapi)
 {
     $query = array(sprintf("type:%s", Event::TYPE_WARN), sprintf("message:'%s'", sprintf(CronWatchdogCommand::EVENT_MESSAGE_LONG_PROCESSING, $job->getId())));
     $params = array('q' => implode(' ', $query), 'component' => KeboolaOrchestratorBundle::SYRUP_COMPONENT_NAME, 'limit' => 1000);
     return array_filter($sapi->listEvents($params), function ($row) use($notification) {
         if (empty($row['params'])) {
             return false;
         }
         if (empty($row['params']['notificationEmail'])) {
             return false;
         }
         return $row['params']['notificationEmail'] === $notification->getEmail();
     });
 }
 /**
  * @param Elasticsearch\Job $job
  * @param Token $token
  * @return OrchestrationTask[]
  */
 public function findTasksByJob(Elasticsearch\Job $job, Token $token)
 {
     $data = $this->components->getConfiguration(KeboolaOrchestratorBundle::SYRUP_COMPONENT_NAME, (new Entity\Orchestration())->setId($job->getOrchestrationId())->getId());
     if (empty($data['configuration']['tasks'])) {
         return array();
     }
     $data = $data['configuration']['tasks'];
     /** @var OrchestrationTask[] $rows */
     $rows = array_map(function ($line) use($token) {
         $task = new OrchestrationTask($token);
         $task->fromArray($line);
         return $task;
     }, $data['configuration']['tasks']);
     return $this->sortTasks($rows);
 }
Example #3
0
 /**
  * @param StorageApiEvent $event
  * @return mixed|string Event ID
  */
 private function save(StorageApiEvent $event)
 {
     $event->setParams(array_merge($event->getParams(), array('jobId' => $this->job->getId(), 'orchestrationId' => $this->job->getOrchestrationId(), 'pid' => getmypid())));
     if ($event->getType() == StorageApiEvent::TYPE_ERROR) {
         $event->setParams(array_merge($event->getParams(), array('notificationEmails' => $this->job->getNotificationsEmails())));
     }
     return EventLogger::create($event, $this->client);
 }
Example #4
0
 /**
  * Update last executed job in orchestration
  *
  * If job failed, send error notification
  *
  * @param SyrupJob $job
  */
 public final function postExecute(SyrupJob $job)
 {
     $this->validateJob($job);
     // not executed or different job - do nothing
     if (!$this->job || !$this->orchestration) {
         return;
     }
     if ($this->job->getId() !== $job->getId()) {
         return;
     }
     $this->job = new Job();
     $this->job->build($job);
     $this->orchestrationManager->updateLastExecutedJob($this->orchestration, $this->job);
     if ($job->getStatus() === SyrupJob::STATUS_ERROR) {
         $this->mailer->sendJobErrorMessage($this->job, $this->orchestration, $this->jobEsManager, $this->componentsList);
     }
     if ($job->getStatus() === SyrupJob::STATUS_WARNING) {
         $this->mailer->sendJobWarningMessage($this->job, $this->orchestration, $this->jobEsManager, $this->componentsList);
     }
 }
Example #5
0
 /**
  * @param Elasticsearch\Job $job
  * @param Orchestration $orchestration
  * @param Notification $notification
  * @param KbcComponentsList $components
  * @param null $averageDuration
  */
 public function sendLongProcessingMessage(Elasticsearch\Job $job, Orchestration $orchestration, Notification $notification, KbcComponentsList $components, $averageDuration = null)
 {
     $notificationsEmails = array($notification->getEmail());
     // validating emails
     foreach ($notificationsEmails as $key => $notificationsEmail) {
         if (!\Swift_Validate::email($notificationsEmail)) {
             unset($notificationsEmails[$key]);
         }
     }
     if (!count($notificationsEmails)) {
         return;
     }
     $message = \Swift_Message::newInstance();
     $message->setSubject(sprintf("[KBC] %s orchestrator %s is still processing", $job->getTokenOwnerName(), $job->getOrchestrationName()));
     $message->setFrom(self::MAIL_SENDER);
     foreach ($notificationsEmails as $notificationsEmail) {
         $message->addTo($notificationsEmail);
     }
     $schedule = null;
     try {
         $cronSchedule = CronSchedule::fromCronString($orchestration->getCrontabRecord(), 'en');
         $schedule = $cronSchedule->asNaturalLanguage();
     } catch (\Exception $e) {
     }
     $results = $job->getResults();
     $tasks = array();
     if (!empty($results->tasks)) {
         $tasks = $results->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:jobLongProcessing.email.html.twig', array('schedule' => $schedule, 'tasks' => $tasks, 'componentsIcons' => $components->getComponentsIcons(), 'componentsNames' => $components->getComponentsNames(), 'componentsTypes' => $this->filterComponentsTypes($components->getComponentsTypes()), 'job' => $job, 'avgDurationString' => Utils::convertDurationToString($averageDuration), 'durationString' => Utils::convertDurationToString(Utils::dateDiffInMinutes($job->getCreatedTime(), new \DateTime()) * 60), 'durationMinutes' => Utils::dateDiffInMinutes($job->getCreatedTime(), new \DateTime()), '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);
     }
 }
Example #7
0
 public function postCleanup(Metadata\Job $job)
 {
     $esJob = new Job();
     $esJob->build($job);
     try {
         $token = new Token($this->storageApi);
     } catch (StorageApiException $e) {
         $this->logger->error('Cleanup error - invalid token', array('jobId' => $esJob->getId()));
         throw new UserException(sprintf("Invalid token for job %d", $esJob->getId()), $e);
     }
     $orchestration = $this->orchestrationManager->findOrchestrationById($esJob->getOrchestrationId(), $token);
     if (!$orchestration) {
         $this->logger->error('PostCleanup error - orchestration not found', array('jobId' => $esJob->getId()));
         throw new UserException(sprintf("Orchestration %s not found. Could not update last job", $esJob->getOrchestrationId()));
     }
     $this->logger->debug('PostCleanup job', array('jobId' => $esJob->getId()));
     $this->orchestrationManager->updateLastExecutedJob($orchestration, $esJob);
 }
Example #8
0
 /**
  * Check if job is using task phases
  *
  * @param Job $job
  * @return bool
  */
 private function usePhases(Job $job)
 {
     $usePhases = false;
     if (is_array($job->getTasks())) {
         foreach ($job->getTasks() as $task) {
             if (!empty($task->phase)) {
                 $usePhases = true;
             }
         }
     }
     return $usePhases;
 }
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     parent::execute($input, $output);
     $this->logger->info('scheduler.start', array());
     $output->writeln('Current time: ' . $this->getStartTime()->format('Y-m-d H:i:s'));
     $this->lock->setLockName('scheduler-' . $this->getStartTime()->format('Y-m-d-H-i'));
     // skip locked
     try {
         if (!$this->lock->lock()) {
             $this->logger->info('scheduler.skipped', array('lockName' => $this->lock->getLockName()));
             return;
         }
     } catch (\Exception $e) {
         $this->logger->info('scheduler.error', array('message' => $e->getMessage(), 'lockName' => $this->lock->getLockName()));
         return;
     }
     $this->logger->info('scheduler.locked', array('lockName' => $this->lock->getLockName()));
     /**
      * @var Elasticsearch\JobManagerFactory $jobManagerFactory
      */
     $jobManagerFactory = $this->getContainer()->get('orchestrator.job_manager.factory');
     $this->queue = $this->getContainer()->get('syrup.queue_factory')->get(KeboolaOrchestratorBundle::SYRUP_COMPONENT_NAME);
     $this->dbOrchestrationManager = $this->getContainer()->get('orchestrator.doctrine.orchestration_manager');
     $newJobs = array();
     $orchestrations = $this->dbOrchestrationManager->findOrchestrations(array());
     foreach ($orchestrations as $orchestration) {
         // skip deleted and non-active orchestrations
         if ($orchestration->getDeleted() || !$orchestration->getActive() || !$orchestration->getCrontabRecord()) {
             continue;
         }
         $cronExpression = CronExpression::factory($orchestration->getCrontabRecord());
         if (!$cronExpression->isDue($this->getStartTime())) {
             continue;
         }
         $jobClient = new Client(array('token' => $orchestration->getToken(), 'url' => $this->getConfiguration()->getStorageApiUrl(), 'userAgent' => $this->appName));
         try {
             $pingClient = new StorageApi\PingClient($jobClient, $this->logger);
             if (!$pingClient->ping()) {
                 $this->logger->info('scheduler.orchestration.skipped.disabled', array('lockName' => $this->lock->getLockName(), 'orchestrationId' => $orchestration->getId(), 'orchestrationName' => $orchestration->getName(), 'projectId' => $orchestration->getProjectId(), 'projectName' => $orchestration->getTokenOwnerName()));
                 continue;
             }
         } catch (\Exception $e) {
             $this->logger->info('scheduler.orchestration.skipped.error', array('lockName' => $this->lock->getLockName(), 'message' => $e->getMessage(), 'orchestrationId' => $orchestration->getId(), 'orchestrationName' => $orchestration->getName(), 'projectId' => $orchestration->getProjectId(), 'projectName' => $orchestration->getTokenOwnerName()));
             continue;
         }
         $token = new StorageApi\Token($jobClient);
         $jobEsManager = $jobManagerFactory->createJobManager($token);
         $jobsStats = $jobEsManager->getWaitingStats();
         // skip waiting
         if (array_key_exists($orchestration->getId(), $jobsStats) && $jobsStats[$orchestration->getId()] >= AppConfiguration::MAX_WAITING_JOBS_COUNT) {
             $this->logger->info('scheduler.orchestration.skipped', array('lockName' => $this->lock->getLockName(), 'waitingCount' => $jobsStats[$orchestration->getId()], 'limit' => AppConfiguration::MAX_WAITING_JOBS_COUNT, 'orchestrationId' => $orchestration->getId(), 'orchestrationName' => $orchestration->getName(), 'projectId' => $token->getProjectId(), 'projectName' => $token->getOwnerName()));
             continue;
         }
         $orchestrationWithTasks = $this->dbOrchestrationManager->findOrchestrationById($orchestration->getId(), new StorageApi\Token($jobClient), true);
         $tasks = array_map(function ($task) {
             /** @var StorageApi\OrchestrationTask $task */
             return $task->toApiArray();
         }, $orchestrationWithTasks->getTasks());
         $orchestration = $this->dbOrchestrationManager->updateLastScheduledTime($orchestration, new \DateTime());
         // save job to queue
         $job = new Elasticsearch\Job();
         $job->setOrchestrationId($orchestration->getId())->setConfig($orchestration->getId())->setOrchestrationName($orchestration->getName())->setToken($orchestration->getToken())->setTokenId($orchestration->getTokenId())->setTokenDesc($orchestration->getTokenDesc())->setTokenOwnerName($token->getOwnerName())->setProjectId($token->getProjectId())->setInitializedBy('scheduler')->setTaks($tasks)->setInitiatorTokenId($orchestration->getTokenId())->setInitiatorTokenDesc($orchestration->getTokenDesc())->setInitiatorUserAgent($this->appName . ' Scheduler');
         $job = $jobEsManager->saveJob($job, new StorageApi\UniqueManager($jobClient));
         $newJobs[] = $job;
         // add job to queue
         try {
             if (is_null($job)) {
                 throw new ApplicationException('Unable to save or retrieve job from ES', null, array('orchestrationId' => $orchestration->getId()));
             }
             $this->queue->enqueue($job->getId(), array('jobId' => $job->getId(), 'component' => KeboolaOrchestratorBundle::SYRUP_COMPONENT_NAME));
             $output->writeln(sprintf('Orchestration %d with cron %s scheduled. Job id: %s', $orchestration->getId(), $orchestration->getCrontabRecord(), $job->getId()));
         } catch (\Exception $e) {
             $this->logger->error('Error during adding job to sqs', array('lockName' => $this->lock->getLockName(), 'jobId' => $job->getId(), 'exception' => $e));
             continue;
         }
         // log event
         try {
             $event = new Event();
             $event->setComponent($this->appName)->setRunId($job->getRunId())->setParams(array('orchestrationId' => $job->getOrchestrationId()))->setResults(array('jobId' => $job->getId()))->setMessage(sprintf('Orchestration job %s scheduled', $job->getId()));
             StorageApi\EventLogger::create($event, $jobClient);
             $this->logger->info($event->getMessage(), array('lockName' => $this->lock->getLockName(), 'configurationId' => $event->getConfigurationId(), 'runId' => $event->getRunId(), 'description' => $event->getDescription(), 'params' => $event->getParams(), 'results' => $event->getResults()));
         } catch (Exception $e) {
             //@TODO user notification when notification API will be available
             $this->logger->warning('Invalid token', array('lockName' => $this->lock->getLockName(), 'jobId' => $job->getId(), 'exception' => $e));
         }
     }
     $this->logger->info('scheduler.end', array('lockName' => $this->lock->getLockName(), 'duration' => $this->getDuration(), 'orchestrationsCheckedCount' => count($orchestrations), 'scheduledJobsCount' => count($newJobs)));
     sleep(90);
     // trying fix double run
     // unlock
     $this->lock->unlock();
 }
 /**
  * @param Elasticsearch\Job $job
  * @param Client $sapi
  * @param StorageApi\Notification $notification
  * @param null $averageDuration
  * @throws Exception
  */
 private function logLongProcessing(Elasticsearch\Job $job, Client $sapi, StorageApi\Notification $notification, $averageDuration = null)
 {
     $event = new Event();
     $event->setComponent(KeboolaOrchestratorBundle::SYRUP_COMPONENT_NAME)->setType(Event::TYPE_WARN)->setMessage(sprintf(self::EVENT_MESSAGE_LONG_PROCESSING, $job->getId()))->setParams(array('jobId' => $job->getId(), 'orchestrationId' => $job->getOrchestrationId(), 'createdTime' => $job->getCreatedTime()->format('c'), 'notificationEmail' => $notification->getEmail(), 'tolerance' => $notification->getParameter('tolerance'), 'averageDuration' => $averageDuration));
     StorageApi\EventLogger::create($event, $sapi);
     $this->logger->error(sprintf(self::EVENT_MESSAGE_LONG_PROCESSING, $job->getId()), array('job' => $job->getId(), 'orchestrationId' => $job->getOrchestrationId(), 'projectId' => $job->getProjectId(), 'project' => $job->getTokenOwnerName(), 'averageDuration' => $averageDuration));
 }
 /**
  * Testing watchdog on waiting jobs from manual run
  *
  */
 public function testManuallyLongProcessingWatchdog()
 {
     $token1 = $this->createNewToken($this->storageApi, 'manage');
     $notification1 = new StorageApi\Notification();
     $notification1->setEmail(TEST_ERROR_NOTIFICATION_EMAIL_1)->setChannel(Metadata\Job::STATUS_WAITING)->setParameters(array('timeout' => 1));
     $notification2 = new StorageApi\Notification();
     $notification2->setEmail(TEST_ERROR_NOTIFICATION_EMAIL_1)->setChannel(Metadata\Job::STATUS_PROCESSING)->setParameters(array('tolerance' => 10));
     $notification3 = new StorageApi\Notification();
     $notification3->setEmail(TEST_ERROR_NOTIFICATION_EMAIL_2)->setChannel(Metadata\Job::STATUS_PROCESSING)->setParameters(array('tolerance' => 10));
     $orchestrationId = $this->createOrchestrationTest($token1, array($notification1, $notification2, $notification3));
     // first job fake processing
     $jobId = $this->enqueueJobTest($orchestrationId);
     $syrupJob = $this->syrupJobMapper->get($jobId);
     $this->assertInstanceOf(get_class(new Metadata\Job($this->objectEncryptor)), $syrupJob);
     $syrupJob->setStartTime((new \DateTime())->format('c'));
     $syrupJob->setStatus(Metadata\Job::STATUS_PROCESSING);
     $syrupJob->setResult(array('tasks' => array()));
     $this->syrupJobMapper->update($syrupJob);
     sleep(3);
     $syrupJob->setEndTime((new \DateTime())->format('c'));
     $syrupJob->setStatus(Metadata\Job::STATUS_SUCCESS);
     $syrupJob->setResult(array('tasks' => array()));
     $this->syrupJobMapper->update($syrupJob);
     // second job fake processing
     $jobId = $this->enqueueJobTest($orchestrationId);
     $syrupJob = $this->syrupJobMapper->get($jobId);
     $this->assertInstanceOf(get_class(new Metadata\Job($this->objectEncryptor)), $syrupJob);
     $syrupJob->setStartTime((new \DateTime())->format('c'));
     $syrupJob->setStatus(Metadata\Job::STATUS_PROCESSING);
     $syrupJob->setResult(array('tasks' => array()));
     $this->syrupJobMapper->update($syrupJob);
     sleep(120);
     // check jobs
     $this->runWatchdogCommandTest();
     // second job finishing
     $syrupJob->setEndTime((new \DateTime())->format('c'));
     $syrupJob->setStatus(Metadata\Job::STATUS_SUCCESS);
     $syrupJob->setResult(array('tasks' => array()));
     $this->syrupJobMapper->update($syrupJob);
     $esJob = new Job();
     $esJob->build($syrupJob);
     // check sended notification - manual run - only one must be sended
     $events = StorageApi\EventLoader::longProcessingEvents($esJob, $notification2, $this->storageApi);
     $this->assertCount(1, $events);
     $events = StorageApi\EventLoader::longProcessingEvents($esJob, $notification3, $this->storageApi);
     $this->assertCount(0, $events);
 }
Example #12
0
 public function getJobStatus($url, Elasticsearch\Job $job, Encryptor $encryptor)
 {
     $timeout = 2;
     return $this->get($url, array('config' => array('curl' => array(CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_0)), 'headers' => array('X-StorageApi-Token' => $encryptor->decrypt($job->getToken()), 'X-KBC-RunId' => $job->getRunId(), 'X-User-Agent', KeboolaOrchestratorBundle::SYRUP_COMPONENT_NAME . " - JobExecutor"), 'timeout' => 60 * $timeout));
     // handling curl errors
     //        $request->getEmitter()->on('error', function (ErrorEvent $e) use ($timeout) {
     //            $curlError = $e->getTransferInfo('curl_result');
     //            if ($curlError == 28) {
     //                throw new Exception\JobRuntimeException(sprintf('Task polling timeout after %d minutes', $timeout));
     //            }
     //        });
 }
 /**
  * @param Job $job
  * @param StorageApi\UniqueManager $uniqueGenerator
  * @return Elasticsearch\Job
  */
 private function buildEsJob(Job $job, StorageApi\UniqueManager $uniqueGenerator)
 {
     //@FIXME replace with jobfactory https://github.com/keboola/syrup/wiki/Upgrade
     $jobId = $uniqueGenerator->generateId();
     $runId = $uniqueGenerator->generateRunId($uniqueGenerator->getRunId());
     $job->setId($jobId);
     $job->setRunId($runId);
     return new Elasticsearch\Job($this->objectEncryptor, ['id' => $jobId, 'lockName' => KeboolaOrchestratorBundle::SYRUP_COMPONENT_NAME . '-' . $job->getProjectId() . '-' . $job->getOrchestrationId(), 'runId' => $runId, 'nestingLevel' => 0, 'project' => ['id' => $job->getProjectId(), 'name' => $job->getTokenOwnerName()], 'token' => ['id' => $job->getTokenId(), 'description' => $job->getTokenDesc(), 'token' => $this->encryptToken($job->getToken())], 'component' => KeboolaOrchestratorBundle::SYRUP_COMPONENT_NAME, 'command' => 'run', 'params' => array('config' => $job->getConfig(), 'orchestration' => array('id' => $job->getOrchestrationId(), 'name' => $job->getOrchestrationName()), 'initializedBy' => $job->getInitializedBy(), 'initiator' => array('id' => $job->getInitiatorTokenId(), 'description' => $job->getInitiatorTokenDesc(), 'userAgent' => $job->getInitiatorUserAgent()), 'notificationsEmails' => $job->getNotificationsEmails(), 'tasks' => $job->getTasks()), 'process' => ['host' => gethostname(), 'pid' => posix_getpid()], 'createdTime' => $job->getCreatedTime()->format('c')]);
 }
 /**
  * @param Job $job
  * @return Phase[]
  */
 private function createPhases(Job $job)
 {
     /** @var StorageApi\OrchestrationTask[] $orchestrationTasks */
     $orchestrationTasks = array();
     if ($job->getTasks()) {
         $orchestrationTasks = array_map(function ($line) {
             $task = new StorageApi\OrchestrationTask();
             $task->fromArray(json_decode(json_encode($line), true));
             return $task;
         }, $job->getTasks());
     }
     /**
      * @var Phase[] $phases
      */
     $phases = array();
     $prevPhase = false;
     foreach ($orchestrationTasks as $orchestrationTask) {
         if ($orchestrationTask->getPhase() !== $prevPhase || $orchestrationTask->getPhase() == null) {
             $phase = new Phase($job);
             $phases[count($phases)] = $phase;
             $this->jobResult->addPhase($phase);
         }
         $taskResult = new TaskResult($orchestrationTask);
         $this->jobResult->addTaskResult($taskResult);
         $phases[count($phases) - 1]->addTaskResult($taskResult);
         $prevPhase = $orchestrationTask->getPhase();
     }
     return $phases;
 }