Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
78.57% |
11 / 14 |
CRAP | |
79.17% |
76 / 96 |
Docker | |
0.00% |
0 / 1 |
|
78.57% |
11 / 14 |
46.08 | |
79.17% |
76 / 96 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
setDockerInstance | |
0.00% |
0 / 1 |
7.04 | |
90.91% |
10 / 11 |
|||
run | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
removeOldService | |
0.00% |
0 / 1 |
12.82 | |
69.57% |
16 / 23 |
|||
create | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
createServiceConfig | |
100.00% |
1 / 1 |
1 | |
100.00% |
6 / 6 |
|||
createNetworkList | |
100.00% |
1 / 1 |
3 | |
100.00% |
5 / 5 |
|||
createTask | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
createContainerSpec | |
100.00% |
1 / 1 |
1 | |
100.00% |
8 / 8 |
|||
createMountPoint | |
100.00% |
1 / 1 |
2 | |
100.00% |
9 / 9 |
|||
createDns | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
createRestartPolicy | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
createReplicationMode | |
100.00% |
1 / 1 |
1 | |
100.00% |
5 / 5 |
|||
getRunningContainerByName | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 12 |
1 | <?php |
2 | |
3 | namespace Qmp\Laravel\DockerLauncher; |
4 | |
5 | use Illuminate\Support\Facades\Log; |
6 | use Qmp\Laravel\DockerLauncher\Docker\DockerConfig; |
7 | use Qmp\Laravel\DockerLauncher\Exceptions\DockerException; |
8 | use Docker\API\Model\Mount; |
9 | use Docker\API\Model\ServicesCreatePostBody; |
10 | use Docker\API\Model\ServiceSpecMode; |
11 | use Docker\API\Model\ServiceSpecModeReplicated; |
12 | use Docker\API\Model\TaskSpec; |
13 | use Docker\API\Model\TaskSpecContainerSpec; |
14 | use Docker\API\Model\TaskSpecContainerSpecDNSConfig; |
15 | use Docker\API\Model\TaskSpecRestartPolicy; |
16 | use Docker\Docker as DockerClient; |
17 | |
18 | use Docker\API\Model\ServicesCreatePostResponse201; |
19 | use Psr\Http\Message\ResponseInterface; |
20 | |
21 | class Docker |
22 | { |
23 | /** |
24 | * @var DockerConfig |
25 | */ |
26 | private $config; |
27 | |
28 | /** |
29 | * @var int |
30 | */ |
31 | private $replicasNb = 1; |
32 | |
33 | /** |
34 | * @var DockerClient |
35 | */ |
36 | private $docker; |
37 | |
38 | /** |
39 | * Docker constructor. |
40 | * @param DockerConfig $config |
41 | * @param $docker |
42 | */ |
43 | public function __construct(DockerConfig $config, $docker) |
44 | { |
45 | $this->config = $config; |
46 | $this->docker = $docker; |
47 | } |
48 | |
49 | /** |
50 | * @param string $scheduleName |
51 | * @param string $command |
52 | * @param string $image |
53 | * @param string $output |
54 | * @param string $launcherCmd |
55 | * @param array $mount |
56 | */ |
57 | public function setDockerInstance(string $scheduleName, string $command, string $image = '', string $output = '', string $launcherCmd = '', array $mount = []) |
58 | { |
59 | $this->config->setScheduleName($scheduleName); |
60 | $this->config->setCommand($command); |
61 | |
62 | if (!empty($image)) { |
63 | $this->config->setDockerImage($image); |
64 | } |
65 | |
66 | if (!empty($output)) { |
67 | $this->config->setOutputFile($output); |
68 | } |
69 | |
70 | if (!empty($launcherCmd)) { |
71 | $this->config->setCommandLauncher($launcherCmd); |
72 | } |
73 | |
74 | if (!empty($mount['source']) && !empty($mount['target']) && !empty($mount['type'])) { |
75 | $this->config->setMountPoint($mount['source'], $mount['target'], $mount['type']); |
76 | } |
77 | } |
78 | |
79 | public function run() |
80 | { |
81 | $this->removeOldService(); |
82 | $this->create(); |
83 | } |
84 | |
85 | public function removeOldService($wait = false, $leftTries = 30) |
86 | { |
87 | $loop = false; |
88 | $taskList = $this->docker->taskList( |
89 | [ |
90 | 'filters' => json_encode(['service' => [$this->config->getScheduleName() => true]]) |
91 | ] |
92 | ); |
93 | |
94 | $removeOldService = $taskList !== null; |
95 | |
96 | if ($removeOldService) { |
97 | foreach ($taskList as $task) { |
98 | if ( |
99 | $task->getDesiredState() !== 'shutdown' |
100 | && $task->getStatus()->getState() !== 'complete' |
101 | && $task->getStatus()->getState() !== 'failed' |
102 | && $task->getStatus()->getMessage() !== 'finished' |
103 | ) { |
104 | |
105 | if($wait === false) { |
106 | Log::debug($task->getStatus()->getState()); |
107 | throw new DockerException('Task is not finished !', ['taskList' => $taskList]); |
108 | } else { |
109 | $loop = true; |
110 | break; |
111 | } |
112 | } |
113 | |
114 | } |
115 | if($loop) { |
116 | $left = $leftTries--; |
117 | if($left > 0) { |
118 | sleep(1); |
119 | Log::debug('Task is not finished, check again ...'); |
120 | $this->removeOldService(true, $left); |
121 | } |
122 | } else { |
123 | $this->docker->serviceDelete($this->config->getScheduleName()); |
124 | } |
125 | |
126 | } |
127 | } |
128 | |
129 | /** |
130 | * @return ServicesCreatePostResponse201|null|ResponseInterface |
131 | */ |
132 | public function create() |
133 | { |
134 | return $this->docker->serviceCreate($this->createServiceConfig()); |
135 | } |
136 | |
137 | /** |
138 | * @return ServicesCreatePostBody |
139 | */ |
140 | public function createServiceConfig() |
141 | { |
142 | $serviceConfig = new ServicesCreatePostBody(); |
143 | $serviceConfig->setName($this->config->getScheduleName()); |
144 | $serviceConfig->setNetworks($this->createNetworkList()); |
145 | $serviceConfig->setTaskTemplate($this->createTask()); |
146 | $serviceConfig->setMode($this->createReplicationMode()); |
147 | |
148 | return $serviceConfig; |
149 | } |
150 | |
151 | /** |
152 | * @return array |
153 | */ |
154 | private function createNetworkList() |
155 | { |
156 | return array_map(function ($item) { |
157 | return ['Target' => $item->getName()]; |
158 | }, array_filter($this->docker->networkList(), function ($item) { |
159 | return $item->getScope() === 'swarm' && $item->getDriver() === 'overlay' && in_array($item->getName(), $this->config->getAuthorizedNetworksList()); |
160 | })); |
161 | } |
162 | |
163 | /** |
164 | * @return TaskSpec |
165 | */ |
166 | private function createTask() |
167 | { |
168 | $taskSpec = new TaskSpec(); |
169 | $taskSpec->setContainerSpec($this->createContainerSpec()); |
170 | $taskSpec->setRestartPolicy($this->createRestartPolicy()); |
171 | |
172 | return $taskSpec; |
173 | } |
174 | |
175 | /** |
176 | * @return TaskSpecContainerSpec |
177 | */ |
178 | private function createContainerSpec() |
179 | { |
180 | $containerSpec = new TaskSpecContainerSpec(); |
181 | $containerSpec->setImage($this->config->getDockerImage()); |
182 | $containerSpec->setCommand($this->config->getBashCommand()); |
183 | $containerSpec->setArgs($this->config->getBashArgs()); |
184 | $containerSpec->setMounts($this->createMountPoint()); |
185 | $containerSpec->setDNSConfig($this->createDns()); |
186 | $containerSpec->setEnv($this->config->getEnvs()); |
187 | |
188 | return $containerSpec; |
189 | } |
190 | |
191 | /** |
192 | * @return array |
193 | */ |
194 | private function createMountPoint() |
195 | { |
196 | $mountInfos = $this->config->getMountPoints(); |
197 | $mountPoints = []; |
198 | |
199 | foreach($mountInfos as $mount) { |
200 | $mountPoint = new Mount(); |
201 | $mountPoint->setSource($mount['source']); |
202 | $mountPoint->setTarget($mount['target']); |
203 | $mountPoint->setType($mount['type']); |
204 | |
205 | $mountPoints[] = $mountPoint; |
206 | } |
207 | |
208 | return $mountPoints; |
209 | } |
210 | |
211 | /** |
212 | * @return TaskSpecContainerSpecDNSConfig |
213 | */ |
214 | private function createDns() |
215 | { |
216 | $dnsConfig = new TaskSpecContainerSpecDNSConfig(); |
217 | $dnsConfig->setNameservers(['8.8.8.8', '8.8.4.4']); |
218 | |
219 | return $dnsConfig; |
220 | } |
221 | |
222 | /** |
223 | * @return TaskSpecRestartPolicy |
224 | */ |
225 | private function createRestartPolicy() |
226 | { |
227 | $restartPolicy = new TaskSpecRestartPolicy(); |
228 | $restartPolicy->setCondition($this->config->getRestartPolicy()); |
229 | |
230 | return $restartPolicy; |
231 | } |
232 | |
233 | /** |
234 | * @return ServiceSpecMode |
235 | */ |
236 | private function createReplicationMode() |
237 | { |
238 | $replicas = new ServiceSpecModeReplicated(); |
239 | $replicas->setReplicas($this->replicasNb); |
240 | $replicationMode = new ServiceSpecMode(); |
241 | $replicationMode->setReplicated($replicas); |
242 | |
243 | return $replicationMode; |
244 | } |
245 | |
246 | public function getRunningContainerByName(string $name) |
247 | { |
248 | $containers = collect($this->docker->containerList([ |
249 | 'all' => true |
250 | ])); |
251 | |
252 | $filters = $containers->filter(function ($container) use ($name) { |
253 | $labels = $container->getLabels(); |
254 | |
255 | if(isset($labels['com.docker.swarm.service.name'])) { |
256 | return $container->getState() !== 'exited' && preg_match('/'. $name .'/', $labels['com.docker.swarm.service.name']); |
257 | } |
258 | }); |
259 | |
260 | return $filters->map(function ($container) { |
261 | $labels = $container->getLabels(); |
262 | |
263 | if(isset($labels['com.docker.swarm.service.name'])) { |
264 | return $labels['com.docker.swarm.service.name']; |
265 | } |
266 | }); |
267 | |
268 | } |
269 | } |