Source of file DeploymentPipelineStep.php
Size: 6,510 Bytes - Last Modified: 2021-12-23T10:29:15+00:00
/var/www/docs.ssmods.com/process/src/code/model/steps/DeploymentPipelineStep.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 | <?php /** * Class DeploymentPipelineTest * This class performs the actual deployment after the smoke test has passed * * Configure using the below code in your deploy.yml * <code> * Steps: * FinalDeployment: * Class: DeploymentPipelineStep * MaxDuration: 3600 # optionally timeout after 1 hour * </code> * * {@see DNRoot::doDeploy()} for non-pipeline equivalent * * @package deploynaut * @subpackage pipeline */ class DeploymentPipelineStep extends LongRunningPipelineStep { private static $db = array( 'Doing' => "Enum('Deployment,Snapshot,Queued', 'Queued')" ); public function getTitle() { // Make sure the title includes the subtask return parent::getTitle() . ":{$this->Doing}"; } public function start() { parent::start(); switch($this->Status) { case 'Started': // If we are doing a subtask, check which one to continue switch($this->Doing) { case 'Deployment': return $this->continueDeploy(); case 'Snapshot': return $this->continueSnapshot(); default: $this->log("Unable to process {$this->Title} with subtask of {$this->Doing}"); $this->markFailed(); return false; } case 'Queued': if ($this->Pipeline()->SkipSnapshot) { return $this->startDeploy(); } else { return $this->createSnapshot(); } default: $this->log("Unable to process {$this->Title} with status of {$this->Status}"); $this->markFailed(); return false; } } /** * 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; } /** * Create a snapshot of the db and store the ID on the Pipline * @return bool True if success */ protected function createSnapshot() { // Mark self as creating a snapshot $this->Status = 'Started'; $this->Doing = 'Snapshot'; $this->log("{$this->Title} creating snapshot of database"); $this->write(); // Skip deployment for dry run if($this->Pipeline()->DryRun) { $this->log("[Skipped] Create DNDataTransfer backup"); return true; } // Skip snapshot for environments with no build if(!$this->Pipeline()->Environment()->CurrentBuild()) { $this->log('[Skipped] No current build, skipping snapshot'); return true; } // create a snapshot $pipeline = $this->Pipeline(); $job = DNDataTransfer::create(); $job->EnvironmentID = $pipeline->EnvironmentID; $job->Direction = 'get'; $job->Mode = 'db'; $job->DataArchiveID = null; $job->AuthorID = $pipeline->AuthorID; $job->write(); $job->start(); $pipeline->PreviousSnapshotID = $job->ID; $pipeline->write(); return true; } /** * Check status of current snapshot */ protected function continueSnapshot() { $this->log("Checking status of {$this->Title}..."); // Skip snapshot for dry run if($this->Pipeline()->DryRun) { $this->log("[Skipped] Checking progress of snapshot backup"); return $this->startDeploy(); } // Skip snapshot for environments with no build if(!$this->Pipeline()->Environment()->CurrentBuild()) { return $this->startDeploy(); } // Get related snapshot $snapshot = $this->Pipeline()->PreviousSnapshot(); if(empty($snapshot) || !$snapshot->exists()) { $this->log("Missing snapshot for in-progress {$this->Title}"); $this->markFailed(); return false; } // Check finished state $status = $snapshot->ResqueStatus(); if($this->checkResqueStatus($status)) { // After snapshot is done, switch to doing deployment return $this->startDeploy(); } } /** * Check status of deployment and finish task if complete, or fail if timedout * * @return boolean */ protected function continueDeploy() { $this->log("Checking status of {$this->Title}..."); // Skip deployment for dry run if($this->Pipeline()->DryRun) { $this->log("[Skipped] Checking progress of deployment"); $this->finish(); return !$this->isFailed(); } // Get related deployment $deployment = $this->Pipeline()->CurrentDeployment(); if(empty($deployment) || !$deployment->exists()) { $this->log("Missing deployment for in-progress {$this->Title}"); $this->markFailed(); return false; } // Check finished state $status = $deployment->ResqueStatus(); if($this->checkResqueStatus($status)) { $this->finish(); } return !$this->isFailed(); } /** * Check the status of a resque sub-task * * @param string $status Resque task status * @return boolean True if the task is finished successfully */ protected function checkResqueStatus($status) { switch($status) { case "Complete": return true; case "Failed": case "Invalid": $this->log("{$this->Title} failed with task status $status"); $this->markFailed(); return false; case "Queued": case "Running": default: // For running or queued tasks ensure that we have not exceeded // a reasonable time-elapsed to consider this job inactive if($this->isTimedOut()) { $this->log("{$this->Title} took longer than {$this->MaxDuration} seconds to run and has timed out"); $this->markFailed(); return false; } else { // While still running report no error, waiting for resque job to eventually finish // some time in the future $this->log("{$this->Title} is still in progress"); return false; } } } } |