Completed
Pull Request — master (#17)
by Tobias
02:16
created

ComposeManager::execute()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 13
rs 9.4285
cc 2
eloc 8
nc 2
nop 1
1
<?php
2
3
namespace DockerCompose\Manager;
4
5
use DockerCompose\Exception\ComposeFileNotFoundException;
6
use DockerCompose\Exception\DockerHostConnexionErrorException;
7
use DockerCompose\Exception\DockerInstallationMissingException;
8
use DockerCompose\Exception\NoSuchServiceException;
9
use DockerCompose\ComposeFileCollection;
10
use mikehaertl\shellcommand\Command;
11
use Exception;
12
13
/**
14
 * DockerCompose\Manager\ComposeManager
15
 */
16
class ComposeManager
17
{
18
19
    /**
20
     * @var array environment variables for command
21
     */
22
    public $env = [];
23
24
    /**
25
     * @var array environment variables for command
26
     */
27
    public $cwd = null;
28
29
    /**
30
     * Start service containers
31
     *
32
     * @param mixed $composeFiles The compose files names
33
     */
34
    public function start($composeFiles = array())
35
    {
36
        return $this->processResult(
37
            $this->execute(
38
                $this->formatCommand('up -d', $this->createComposeFileCollection($composeFiles))
39
            )
40
        );
41
    }
42
43
    /**
44
     * Stop service containers
45
     *
46
     * @param mixed $composeFiles The compose files names
47
     */
48
    public function stop($composeFiles = array())
49
    {
50
        return $this->processResult(
51
            $this->execute(
52
                $this->formatCommand('stop', $this->createComposeFileCollection($composeFiles))
53
            )
54
        );
55
    }
56
57
    /**
58
     * Stop service containers
59
     *
60
     * @param mixed   $composeFiles  The compose files names
61
     * @param boolean $force         If the remove need to be force (default=false)
62
     * @param boolean $removeVolumes If we need to remove the volumes (default=false)
63
     */
64
    public function remove($composeFiles = array(), $force = false, $removeVolumes = false)
0 ignored issues
show
Unused Code introduced by
The parameter $force is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
65
    {
66
        $command = 'rm --force';
67
68
        if ($removeVolumes) {
69
            $command .= ' -v';
70
        }
71
72
        return $this->processResult(
73
            $this->execute(
74
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
75
            )
76
        );
77
    }
78
79
     /**
80
     * Stop service containers
81
     *
82
     * @param mixed   $composeFiles  The compose files names
83
     * @param string  $signal        Optionnal to precise SIGNAL to send to the container for SIGKILL replacement.
84
     */
85 View Code Duplication
    public function kill($composeFiles = array(), $signal = 'SIGKILL')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
86
    {
87
        $command = 'kill';
88
89
        if ($signal !== 'SIGKILL') {
90
            $command .= ' -s ' . $signal;
91
        }
92
93
        return $this->processResult(
94
            $this->execute(
95
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
96
            )
97
        );
98
    }
99
100
    /**
101
     * Down service containers
102
     *
103
     * @param mixed   $composeFiles  The compose files names
104
     */
105
    public function down($composeFiles = array())
106
    {
107
        $command = 'down';
108
109
        return $this->processResult(
110
            $this->execute(
111
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
112
            )
113
        );
114
    }
115
116
    /**
117
     * Build service images
118
     *
119
     * @param mixed   $composeFiles  The compose files names
120
     * @param boolean $pull          If we want attempt to pull a newer version of the from image
121
     * @param boolean $forceRemove   If we want remove the intermediate containers
122
     * @param bollean $cache         If we can use the cache when building the image
123
     */
124
    public function build($composeFiles = array(), $pull = true, $forceRemove = false, $cache = true)
125
    {
126
        $command = 'build';
127
128
        if ($pull) {
129
            $command .= ' --pull';
130
        }
131
132
        if ($forceRemove) {
133
            $command .= ' --force-rm';
134
        }
135
136
        if (!$cache) {
137
            $command .= ' --no-cache';
138
        }
139
140
        return $this->processResult(
141
            $this->execute(
142
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
143
            )
144
        );
145
    }
146
147
    /**
148
     * Pull service images
149
     *
150
     * @param mixed   $composeFiles  The compose files names
151
     */
152
    public function pull($composeFiles = array())
153
    {
154
        $command = 'pull';
155
156
        return $this->processResult(
157
            $this->execute(
158
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
159
            )
160
        );
161
    }
162
163
    /**
164
     * Push service images
165
     *
166
     * @param mixed   $composeFiles  The compose files names
167
     */
168
    public function push($composeFiles = array())
169
    {
170
        $command = 'push';
171
172
        return $this->processResult(
173
            $this->execute(
174
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
175
            )
176
        );
177
    }
178
179
180
    /**
181
     * Show logs
182
     *
183
     * @param mixed   $composeFiles  The compose files names
184
     */
185
    public function logs($composeFiles = array(), $tail = 100)
186
    {
187
        $command = 'logs --tail '.$tail;
188
189
        return $this->processResult(
190
            $this->execute(
191
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
192
            )
193
        );
194
    }
195
196
197
    /**
198
     * Restart running containers
199
     *
200
     * @param mixed   $composeFiles  The compose files names
201
     * @param integer $timeout       If we want attempt to pull a newer version of the from image
202
     */
203 View Code Duplication
    public function restart($composeFiles = array(), $timeout = 10)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
204
    {
205
        $command = 'restart';
206
207
        if ($timeout != 10) {
208
            $command .= ' --timeout='.$timeout;
209
        }
210
211
        return $this->processResult(
212
            $this->execute(
213
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
214
            )
215
        );
216
    }
217
218
    /**
219
     * Run service with command
220
     *
221
     * @param string $service Service name
222
     * @param string $command Command to pass to service
223
     * @param mixed   $composeFiles  The compose files names
224
     */
225
    public function run($service, $command = null, $composeFiles = array())
226
    {
227
        $command = 'run ' . $service . ' ' . $command;
228
        #echo $this->formatCommand($command, $this->createComposeFileCollection($composeFiles)); exit;
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
229
        $result = $this->execute(
230
            $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
231
        );
232
233
        if ($result['code'] == 1 && strpos($result['output'], 'service') != false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpos($result['output'], 'service') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
234
            throw new NoSuchServiceException($result['output']);
235
        }
236
237
        return $this->processResult($result);
238
    }
239
240
    /**
241
     * List containers
242
     *
243
     * @param mixed $composeFiles The compose files names
244
     */
245
    public function ps($composeFiles = array())
246
    {
247
        return $this->processResult(
248
            $this->execute(
249
                $this->formatCommand('ps', $this->createComposeFileCollection($composeFiles))
250
            )
251
        );
252
    }
253
254
    /**
255
     * Show configuration (yaml)
256
     *
257
     * @param mixed $composeFiles The compose files names
258
     */
259
    public function config($composeFiles = array())
260
    {
261
        return $this->processResult(
262
            $this->execute(
263
                $this->formatCommand('config', $this->createComposeFileCollection($composeFiles))
264
            )
265
        );
266
    }
267
268
    /**
269
     * List IP containers
270
     *
271
     * @param mixed $composeFiles The compose files names
272
     */
273 View Code Duplication
    public function ips($composeFiles = array())
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
274
    {
275
        $command = $this->formatCommand('ps', $this->createComposeFileCollection($composeFiles));
276
277
        $command = 'for CONTAINER in $(' . $command . ' -q); ';
278
        $command .= 'do echo "$(docker inspect --format \' {{ .Name }} \' $CONTAINER)\t';
279
        $command .= '$(docker inspect --format \' {{ .NetworkSettings.IPAddress }} \' $CONTAINER)"; done';
280
281
        return $this->processResult($this->execute($command));
0 ignored issues
show
Documentation introduced by
$command is of type string, but the function expects a object<mikehaertl\shellcommand\Command>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
282
    }
283
284
    /**
285
     * Process result with returned code and output
286
     *
287
     * @param array $result The result of command with output and returnCode
288
     *
289
     * @throws DockerInstallationMissingException When returned code is 127
290
     * @throws ComposeFileNotFoundException When no compose file precise and docker-compose.yml not found
291
     * @throws DockerHostConnexionErrorException When we can't connect to docker host
292
     * @throws \Exception When an unknown error is returned
293
     */
294
    private function processResult($result)
295
    {
296
        if ($result['code'] === 127) {
297
            throw new DockerInstallationMissingException();
298
        }
299
300
        if ($result['code'] === 1) {
301
            if (!strpos($result['output'], 'DOCKER_HOST')) {
302
                if (!strpos($result['output'], 'docker-compose.yml')) {
303
                    throw new Exception($result['output']);
304
                } else {
305
                    throw new ComposeFileNotFoundException();
306
                }
307
            } else {
308
                throw new DockerHostConnexionErrorException();
309
            }
310
        }
311
312
        return $result['output'];
313
    }
314
315
    /**
316
     * Create the composeFileCollection from the type of value given
317
     *
318
     * @param mixed $composeFiles The docker-compose files (can be array, string or ComposeFile)
319
     *
320
     * @return ComposeFileCollection
321
     */
322
    private function createComposeFileCollection($composeFiles)
323
    {
324
        if (!$composeFiles instanceof ComposeFileCollection) {
325
            if (!is_array($composeFiles)) {
326
                return new ComposeFileCollection([$composeFiles]);
327
            } else {
328
                return new ComposeFileCollection($composeFiles);
329
            }
330
        } else {
331
            return $composeFiles;
332
        }
333
    }
334
335
    /**
336
     * Format the command to execute
337
     *
338
     * @param string                $subcommand   The subcommand to pass to docker-compose command
339
     * @param ComposeFileCollection $composeFiles The compose files to precise in the command
340
     */
341
    private function formatCommand($subcommand, ComposeFileCollection $composeFiles)
342
    {
343
        $command = new Command("docker-compose");
344
345
        # Set working directory
346
        if (!empty($this->cwd)) {
347
            $command->procCwd = $this->cwd;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->cwd of type array is incompatible with the declared type string|null of property $procCwd.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
348
        }
349
350
        # Set environment variables
351
        if (!empty($this->env)) {
352
            $command->procEnv = $this->env;
353
        }
354
355
        # Add files names
356
        foreach ($composeFiles->getAll() as $composeFile) {
357
            $command->addArg('-f', $composeFile->getFileName());
358
        }
359
360
        # Add project name
361
        if ($composeFiles->getProjectName() != null) {
362
            $command->addArg('--project-name', $composeFiles->getProjectName());
363
        }
364
365
        $command->addArg($subcommand);
366
367
        return $command;
368
    }
369
370
    /**
371
     * Execute docker-compose commande
372
     * @codeCoverageIgnore
373
     * @param Command $command The command to execute.
374
     */
375
    protected function execute($command)
376
    {
377
        if ($command->execute()) {
378
            $output = $command->getOutput();
379
        } else {
380
            $output = $command->getError();
381
        }
382
383
        return array(
384
            'output' => $output,
385
            'code' => $command->getExitCode()
386
        );
387
    }
388
}
389