Completed
Pull Request — master (#7)
by ophelie
02:01
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
     * Process result with returned code and output
148
     *
149
     * @param array $result The result of command with output and returnCode
150
     *
151
     * @throws DockerInstallationMissingException When returned code is 127
152
     * @throws ComposeFileNotFoundException When no compose file precise and docker-compose.yml not found
153
     * @throws DockerHostConnexionErrorException When we can't connect to docker host
154
     * @throws \Exception When an unknown error is returned
155
     */
156
    private function processResult($result)
157
    {
158
        if ($result['code'] === 127) {
159
            throw new DockerInstallationMissingException();
160
        }
161
162
        if ($result['code'] === 1) {
163
            if (!strpos($result['output'], 'DOCKER_HOST')) {
164
                if (!strpos($result['output'], 'docker-compose.yml')) {
165
                    throw new Exception($result['output']);
166
                } else {
167
                    throw new ComposeFileNotFoundException();
168
                }
169
            } else {
170
                throw new DockerHostConnexionErrorException();
171
            }
172
        }
173
174
        return $result['output'];
175
    }
176
177
    /**
178
     * Create the composeFileCollection from the type of value given
179
     *
180
     * @param mixed $composeFiles The docker-compose files (can be array, string or ComposeFile)
181
     *
182
     * @return ComposeFileCollection
183
     */
184
    private function createComposeFileCollection($composeFiles)
185
    {
186
        if (!$composeFiles instanceof ComposeFileCollection) {
187
            if (!is_array($composeFiles)) {
188
                return new ComposeFileCollection([$composeFiles]);
189
            } else {
190
                return new ComposeFileCollection($composeFiles);
191
            }
192
        } else {
193
            return $composeFiles;
194
        }
195
    }
196
197
    /**
198
     * Format the command to execute
199
     *
200
     * @param string                $subcommand   The subcommand to pass to docker-compose command
201
     * @param ComposeFileCollection $composeFiles The compose files to precise in the command
202
     */
203
    private function formatCommand($subcommand, ComposeFileCollection $composeFiles)
204
    {
205
        $project = '';
206
        $networking = '';
207
        $networkDriver = '';
208
209
        # Add project name, and network options
210
        if ($composeFiles->getProjectName() != null) {
211
            $project = ' --project-name ' . $composeFiles->getProjectName();
212
            if ($composeFiles->isNetworking()) {
213
                $networking = ' --x-networking';
214
                if ($composeFiles->getNetworkDriver() != null) {
215
                    $networkDriver = ' --x-network-driver ' . $composeFiles->getNetworkDriver();
216
                }
217
            }
218
        }
219
220
        # Add files names
221
        $preciseFiles = '';
222
        foreach ($composeFiles->getAll() as $composeFile) {
223
            $preciseFiles .= ' -f ' . $composeFile->getFileName();
224
        }
225
226
        $command = 'docker-compose' . $preciseFiles . $networking . $networkDriver . $project . ' ' . $subcommand;
227
228
        return $command;
229
    }
230
231
    /**
232
     * Execute docker-compose commande
233
     * @codeCoverageIgnore
234
     * @param string                $command      The command to execute
235
     */
236
    protected function execute($command)
237
    {
238
        $exec = new Command($command);
239
240
        if ($exec->execute()) {
241
            $output = $exec->getOutput();
242
        } else {
243
            $output = $exec->getError();
244
        }
245
246
        return array(
247
            'output' => $output,
248
            'code' => $exec->getExitCode()
249
        );
250
    }
251
}
252