Passed
Push — master ( a5203b...48c4ea )
by Tom
04:36
created

StepContainer::generateStepName()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 14
nc 4
nop 3
dl 0
loc 20
ccs 14
cts 14
cp 1
crap 3
rs 9.7998
c 0
b 0
f 0
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines\Runner;
6
7
use Ktomk\Pipelines\Cli\Docker;
8
use Ktomk\Pipelines\Cli\Exec;
9
use Ktomk\Pipelines\Cli\Streams;
10
use Ktomk\Pipelines\File\Pipeline\Step;
11
use Ktomk\Pipelines\Lib;
12
13
/**
14
 * Class StepContainer
15
 *
16
 * @package Ktomk\Pipelines\Runner
17
 */
18
class StepContainer
19
{
20
    /**
21
     * @var null|string id of the (running) container
22
     */
23
    private $id;
24
25
    /**
26
     * @var null|string name of the container
27
     */
28
    private $name;
29
30
    /**
31
     * @var Step
32
     */
33
    private $step;
34
35
    /**
36
     * @var Exec
37
     */
38
    private $exec;
39
40
    /**
41
     * @param Step $step
42
     * @param null|Exec $exec
43
     *
44
     * @return StepContainer
45
     */
46 6
    public static function create(Step $step, Exec $exec = null)
47
    {
48 6
        if (null === $exec) {
49 2
            $exec = new Exec();
50
        }
51
52 6
        return new self($step, $exec);
53
    }
54
55
    /**
56
     * @param Step $step
57
     * @param string $prefix
58
     * @param string $project name
59
     *
60
     * @return string
61
     */
62 1
    public static function createName(Step $step, $prefix, $project)
63
    {
64 1
        return self::create($step)->generateName($prefix, $project);
65
    }
66
67
    /**
68
     * kill and remove static implementation
69
     *
70
     * @param Exec $exec
71
     * @param string|string[] $idOrIds container id(s) or name(s)
72
     * @param bool $kill
73
     * @param bool $remove
74
     *
75
     * @return void
76
     */
77 4
    public static function execKillAndRemove(Exec $exec, $idOrIds, $kill, $remove)
78
    {
79 4
        if ($kill) {
80 2
            Docker::create($exec)->getProcessManager()->kill($idOrIds);
81
        }
82
83 4
        if ($remove) {
84 3
            Docker::create($exec)->getProcessManager()->remove($idOrIds);
85
        }
86 4
    }
87
88
    /**
89
     * @param Exec $exec
90
     * @param array $args
91
     *
92
     * @return array array(int $status, string $out, string $err, string|null $id)
93
     */
94 4
    public static function execRun(Exec $exec, array $args)
95
    {
96 4
        $status = $exec->capture('docker', Lib::merge('run', $args), $out, $err);
97
98 4
        $id = null;
99 4
        if (0 === $status) {
100 4
            $id = rtrim($out) ?: null;
101
        }
102
103 4
        return array($status, $out, $err, $id);
104
    }
105
106
    /**
107
     * @param Exec $exec
108
     * @param Streams $streams
109
     * @param string|string[] $idOrIds
110
     * @param int $status
111
     * @param Flags $flags
112
     * @param string $message
113
     * @param string $id
114
     *
115
     * @return void
116
     *
117
     * @see StepRunner::shutdownStepContainer
118
     */
119 4
    public static function execShutdownContainer(Exec $exec, Streams $streams, $idOrIds, $status, Flags $flags, $message)
120
    {
121
        # keep container on error
122 4
        if (0 !== $status && $flags->keepOnError()) {
123 2
            $streams->err(sprintf("error, %s\n", $message));
124
125 2
            return;
126
        }
127
128
        # keep or kill/remove container
129 2
        self::execKillAndRemove($exec, $idOrIds, $flags->killContainer(), $flags->removeContainer());
130
131 2
        if ($flags->keep()) {
132 1
            $streams->out(sprintf("%s\n", $message));
133
        }
134 2
    }
135
136
    /**
137
     * generate step container name
138
     *
139
     * example: pipelines-1.pipeline-features-and-introspection.default.app
140
     *              ^    `^`                  ^                `    ^  ` ^
141
     *              |     |                   |                     |    |
142
     *              | step number        step name           pipeline id |
143
     *           prefix                                                project
144
     *
145
     * @param string $prefix
146
     * @param Step $step
147
     * @param string $project
148
     *
149
     * @return string
150
     *
151
     * @see StepContainer::generateName()
152
     */
153 9
    public static function generateStepName($prefix, Step $step, $project)
154
    {
155 9
        $idContainerSlug = preg_replace('([^a-zA-Z0-9_.-]+)', '-', $step->getPipeline()->getId());
156 9
        if ('' === $idContainerSlug) {
157 9
            $idContainerSlug = 'null';
158
        }
159 9
        $nameSlug = preg_replace(array('( )', '([^a-zA-Z0-9_.-]+)'), array('-', ''), $step->getName());
160 9
        if ('' === $nameSlug) {
161 9
            $nameSlug = 'no-name';
162
        }
163
164 9
        $stepNumber = $step->getIndex() + 1;
165
166 9
        return $prefix . '-' . implode(
167 9
            '.',
168
            array(
169 9
                    $stepNumber,
170 9
                    $nameSlug,
171 9
                    trim($idContainerSlug, '-'),
172 9
                    $project,
173
                )
174
        );
175
    }
176
177
    /**
178
     * generate service container name
179
     *
180
     * exmaple: pipelines-service-redis.pipelines
181
     *              ^    `   ^   `  ^  `   ^
182
     *              |        |      |      |
183
     *              |    "service"  |   project
184
     *           prefix       service name
185
     *
186
     * @param string $prefix
187
     * @param string $serviceName
188
     * @param string $project
189
     *
190
     * @return string
191
     *
192
     * @see StepRunner::obtainServicesNetwork()
193
     */
194 1
    public static function generateServiceName($prefix, $serviceName, $project)
195
    {
196 1
        $nameSlug = preg_replace(array('( )', '([^a-zA-Z0-9_.-]+)'), array('-', ''), $serviceName);
197
198 1
        return $prefix . '-service-' . implode(
199 1
            '.',
200
            array(
201 1
                    $nameSlug,
202 1
                    $project,
203
                )
204
        );
205
    }
206
207
    /**
208
     * StepContainer constructor.
209
     *
210
     * @param Step $step
211
     * @param Exec $exec
212
     */
213 14
    public function __construct(Step $step, Exec $exec)
214
    {
215 14
        $this->step = $step;
216 14
        $this->exec = $exec;
217 14
    }
218
219
    /**
220
     * generate step container name
221
     *
222
     * @param string $prefix for the name (normally "pipelines")
223
     * @param string $project name
224
     *
225
     * @return string
226
     */
227 9
    public function generateName($prefix, $project)
228
    {
229 9
        return $this->name = self::generateStepName($prefix, $this->step, $project);
230
    }
231
232
    /**
233
     * the display id
234
     *
235
     *   side-effect: if id is null, this signals a dry-run which is made
236
     * visible by the string "*dry-run*"
237
     *
238
     * @return string
239
     */
240 8
    public function getDisplayId()
241
    {
242 8
        return isset($this->id) ? $this->id : '*dry-run*';
243
    }
244
245
    /**
246
     * @return null|string ID of (once) running container or null if not yet running
247
     */
248 3
    public function getId()
249
    {
250 3
        return $this->id;
251
    }
252
253
    /**
254
     * @return null|string name of the container, NULL if no name generated yet
255
     */
256 3
    public function getName()
257
    {
258 3
        return $this->name;
259
    }
260
261
    /**
262
     * @param bool $keep a container on true, kill on false (if it exists)
263
     *
264
     * @return null|string
265
     */
266 7
    public function keepOrKill($keep)
267
    {
268 7
        $name = $this->name;
269 7
        if (null === $name) {
270 1
            throw new \BadMethodCallException('Container has no name yet');
271
        }
272
273 6
        $processManager = Docker::create($this->exec)->getProcessManager();
274
275 6
        if (false === $keep) {
276 2
            $processManager->zapContainersByName($name);
277
278 2
            return $this->id = null;
279
        }
280
281 5
        return $this->id = $processManager->findContainerIdByName($name);
282
    }
283
284
    /**
285
     * @param bool $kill
286
     * @param bool $remove
287
     *
288
     * @return void
289
     */
290 2
    public function killAndRemove($kill, $remove)
291
    {
292 2
        $id = $this->getDisplayId();
293
294 2
        self::execKillAndRemove($this->exec, $id, $kill, $remove);
295 2
    }
296
297
    /**
298
     * @param array $args
299
     *
300
     * @return array array(int $status, string $out, string $err)
301
     */
302 4
    public function run(array $args)
303
    {
304 4
        $execRun = self::execRun($this->exec, $args);
305 4
        $this->id = array_pop($execRun);
306
307 4
        return $execRun;
308
    }
309
}
310