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

ComposeManager::push()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 10
rs 9.4285
cc 1
eloc 5
nc 1
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
     * Build service images
102
     *
103
     * @param mixed   $composeFiles  The compose files names
104
     * @param boolean $pull          If we want attempt to pull a newer version of the from image
105
     * @param boolean $forceRemove   If we want remove the intermediate containers
106
     * @param bollean $cache         If we can use the cache when building the image
107
     */
108
    public function build($composeFiles = array(), $pull = true, $forceRemove = false, $cache = true)
109
    {
110
        $command = 'build';
111
112
        if ($pull) {
113
            $command .= ' --pull';
114
        }
115
116
        if ($forceRemove) {
117
            $command .= ' --force-rm';
118
        }
119
120
        if (!$cache) {
121
            $command .= ' --no-cache';
122
        }
123
124
        return $this->processResult(
125
            $this->execute(
126
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
127
            )
128
        );
129
    }
130
131
    /**
132
     * Pull service images
133
     *
134
     * @param mixed   $composeFiles  The compose files names
135
     */
136
    public function pull($composeFiles = array())
137
    {
138
        $command = 'pull';
139
140
        return $this->processResult(
141
            $this->execute(
142
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
143
            )
144
        );
145
    }
146
147
    /**
148
     * Push service images
149
     *
150
     * @param mixed   $composeFiles  The compose files names
151
     */
152
    public function push($composeFiles = array())
153
    {
154
        $command = 'push';
155
156
        return $this->processResult(
157
            $this->execute(
158
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
159
            )
160
        );
161
    }
162
163
164
    /**
165
     * Show logs
166
     *
167
     * @param mixed   $composeFiles  The compose files names
168
     */
169
    public function logs($composeFiles = array(), $tail = 100)
170
    {
171
        $command = 'logs --tail '.$tail;
172
173
        return $this->processResult(
174
            $this->execute(
175
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
176
            )
177
        );
178
    }
179
180
181
    /**
182
     * Restart running containers
183
     *
184
     * @param mixed   $composeFiles  The compose files names
185
     * @param integer $timeout       If we want attempt to pull a newer version of the from image
186
     */
187 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...
188
    {
189
        $command = 'restart';
190
191
        if ($timeout != 10) {
192
            $command .= ' --timeout='.$timeout;
193
        }
194
195
        return $this->processResult(
196
            $this->execute(
197
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
198
            )
199
        );
200
    }
201
202
    /**
203
     * Run service with command
204
     *
205
     * @param string $service Service name
206
     * @param string $command Command to pass to service
207
     * @param mixed   $composeFiles  The compose files names
208
     */
209
    public function run($service, $command, $composeFiles = array())
210
    {
211
        $command = 'run --rm ' . $service . ' ' . $command;
212
        $result = $this->execute(
213
            $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
214
        );
215
216
        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...
217
            throw new NoSuchServiceException($result['output']);
218
        }
219
220
        return $this->processResult($result);
221
    }
222
223
    /**
224
     * List containers
225
     *
226
     * @param mixed $composeFiles The compose files names
227
     */
228
    public function ps($composeFiles = array())
229
    {
230
        return $this->processResult(
231
            $this->execute(
232
                $this->formatCommand('ps', $this->createComposeFileCollection($composeFiles))
233
            )
234
        );
235
    }
236
237
    /**
238
     * Show configuration (yaml)
239
     *
240
     * @param mixed $composeFiles The compose files names
241
     */
242
    public function config($composeFiles = array())
243
    {
244
        return $this->processResult(
245
            $this->execute(
246
                $this->formatCommand('config', $this->createComposeFileCollection($composeFiles))
247
            )
248
        );
249
    }
250
251
    /**
252
     * List IP containers
253
     *
254
     * @param mixed $composeFiles The compose files names
255
     */
256 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...
257
    {
258
        $command = $this->formatCommand('ps', $this->createComposeFileCollection($composeFiles));
259
260
        $command = 'for CONTAINER in $(' . $command . ' -q); ';
261
        $command .= 'do echo "$(docker inspect --format \' {{ .Name }} \' $CONTAINER)\t';
262
        $command .= '$(docker inspect --format \' {{ .NetworkSettings.IPAddress }} \' $CONTAINER)"; done';
263
264
        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...
265
    }
266
267
    /**
268
     * Process result with returned code and output
269
     *
270
     * @param array $result The result of command with output and returnCode
271
     *
272
     * @throws DockerInstallationMissingException When returned code is 127
273
     * @throws ComposeFileNotFoundException When no compose file precise and docker-compose.yml not found
274
     * @throws DockerHostConnexionErrorException When we can't connect to docker host
275
     * @throws \Exception When an unknown error is returned
276
     */
277
    private function processResult($result)
278
    {
279
        if ($result['code'] === 127) {
280
            throw new DockerInstallationMissingException();
281
        }
282
283
        if ($result['code'] === 1) {
284
            if (!strpos($result['output'], 'DOCKER_HOST')) {
285
                if (!strpos($result['output'], 'docker-compose.yml')) {
286
                    throw new Exception($result['output']);
287
                } else {
288
                    throw new ComposeFileNotFoundException();
289
                }
290
            } else {
291
                throw new DockerHostConnexionErrorException();
292
            }
293
        }
294
295
        return $result['output'];
296
    }
297
298
    /**
299
     * Create the composeFileCollection from the type of value given
300
     *
301
     * @param mixed $composeFiles The docker-compose files (can be array, string or ComposeFile)
302
     *
303
     * @return ComposeFileCollection
304
     */
305
    private function createComposeFileCollection($composeFiles)
306
    {
307
        if (!$composeFiles instanceof ComposeFileCollection) {
308
            if (!is_array($composeFiles)) {
309
                return new ComposeFileCollection([$composeFiles]);
310
            } else {
311
                return new ComposeFileCollection($composeFiles);
312
            }
313
        } else {
314
            return $composeFiles;
315
        }
316
    }
317
318
    /**
319
     * Format the command to execute
320
     *
321
     * @param string                $subcommand   The subcommand to pass to docker-compose command
322
     * @param ComposeFileCollection $composeFiles The compose files to precise in the command
323
     */
324
    private function formatCommand($subcommand, ComposeFileCollection $composeFiles)
325
    {
326
        $command = new Command("docker-compose");
327
328
        # Set working directory
329
        if (!empty($this->cwd)) {
330
            $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...
331
        }
332
333
        # Set environment variables
334
        if (!empty($this->env)) {
335
            $command->procEnv = $this->env;
336
        }
337
338
        # Add files names
339
        foreach ($composeFiles->getAll() as $composeFile) {
340
            $command->addArg('-f', $composeFile->getFileName());
341
        }
342
343
        # Add project name
344
        if ($composeFiles->getProjectName() != null) {
345
            $command->addArg('--project-name', $composeFiles->getProjectName());
346
        }
347
348
        $command->addArg($subcommand);
349
350
        return $command;
351
    }
352
353
    /**
354
     * Execute docker-compose commande
355
     * @codeCoverageIgnore
356
     * @param Command $command The command to execute.
357
     */
358
    protected function execute($command)
359
    {
360
        if ($command->execute()) {
361
            $output = $command->getOutput();
362
        } else {
363
            $output = $command->getError();
364
        }
365
366
        return array(
367
            'output' => $output,
368
            'code' => $command->getExitCode()
369
        );
370
    }
371
}
372