Completed
Push — master ( 847154...b17a4a )
by ophelie
12:26
created

ComposeManager::remove()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 9
Bugs 0 Features 7
Metric Value
c 9
b 0
f 7
dl 0
loc 17
rs 9.4285
cc 3
eloc 9
nc 4
nop 3
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
     * Start service containers
21
     *
22
     * @param mixed $composeFiles The compose files names
23
     */
24
    public function start($composeFiles = array())
25
    {
26
        return $this->processResult(
27
            $this->execute(
28
                $this->formatCommand('up -d', $this->createComposeFileCollection($composeFiles))
29
            )
30
        );
31
    }
32
33
    /**
34
     * Stop service containers
35
     *
36
     * @param mixed $composeFiles The compose files names
37
     */
38
    public function stop($composeFiles = array())
39
    {
40
        return $this->processResult(
41
            $this->execute(
42
                $this->formatCommand('stop', $this->createComposeFileCollection($composeFiles))
43
            )
44
        );
45
    }
46
47
    /**
48
     * Stop service containers
49
     *
50
     * @param mixed   $composeFiles  The compose files names
51
     * @param boolean $force         If the remove need to be force (default=false)
52
     * @param boolean $removeVolumes If we need to remove the volumes (default=false)
53
     */
54
    public function remove($composeFiles = array(), $force = false, $removeVolumes = false)
55
    {
56
        $command = 'rm';
57
        if ($force) {
58
            $command .= ' --force';
59
        }
60
61
        if ($removeVolumes) {
62
            $command .= ' -v';
63
        }
64
65
        return $this->processResult(
66
            $this->execute(
67
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
68
            )
69
        );
70
    }
71
72
     /**
73
     * Stop service containers
74
     *
75
     * @param mixed   $composeFiles  The compose files names
76
     * @param string  $signal        Optionnal to precise SIGNAL to send to the container for SIGKILL replacement.
77
     */
78 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...
79
    {
80
        $command = 'kill';
81
82
        if ($signal !== 'SIGKILL') {
83
            $command .= ' -s ' . $signal;
84
        }
85
86
        return $this->processResult(
87
            $this->execute(
88
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
89
            )
90
        );
91
    }
92
93
    /**
94
     * Build service images
95
     *
96
     * @param mixed   $composeFiles  The compose files names
97
     * @param boolean $pull          If we want attempt to pull a newer version of the from image
98
     * @param boolean $forceRemove   If we want remove the intermediate containers
99
     * @param bollean $cache         If we can use the cache when building the image
100
     */
101
    public function build($composeFiles = array(), $pull = true, $forceRemove = false, $cache = true)
102
    {
103
        $command = 'build';
104
105
        if ($pull) {
106
            $command .= ' --pull';
107
        }
108
109
        if ($forceRemove) {
110
            $command .= ' --force-rm';
111
        }
112
113
        if (!$cache) {
114
            $command .= ' --no-cache';
115
        }
116
117
        return $this->processResult(
118
            $this->execute(
119
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
120
            )
121
        );
122
    }
123
124
125
    /**
126
     * Restart running containers
127
     *
128
     * @param mixed   $composeFiles  The compose files names
129
     * @param integer $timeout       If we want attempt to pull a newer version of the from image
130
     */
131 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...
132
    {
133
        $command = 'restart';
134
135
        if ($timeout != 10) {
136
            $command .= ' --timeout='.$timeout;
137
        }
138
139
        return $this->processResult(
140
            $this->execute(
141
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
142
            )
143
        );
144
    }
145
146
    /**
147
     * Run service with command
148
     *
149
     * @param string $service Service name
150
     * @param string $command Command to pass to service
151
     * @param mixed   $composeFiles  The compose files names
152
     */
153
    public function run($service, $command, $composeFiles = array())
154
    {
155
        $command = 'run --rm ' . $service . ' ' . $command;
156
        $result = $this->execute(
157
            $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
158
        );
159
160
        if ($result['code'] == 1 && strpos($result['output'], 'No such service') != false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpos($result['output'], 'No such 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...
161
            throw new NoSuchServiceException($result['output']);
162
        }
163
164
        return $this->processResult($result);
165
    }
166
167
    /**
168
     * List containers
169
     *
170
     * @param mixed $composeFiles The compose files names
171
     */
172
    public function ps($composeFiles = array())
173
    {
174
        return $this->processResult(
175
            $this->execute(
176
                $this->formatCommand('ps', $this->createComposeFileCollection($composeFiles))
177
            )
178
        );
179
    }
180
181
    /**
182
     * List IP containers
183
     *
184
     * @param mixed $composeFiles The compose files names
185
     */
186 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...
187
    {
188
        $command = $this->formatCommand('ps', $this->createComposeFileCollection($composeFiles));
189
190
        $command = 'for CONTAINER in $(' . $command . ' -q); ';
191
        $command .= 'do echo "$(docker inspect --format \' {{ .Name }} \' $CONTAINER)\t';
192
        $command .= '$(docker inspect --format \' {{ .NetworkSettings.IPAddress }} \' $CONTAINER)"; done';
193
194
        return $this->processResult($this->execute($command));
195
    }
196
197
    /**
198
     * Process result with returned code and output
199
     *
200
     * @param array $result The result of command with output and returnCode
201
     *
202
     * @throws DockerInstallationMissingException When returned code is 127
203
     * @throws ComposeFileNotFoundException When no compose file precise and docker-compose.yml not found
204
     * @throws DockerHostConnexionErrorException When we can't connect to docker host
205
     * @throws \Exception When an unknown error is returned
206
     */
207
    private function processResult($result)
208
    {
209
        if ($result['code'] === 127) {
210
            throw new DockerInstallationMissingException();
211
        }
212
213
        if ($result['code'] === 1) {
214
            if (!strpos($result['output'], 'DOCKER_HOST')) {
215
                if (!strpos($result['output'], 'docker-compose.yml')) {
216
                    throw new Exception($result['output']);
217
                } else {
218
                    throw new ComposeFileNotFoundException();
219
                }
220
            } else {
221
                throw new DockerHostConnexionErrorException();
222
            }
223
        }
224
225
        return $result['output'];
226
    }
227
228
    /**
229
     * Create the composeFileCollection from the type of value given
230
     *
231
     * @param mixed $composeFiles The docker-compose files (can be array, string or ComposeFile)
232
     *
233
     * @return ComposeFileCollection
234
     */
235
    private function createComposeFileCollection($composeFiles)
236
    {
237
        if (!$composeFiles instanceof ComposeFileCollection) {
238
            if (!is_array($composeFiles)) {
239
                return new ComposeFileCollection([$composeFiles]);
240
            } else {
241
                return new ComposeFileCollection($composeFiles);
242
            }
243
        } else {
244
            return $composeFiles;
245
        }
246
    }
247
248
    /**
249
     * Format the command to execute
250
     *
251
     * @param string                $subcommand   The subcommand to pass to docker-compose command
252
     * @param ComposeFileCollection $composeFiles The compose files to precise in the command
253
     */
254
    private function formatCommand($subcommand, ComposeFileCollection $composeFiles)
255
    {
256
        $project = '';
257
        $networking = '';
258
        $networkDriver = '';
259
260
        # Add project name, and network options
261
        if ($composeFiles->getProjectName() != null) {
262
            $project = ' --project-name ' . $composeFiles->getProjectName();
263
            if ($composeFiles->isNetworking()) {
264
                $networking = ' --x-networking';
265
                if ($composeFiles->getNetworkDriver() != null) {
266
                    $networkDriver = ' --x-network-driver ' . $composeFiles->getNetworkDriver();
267
                }
268
            }
269
        }
270
271
        # Add files names
272
        $preciseFiles = '';
273
        foreach ($composeFiles->getAll() as $composeFile) {
274
            $preciseFiles .= ' -f ' . $composeFile->getFileName();
275
        }
276
277
        $command = 'docker-compose' . $preciseFiles . $networking . $networkDriver . $project . ' ' . $subcommand;
278
279
        return $command;
280
    }
281
282
    /**
283
     * Execute docker-compose commande
284
     * @codeCoverageIgnore
285
     * @param string                $command      The command to execute
286
     */
287
    protected function execute($command)
288
    {
289
        $exec = new Command($command);
290
291
        if ($exec->execute()) {
292
            $output = $exec->getOutput();
293
        } else {
294
            $output = $exec->getError();
295
        }
296
297
        return array(
298
            'output' => $output,
299
            'code' => $exec->getExitCode()
300
        );
301
    }
302
}
303