protected function canSendEmail(\DNDeployment $deployment) { $deployer = $deployment->Deployer(); $approver = $deployment->Approver(); if (!$deployer || !$deployer->exists()) { return false; } if (!$approver || !$approver->exists()) { return false; } return true; }
/** * Begin a new deployment * * @return boolean */ protected function startRevertDeploy() { $this->Status = 'Started'; $this->Doing = 'Deployment'; $this->log("{$this->Title} starting revert deployment"); // Skip deployment for dry run if ($this->Pipeline()->DryRun) { $this->log("[Skipped] Create DNDeployment"); $this->write(); return true; } // Get old deployment from pipeline $pipeline = $this->Pipeline(); $previous = $pipeline->PreviousDeployment(); if (empty($previous) || empty($previous->SHA)) { $this->log("No available SHA for {$this->Title}"); $this->markFailed(); return false; } // Initialise deployment $deployment = DNDeployment::create(); // Leave the maintenance page up if we are restoring the DB $deployment->LeaveMaintenacePage = $this->doRestoreDB(); $deployment->EnvironmentID = $pipeline->EnvironmentID; $deployment->SHA = $previous->SHA; $deployment->DeployerID = $pipeline->AuthorID; $deployment->write(); $deployment->start(); $this->RollbackDeploymentID = $deployment->ID; $this->write(); return true; }
/** * @param string $status * @global array $databaseConfig */ protected function updateStatus($status) { global $databaseConfig; DB::connect($databaseConfig); $dnDeployment = DNDeployment::get()->byID($this->args['deploymentID']); $dnDeployment->Status = $status; $dnDeployment->write(); }
/** * Do the actual job by calling the appropiate backend */ public function perform() { echo "[-] PingJob starting" . PHP_EOL; $log = new DeploynautLogFile($this->args['logfile']); $ping = DNDeployment::get()->byID($this->args['pingID']); $environment = $ping->Environment(); $project = $environment->Project(); $environment->Backend()->ping($environment, $log, $project); }
/** * Makes the dummy deployment step * * @return Pipeline */ public function getDummyPipeline($restoreDB = true) { // Get default backups $previous = DNDeployment::create(); $previous->write(); $current = DNDeployment::create(); $current->write(); $snapshot = DNDataTransfer::create(); $snapshot->write(); // Setup default pipeline $pipeline = $this->objFromFixture('Pipeline', 'testpipesmoketest'); $pipeline->Config = serialize(array('RollbackStep1' => array('Class' => 'RollbackStep', 'RestoreDB' => $restoreDB, 'MaxDuration' => '3600'))); $pipeline->PreviousDeploymentID = $previous->ID; $pipeline->CurrentDeploymentID = $current->ID; $pipeline->PreviousSnapshotID = $snapshot->ID; $pipeline->write(); return $pipeline; }
/** * Begin a new deployment * * @return boolean */ protected function startDeploy() { $this->Status = 'Started'; $this->Doing = 'Deployment'; $this->log("{$this->Title} starting deployment"); // Check environment and SHA $pipeline = $this->Pipeline(); $environment = $pipeline->Environment(); if (empty($environment) || !$environment->exists()) { $this->log("No available environment for {$this->Title}"); $this->markFailed(); return false; } if (empty($pipeline->SHA)) { $this->log("No available SHA for {$this->Title}"); $this->markFailed(); return false; } // Skip deployment for dry run if ($this->Pipeline()->DryRun) { $this->log("[Skipped] Create DNDeployment for SHA " . $pipeline->SHA); $this->write(); return true; } // Initialise deployment $deployment = DNDeployment::create(); $deployment->EnvironmentID = $environment->ID; $deployment->SHA = $pipeline->SHA; $previousStep = $pipeline->findPreviousStep(); $deployment->DeployerID = $previousStep && $previousStep->ResponderID ? $previousStep->ResponderID : $pipeline->AuthorID; $deployment->write(); $deployment->start(); $pipeline->CurrentDeploymentID = $deployment->ID; $pipeline->write(); $this->write(); return true; }
public function DeployHistory() { return DNDeployment::get()->filter('EnvironmentID', $this->ID)->sort('LastEdited DESC'); }
/** * Get the current deployed build for this environment * * Dear people of the future: If you are looking to optimize this, simply create a CurrentBuildSHA(), which can be * a lot faster. I presume you came here because of the Project display template, which only needs a SHA. * * @return false|DNDeployment */ public function CurrentBuild() { // The DeployHistory function is far too slow to use for this /** @var DNDeployment $deploy */ $deploy = DNDeployment::get()->filter(array('EnvironmentID' => $this->ID, 'Status' => 'Finished'))->sort('LastEdited DESC')->first(); if (!$deploy || !$deploy->SHA) { return false; } $repo = $this->Project()->getRepository(); if (!$repo) { return $deploy; } try { $commit = $repo->getCommit($deploy->SHA); if ($commit) { $deploy->Message = Convert::raw2xml($commit->getMessage()); $deploy->Committer = Convert::raw2xml($commit->getCommitterName()); $deploy->CommitDate = $commit->getCommitterDate()->Format('d/m/Y g:ia'); $deploy->Author = Convert::raw2xml($commit->getAuthorName()); $deploy->AuthorDate = $commit->getAuthorDate()->Format('d/m/Y g:ia'); } // We can't find this SHA, so we ignore adding a commit message to the deployment } catch (Exception $ex) { } return $deploy; }
/** * Provide rollback-able pipeline on the verge of failing. */ public function getFailingPipeline() { // Get default backups $previous = DNDeployment::create(); $previous->SHA = '9f0a012e97715b1871n41gk30f34268u12a0029q'; $previous->write(); $current = DNDeployment::create(); $current->write(); $snapshot = DNDataTransfer::create(); $snapshot->write(); $pipeline = $this->objFromFixture('Pipeline', 'FailingPipe'); $pipeline->Config = serialize(array('RollbackStep1' => array('Class' => 'RollbackStep', 'RestoreDB' => false, 'MaxDuration' => '3600'), 'RollbackStep2' => array('Class' => 'SmokeTestPipelineStep', 'MaxDuration' => '3600'))); $pipeline->PreviousDeploymentID = $previous->ID; $pipeline->CurrentDeploymentID = $current->ID; $pipeline->PreviousSnapshotID = $snapshot->ID; $pipeline->write(); return $pipeline; }
/** * Fetchs all deployments in progress. Limits to 1 hour to prevent deployments * if an old deployment is stuck. * * @return DataList */ public function runningDeployments() { return DNDeployment::get()->filter(['EnvironmentID' => $this->ID, 'Status' => ['Queued', 'Started'], 'Created:GreaterThan' => strtotime('-1 hour')]); }
/** * @param int $deploymentID * @return \DNDeployment */ public function updateDeployment($deploymentID) { $deployment = \DNDeployment::get()->byId($deploymentID); $deployment->EnvironmentID = $this->environment->ID; $deployment->SHA = $this->getOption('sha'); $deployment->RefType = $this->getOption('ref_type'); $deployment->RefName = $this->getOption('ref_name'); $deployment->Summary = $this->getOption('summary'); $deployment->Title = $this->getOption('title'); $deployment->Strategy = $this->toJSON(); $deployment->DeployerID = \Member::currentUserID(); $deployment->write(); // re-get and return the deployment so we have the correct state return \DNDeployment::get()->byId($deployment->ID); }
/** * * @param int $id * @return SS_HTTPResponse */ protected function getDeploy($id) { $deploy = DNDeployment::get()->byID($id); if (!$deploy) { return $this->message('Deploy not found', 404); } $output = array('status' => $deploy->ResqueStatus(), 'message' => $deploy->LogContent()); return $this->getAPIResponse($output); }
/** * Get the current deployed build for this environment * * Dear people of the future: If you are looking to optimize this, simply create a CurrentBuildSHA(), which can be a lot faster. * I presume you came here because of the Project display template, which only needs a SHA. * * @return string */ public function CurrentBuild() { // The DeployHistory function is far too slow to use for this $deploy = DNDeployment::get()->filter(array('EnvironmentID' => $this->ID, 'Status' => 'Finished'))->sort('LastEdited DESC')->first(); if (!$deploy || !$deploy->SHA) { return false; } $repo = $this->Project()->getRepository(); if (!$repo) { return $deploy; } try { $commit = $repo->getCommit($deploy->SHA); if ($commit) { $deploy->Message = Convert::raw2xml($commit->getMessage()); } // We can't find this SHA, so we ignore adding a commit message to the deployment } catch (Gitonomy\Git\Exception\ReferenceNotFoundException $ex) { } return $deploy; }
public function getSHA() { $sha = parent::getField('SHA'); return $sha ?: '9ae502821345ab39b04d46ce6bb822ccdd7f7414'; }
/** * Check if a DNDeployment exists and do permission checks on it. If there is something wrong it will return * an APIResponse with the error, otherwise null. * * @param \DNDeployment $deployment * * @return null|SS_HTTPResponse */ protected function validateDeployment($deployment) { if (!$deployment || !$deployment->exists()) { return $this->getAPIResponse(['message' => 'This deployment does not exist'], 404); } if ($deployment->EnvironmentID != $this->environment->ID) { return $this->getAPIResponse(['message' => 'This deployment does not belong to the environment'], 403); } if (!$deployment->canView()) { return $this->getAPIResponse(['message' => 'You are not authorised to view this deployment'], 403); } return null; }
/** * A history of all builds deployed to this environment * * @return ArrayList */ public function DeployHistory() { $history = DNDeployment::get()->filter('EnvironmentID', $this->ID)->sort('LastEdited DESC'); $repo = $this->Project()->getRepository(); if (!$repo) { return $history; } $ammendedHistory = new ArrayList(); foreach ($history as $deploy) { if (!$deploy->SHA) { continue; } try { $commit = $repo->getCommit($deploy->SHA); if ($commit) { $deploy->Message = Convert::raw2xml($commit->getMessage()); } // We can't find this SHA, so we ignore adding a commit message to the deployment } catch (Gitonomy\Git\Exception\ReferenceNotFoundException $ex) { } $ammendedHistory->push($deploy); } return $ammendedHistory; }
/** * @return DNDeployment */ public function createDeployment() { $deployment = DNDeployment::create(); $deployment->EnvironmentID = $this->environment->ID; // Pull out the SHA from the options so we can make it queryable. $deployment->SHA = $this->getOption('sha'); $deployment->Strategy = $this->toJSON(); $deployment->write(); return $deployment; }
/** * @param string $status Transition * @global array $databaseConfig */ protected function updateStatus($status) { global $databaseConfig; DB::connect($databaseConfig); $deployment = DNDeployment::get()->byID($this->args['deploymentID']); $deployment->getMachine()->apply($status); }
/** * Return data about a single deployment for use in API response. * @param \DNDeployment $deployment * @return array */ public function getDeploymentData(\DNDeployment $deployment) { if (empty(self::$_cache_current_build[$deployment->EnvironmentID])) { self::$_cache_current_build[$deployment->EnvironmentID] = $deployment->Environment()->CurrentBuild(); } $environment = $deployment->Environment(); $project = $environment->Project(); $deployerData = $this->getStackMemberData($project, $deployment->DeployerID); $approverData = $this->getStackMemberData($project, $deployment->ApproverID); $started = null; $startedNice = null; $startedAgo = null; // we check first, before we do a expensive ->Nice() and ->Ago() if (!$deployment->DeployStarted) { $started = $deployment->Created; $startedNice = $deployment->obj('Created')->Nice(); $startedAgo = $deployment->obj('Created')->Ago(); } else { $started = $deployment->DeployStarted; $startedNice = $deployment->obj('DeployStarted')->Nice(); $startedAgo = $deployment->obj('DeployStarted')->Ago(); } $isCurrentBuild = self::$_cache_current_build[$deployment->EnvironmentID] ? $deployment->ID === self::$_cache_current_build[$deployment->EnvironmentID]->ID : false; $supportedOptions = $environment->getSupportedOptions(); $setOptions = $deployment->getDeploymentStrategy() ? $deployment->getDeploymentStrategy()->getOptions() : []; $options = []; foreach ($supportedOptions as $option) { if (!isset($setOptions[$option->getName()])) { continue; } if ($setOptions[$option->getName()] === 'true' || $setOptions[$option->getName()] === true) { $options[$option->getName()] = true; } } $tags = []; try { $tags = $deployment->getTags()->toArray(); } catch (\Exception $e) { // gitonomy exception } $type = 'full'; if ($deployment->getDeploymentStrategy()->getActionCode() === 'fast') { $type = 'code-only'; } return ['id' => $deployment->ID, 'date_created' => $deployment->Created, 'date_created_nice' => $deployment->obj('Created')->Nice(), 'date_created_ago' => $deployment->obj('Created')->Ago(), 'date_started' => $started, 'date_started_nice' => $startedNice, 'date_started_ago' => $startedAgo, 'date_requested' => $deployment->DeployRequested, 'date_requested_nice' => $deployment->obj('DeployRequested')->Nice(), 'date_requested_ago' => $deployment->obj('DeployRequested')->Ago(), 'date_updated' => $deployment->LastEdited, 'date_updated_nice' => $deployment->obj('LastEdited')->Nice(), 'date_updated_ago' => $deployment->obj('LastEdited')->Ago(), 'title' => $deployment->Title, 'summary' => $deployment->Summary, 'ref_type' => $deployment->RefType, 'ref_name' => $deployment->RefName, 'rejected_reason' => $deployment->RejectedReason ?: '', 'tags' => $tags, 'changes' => $deployment->getDeploymentStrategy()->getChanges(), 'deployment_type' => $type, 'deployment_estimate' => $deployment->getDeploymentStrategy()->getEstimatedTime(), 'sha' => $deployment->SHA, 'short_sha' => substr($deployment->SHA, 0, 7), 'options' => $options, 'commit_subject' => $deployment->getCommitSubjectMessage(), 'commit_message' => $deployment->getCommitMessage(), 'commit_url' => $deployment->getCommitURL(), 'deployer' => $deployerData, 'approver_id' => $deployment->ApproverID ?: '', 'approver' => $approverData, 'state' => $deployment->State, 'is_current_build' => $isCurrentBuild, 'dirty' => false]; }
/** * * @param string $environmentName * @return boolean True if this release has ever been deployed to the given environment */ public function EverDeployedTo($environmentName) { $environments = $this->project->Environments()->filter('Name', $environmentName); if (!$environments->count()) { return false; } $environment = $environments->first(); $deployments = DNDeployment::get()->filter('Status', 'Finished')->filter('EnvironmentID', $environment->ID); if ($deployments->count()) { return true; } return false; }
public function run($request) { $args = $request->getVar('args'); $dryRun = $args && in_array('--dry-run', $args); $log = function ($message) { $message = sprintf('[%s] ', date('Y-m-d H:i:s')) . $message; echo $message . PHP_EOL; }; if (!Director::is_cli()) { $log('This task must be run under the command line'); return; } if ($dryRun) { $log('Running in dry-run mode. No data will be deleted'); } $count = 0; foreach (DNEnvironment::get() as $environment) { $project = $environment->Project(); if (!$project || !$project->exists()) { $log(sprintf('Environment (ID %s, Name: %s, Created: %s) is linked to a non-existent project. Deleting', $environment->ID, $environment->Name, $environment->Created)); if (!$dryRun) { $environment->delete(); $environment->destroy(); } $count++; } } foreach (DNDeployment::get() as $deployment) { $environment = $deployment->Environment(); if (!$environment || !$environment->exists()) { $log(sprintf('Deployment (ID %s, Created: %s) is linked to a non-existent environment. Deleting', $deployment->ID, $deployment->Created)); if (!$dryRun) { $deployment->delete(); $deployment->destroy(); } $count++; } } foreach (DNDataTransfer::get() as $transfer) { $environment = $transfer->Environment(); if (!$environment || !$environment->exists()) { $log(sprintf('Data transfer (ID %s, Created: %s) is linked to a non-existent environment. Deleting', $transfer->ID, $transfer->Created)); if (!$dryRun) { $transfer->delete(); $transfer->destroy(); } $count++; } } foreach (DNDataArchive::get() as $archive) { $environment = $archive->Environment(); if (!$environment || !$environment->exists()) { $log(sprintf('Archive (ID %s, Created: %s) is linked to a non-existent environment. Deleting', $archive->ID, $archive->Created)); if (!$dryRun) { $archive->delete(); $archive->destroy(); } $count++; } } foreach (DNGitFetch::get() as $fetch) { $project = $fetch->Project(); if (!$project || !$project->exists()) { $log(sprintf('Git fetch (ID %s, Created: %s) is linked to a non-existent project. Deleting', $fetch->ID, $fetch->Created)); if (!$dryRun) { $fetch->delete(); $fetch->destroy(); } $count++; } } foreach (DNPing::get() as $ping) { $environment = $ping->Environment(); if (!$environment || !$environment->exists()) { $log(sprintf('Ping (ID %s, Created: %s) is linked to a non-existent environment. Deleting', $ping->ID, $ping->Created)); if (!$dryRun) { $ping->delete(); $ping->destroy(); } $count++; } } $log(sprintf('Finished. Processed %s records', $count)); }
/** * Fetchs all deployments in progress. Limits to 1 hour to prevent deployments * if an old deployment is stuck. * * @return DataList */ public function runningDeployments() { return DNDeployment::get()->filter(['EnvironmentID' => $this->ID, 'State' => [DNDeployment::STATE_QUEUED, DNDeployment::STATE_DEPLOYING, DNDeployment::STATE_ABORTING], 'Created:GreaterThan' => strtotime('-1 hour')]); }
/** * @deprecated 2.0.0 - moved to DeployDispatcher * * @param \SS_HTTPRequest $request * * @return string * @throws SS_HTTPResponse_Exception */ public function abortDeploy(\SS_HTTPRequest $request) { $params = $request->params(); $deployment = DNDeployment::get()->byId($params['Identifier']); if (!$deployment || !$deployment->ID) { throw new SS_HTTPResponse_Exception('Deployment not found', 404); } if (!$deployment->canView()) { return Security::permissionFailure(); } // For now restrict to ADMINs only. if (!Permission::check('ADMIN')) { return Security::permissionFailure(); } $environment = $deployment->Environment(); $project = $environment->Project(); if ($environment->Name != $params['Environment']) { throw new LogicException("Environment in URL doesn't match this deploy"); } if ($project->Name != $params['Project']) { throw new LogicException("Project in URL doesn't match this deploy"); } if (!in_array($deployment->Status, ['Queued', 'Deploying', 'Aborting'])) { throw new LogicException(sprintf("Cannot abort from %s state.", $deployment->Status)); } $deployment->getMachine()->apply(DNDeployment::TR_ABORT); return $this->sendResponse($deployment->ResqueStatus(), []); }
/** * Action - Get the latest deploy log * * @param SS_HTTPRequest $request * * @return string * @throws SS_HTTPResponse_Exception */ public function deploylog(SS_HTTPRequest $request) { $params = $request->params(); $deployment = DNDeployment::get()->byId($params['Identifier']); if (!$deployment || !$deployment->ID) { throw new SS_HTTPResponse_Exception('Deployment not found', 404); } if (!$deployment->canView()) { return Security::permissionFailure(); } $environment = $deployment->Environment(); $project = $environment->Project(); if ($environment->Name != $params['Environment']) { throw new LogicException("Environment in URL doesn't match this deploy"); } if ($project->Name != $params['Project']) { throw new LogicException("Project in URL doesn't match this deploy"); } $log = $deployment->log(); if ($log->exists()) { $content = $log->content(); } else { $content = 'Waiting for action to start'; } return $this->sendResponse($deployment->ResqueStatus(), $content); }
/** * Action - Get the latest deploy log * * @return string */ public function deploylog(SS_HTTPRequest $request) { $params = $request->params(); $deployment = DNDeployment::get()->byId($params['Identifier']); if (!$deployment || !$deployment->ID) { throw new SS_HTTPResponse_Exception('Deployment not found', 404); } if (!$deployment->canView()) { return Security::permissionFailure(); } $environment = $deployment->Environment(); $project = $environment->Project(); if ($environment->Name != $params['Environment']) { throw new LogicException("Environment in URL doesn't match this deploy"); } if ($project->Name != $params['Project']) { throw new LogicException("Project in URL doesn't match this deploy"); } $log = $deployment->log(); if ($log->exists()) { $content = $log->content(); } else { $content = 'Waiting for action to start'; } $sendJSON = strpos($request->getHeader('Accept'), 'application/json') !== false || $request->getExtension() == 'json'; $content = preg_replace('/(?:(?:\\r\\n|\\r|\\n)\\s*){2}/s', "\n", $content); if ($sendJSON) { $this->response->addHeader("Content-type", "application/json"); return json_encode(array('status' => $deployment->ResqueStatus(), 'content' => $content)); } else { $this->response->addHeader("Content-type", "text/plain"); return $content; } }