Completed
Pull Request — master (#575)
by Mateusz
03:07
created

DeployJob   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 155
Duplicated Lines 8.39 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 7
Bugs 1 Features 0
Metric Value
wmc 19
c 7
b 1
f 0
lcom 1
cbo 7
dl 13
loc 155
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A sig_file_for_data_object() 0 12 2
A set_signal() 0 5 1
C alarmHandler() 0 39 7
A setUp() 0 17 4
B perform() 13 41 3
A updateStatus() 0 7 1
A DNData() 0 3 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/**
4
 * Runs a deployment via the most appropriate backend
5
 */
6
class DeployJob extends DeploynautJob {
7
8
	/**
9
	 * @var array
10
	 */
11
	public $args;
12
13
	public static function sig_file_for_data_object($dataObject) {
14
		$dir = DNData::inst()->getSignalDir();
15
		if (!is_dir($dir)) {
16
			`mkdir $dir`;
17
		}
18
		return sprintf(
19
			'%s/deploynaut-signal-%s-%s',
20
			DNData::inst()->getSignalDir(),
21
			$dataObject->ClassName,
22
			$dataObject->ID
23
		);
24
	}
25
26
	/**
27
	 * Signal the worker to self-abort. If we had a reliable way of figuring out the right PID,
28
	 * we could posix_kill directly, but Resque seems to not provide a way to find out the PID
29
	 * from the job nor worker.
30
	 */
31
	public static function set_signal($dataObject, $signal) {
32
		$sigFile = DeployJob::sig_file_for_data_object($dataObject);
33
		// 2 is SIGINT - we can't use SIGINT constant in the Apache context, only available in workers.
34
		file_put_contents($sigFile, $signal);
35
	}
36
37
	/**
38
	 * Poll the sigFile looking for a signal to self-deliver.
39
	 * This is useful if we don't know the PID of the worker - we can easily deliver signals
40
	 * if we only know the ClassName and ID of the DataObject.
41
	 */
42
	public function alarmHandler() {
43
		$sigFile = $this->args['sigFile'];
44
		if (file_exists($sigFile) && is_readable($sigFile) && is_writable($sigFile)) {
45
			$signal = (int)file_get_contents($sigFile);
46
			if (is_int($signal) && in_array((int)$signal, [
47
				// The following signals are trapped by both Resque and Rainforest.
48
				SIGTERM,
49
				SIGINT,
50
				SIGQUIT,
51
				// The following are Resque only.
52
				SIGUSR1,
53
				SIGUSR2,
54
				SIGCONT
55
			])) {
56
				echo sprintf(
57
					'[-] Signal "%s" received, delivering to own process group, PID "%s".' . PHP_EOL,
58
					$signal,
59
					getmypid()
60
				);
61
62
				// Mark the signal as received.
63
				unlink($sigFile);
64
65
				// Dispatch to own process group.
66
				$pgid = posix_getpgid(getmypid());
67
				if ($pgid<=0) {
68
					echo sprintf(
69
						'[-] Unable to send signal to invalid PGID "%s".' . PHP_EOL,
70
						$pgid
71
					);
72
				} else {
73
					posix_kill(-$pgid, $signal);
74
				}
75
			}
76
		}
77
78
		// Wake up again soon.
79
		pcntl_alarm(1);
80
	}
81
82
	public function setUp() {
83
		// Make this process a session leader so we can send signals
84
		// to this job as a whole (including any subprocesses such as spawned by Symfony).
85
		posix_setsid();
86
87
		if(function_exists('pcntl_alarm') && function_exists('pcntl_signal')) {
88
			if (!empty($this->args['sigFile'])) {
89
				echo sprintf('[-] Signal file requested, polling "%s".' . PHP_EOL, $this->args['sigFile']);
90
				declare(ticks = 1);
91
				pcntl_signal(SIGALRM, [$this, 'alarmHandler']);
92
				pcntl_alarm(1);
93
			}
94
		}
95
96
		$this->updateStatus('Started');
97
		chdir(BASE_PATH);
98
	}
99
100
	public function perform() {
101
		echo "[-] DeployJob starting" . PHP_EOL;
102
		$log = new DeploynautLogFile($this->args['logfile']);
103
104
		$deployment = DNDeployment::get()->byID($this->args['deploymentID']);
105
		$environment = $deployment->Environment();
106
		$project = $environment->Project();
107
		// This is a bit icky, but there is no easy way of capturing a failed deploy by using the PHP Resque
108
		try {
109
			// Disallow concurrent deployments (don't rely on queuing implementation to restrict this)
110
			// Only consider deployments started in the last 30 minutes (older jobs probably got stuck)
111
			$runningDeployments = $environment->runningDeployments()->exclude('ID', $this->args['deploymentID']);
112 View Code Duplication
			if($runningDeployments->count()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
113
				$runningDeployment = $runningDeployments->first();
114
				$log->write(sprintf(
115
					'[-] Error: another deployment is in progress (started at %s by %s)',
116
					$runningDeployment->dbObject('Created')->Nice(),
117
					$runningDeployment->Deployer()->Title
118
				));
119
				throw new RuntimeException(sprintf(
120
					'Another deployment is in progress (started at %s by %s)',
121
					$runningDeployment->dbObject('Created')->Nice(),
122
					$runningDeployment->Deployer()->Title
123
				));
124
			}
125
126
			$environment->Backend()->deploy(
127
				$environment,
128
				$log,
129
				$project,
130
				// Pass all args to give the backend full visibility. These args also contain
131
				// all options from the DeploymentStrategy merged in, including sha.
132
				$this->args
133
			);
134
		} catch(Exception $e) {
135
			echo "[-] DeployJob failed" . PHP_EOL;
136
			throw $e;
137
		}
138
		$this->updateStatus('Finished');
139
		echo "[-] DeployJob finished" . PHP_EOL;
140
	}
141
142
	/**
143
	 * @param string $status
144
	 * @global array $databaseConfig
145
	 */
146
	protected function updateStatus($status) {
147
		global $databaseConfig;
148
		DB::connect($databaseConfig);
149
		$dnDeployment = DNDeployment::get()->byID($this->args['deploymentID']);
150
		$dnDeployment->Status = $status;
151
		$dnDeployment->write();
152
	}
153
154
	/**
155
	 * @return DNData
156
	 */
157
	protected function DNData() {
158
		return DNData::inst();
159
	}
160
}
161