Completed
Push — master ( f4794e...1f0a28 )
by ophelie
05:23
created

ComposeManager::restart()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 14
rs 9.4285
cc 2
eloc 7
nc 2
nop 2
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
     * Build service images
74
     *
75
     * @param mixed   $composeFiles  The compose files names
76
     * @param boolean $pull          If we want attempt to pull a newer version of the from image
77
     * @param boolean $forceRemove   If we want remove the intermediate containers
78
     * @param bollean $cache         If we can use the cache when building the image
79
     */
80
    public function build($composeFiles = array(), $pull = true, $forceRemove = false, $cache = true)
81
    {
82
        $command = 'build';
83
84
        if ($pull) {
85
            $command .= ' --pull';
86
        }
87
88
        if ($forceRemove) {
89
            $command .= ' --force-rm';
90
        }
91
92
        if (!$cache) {
93
            $command .= ' --no-cache';
94
        }
95
96
        return $this->processResult(
97
            $this->execute(
98
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
99
            )
100
        );
101
    }
102
103
104
    /**
105
     * Restart running containers
106
     *
107
     * @param mixed   $composeFiles  The compose files names
108
     * @param integer $timeout       If we want attempt to pull a newer version of the from image
109
     */
110
    public function restart($composeFiles = array(), $timeout = 10)
111
    {
112
        $command = 'restart';
113
114
        if ($timeout != 10) {
115
            $command .= ' --timeout='.$timeout;
116
        }
117
118
        return $this->processResult(
119
            $this->execute(
120
                $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
121
            )
122
        );
123
    }
124
125
    /**
126
     * Run service with command
127
     *
128
     * @param string $service Service name
129
     * @param string $command Command to pass to service
130
     * @param mixed   $composeFiles  The compose files names
131
     */
132
    public function run($service, $command, $composeFiles = array())
133
    {
134
        $command = 'run --rm ' . $service . ' ' . $command;
135
        $result = $this->execute(
136
            $this->formatCommand($command, $this->createComposeFileCollection($composeFiles))
137
        );
138
139
        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...
140
            throw new NoSuchServiceException($result['output']);
141
        }
142
143
        return $this->processResult($result);
144
    }
145
146
    /**
147
     * List containers
148
     *
149
     * @param mixed $composeFiles The compose files names
150
     */
151
    public function ps($composeFiles = array())
152
    {
153
        return $this->processResult(
154
            $this->execute(
155
                $this->formatCommand('ps', $this->createComposeFileCollection($composeFiles))
156
            )
157
        );
158
    }
159
160
    /**
161
     * Process result with returned code and output
162
     *
163
     * @param array $result The result of command with output and returnCode
164
     *
165
     * @throws DockerInstallationMissingException When returned code is 127
166
     * @throws ComposeFileNotFoundException When no compose file precise and docker-compose.yml not found
167
     * @throws DockerHostConnexionErrorException When we can't connect to docker host
168
     * @throws \Exception When an unknown error is returned
169
     */
170
    private function processResult($result)
171
    {
172
        if ($result['code'] === 127) {
173
            throw new DockerInstallationMissingException();
174
        }
175
176
        if ($result['code'] === 1) {
177
            if (!strpos($result['output'], 'DOCKER_HOST')) {
178
                if (!strpos($result['output'], 'docker-compose.yml')) {
179
                    throw new Exception($result['output']);
180
                } else {
181
                    throw new ComposeFileNotFoundException();
182
                }
183
            } else {
184
                throw new DockerHostConnexionErrorException();
185
            }
186
        }
187
188
        return $result['output'];
189
    }
190
191
    /**
192
     * Create the composeFileCollection from the type of value given
193
     *
194
     * @param mixed $composeFiles The docker-compose files (can be array, string or ComposeFile)
195
     *
196
     * @return ComposeFileCollection
197
     */
198
    private function createComposeFileCollection($composeFiles)
199
    {
200
        if (!$composeFiles instanceof ComposeFileCollection) {
201
            if (!is_array($composeFiles)) {
202
                return new ComposeFileCollection([$composeFiles]);
203
            } else {
204
                return new ComposeFileCollection($composeFiles);
205
            }
206
        } else {
207
            return $composeFiles;
208
        }
209
    }
210
211
    /**
212
     * Format the command to execute
213
     *
214
     * @param string                $subcommand   The subcommand to pass to docker-compose command
215
     * @param ComposeFileCollection $composeFiles The compose files to precise in the command
216
     */
217
    private function formatCommand($subcommand, ComposeFileCollection $composeFiles)
218
    {
219
        $project = '';
220
        $networking = '';
221
        $networkDriver = '';
222
223
        # Add project name, and network options
224
        if ($composeFiles->getProjectName() != null) {
225
            $project = ' --project-name ' . $composeFiles->getProjectName();
226
            if ($composeFiles->isNetworking()) {
227
                $networking = ' --x-networking';
228
                if ($composeFiles->getNetworkDriver() != null) {
229
                    $networkDriver = ' --x-network-driver ' . $composeFiles->getNetworkDriver();
230
                }
231
            }
232
        }
233
234
        # Add files names
235
        $preciseFiles = '';
236
        foreach ($composeFiles->getAll() as $composeFile) {
237
            $preciseFiles .= ' -f ' . $composeFile->getFileName();
238
        }
239
240
        $command = 'docker-compose' . $preciseFiles . $networking . $networkDriver . $project . ' ' . $subcommand;
241
242
        return $command;
243
    }
244
245
    /**
246
     * Execute docker-compose commande
247
     * @codeCoverageIgnore
248
     * @param string                $command      The command to execute
249
     */
250
    protected function execute($command)
251
    {
252
        $exec = new Command($command);
253
254
        if ($exec->execute()) {
255
            $output = $exec->getOutput();
256
        } else {
257
            $output = $exec->getError();
258
        }
259
260
        return array(
261
            'output' => $output,
262
            'code' => $exec->getExitCode()
263
        );
264
    }
265
}
266