Executor   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 23
Bugs 7 Features 6
Metric Value
wmc 10
c 23
b 7
f 6
lcom 1
cbo 11
dl 0
loc 155
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A test() 0 10 2
A create() 0 23 3
B run() 0 50 4
1
<?php
2
/*
3
 * This file is part of JoliCi.
4
*
5
* (c) Joel Wurtz <[email protected]>
6
*
7
* For the full copyright and license information, please view the LICENSE
8
* file that was distributed with this source code.
9
*/
10
11
namespace Joli\JoliCi;
12
13
use Docker\API\Model\BuildInfo;
14
use Docker\API\Model\ContainerConfig;
15
use Docker\API\Model\HostConfig;
16
use Docker\Docker;
17
use Docker\Context\Context;
18
use Docker\Manager\ContainerManager;
19
use Docker\Manager\ImageManager;
20
use Docker\Stream\BuildStream;
21
use Http\Client\Plugin\Exception\ClientErrorException;
22
use Monolog\Logger;
23
24
class Executor
25
{
26
    /**
27
     * Docker client
28
     *
29
     * @var Docker
30
     */
31
    protected $docker;
32
33
    /**
34
     * Logger to log message when building
35
     *
36
     * @var LoggerCallback
37
     */
38
    protected $logger;
39
40
    /**
41
     * @var boolean Use cache when building
42
     */
43
    private $usecache = true;
44
45
    /**
46
     * @var boolean Use cache when building
47
     */
48
    private $quietBuild = true;
49
50
    /**
51
     * @var integer Default timeout for run
52
     */
53
    private $timeout = 600;
54
55
    /**
56
     * @var string Base directory where builds are located
57
     */
58
    private $buildPath;
59
60
    public function __construct(LoggerCallback $logger, Docker $docker, $buildPath, $usecache = true, $quietBuild = true, $timeout = 600)
61
    {
62
        $this->logger     = $logger;
63
        $this->docker     = $docker;
64
        $this->usecache   = $usecache;
65
        $this->quietBuild = $quietBuild;
66
        $this->timeout    = $timeout;
67
        $this->buildPath  = $buildPath;
68
    }
69
70
    /**
71
     * Test a build
72
     *
73
     * @param Job $build
74
     * @param array|string $command
75
     *
76
     * @return integer
77
     */
78
    public function test(Job $build, $command = null)
79
    {
80
        $exitCode = 1;
81
82
        if (false !== $this->create($build)) {
83
            $exitCode = $this->run($build, $command);
0 ignored issues
show
Bug introduced by
It seems like $command defined by parameter $command on line 78 can also be of type null; however, Joli\JoliCi\Executor::run() does only seem to accept string|array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
84
        }
85
86
        return $exitCode;
87
    }
88
89
    /**
90
     * Create a build
91
     *
92
     * @param Job $job Build used to create image
93
     *
94
     * @return \Docker\API\Model\Image|boolean Return the image created if successful or false otherwise
95
     */
96
    public function create(Job $job)
97
    {
98
        $context  = new Context($this->buildPath . DIRECTORY_SEPARATOR . $job->getDirectory());
99
100
        $buildStream = $this->docker->getImageManager()->build($context->toStream(), [
101
            't' => $job->getName(),
102
            'q' => $this->quietBuild,
103
            'nocache' => !$this->usecache
104
        ], ImageManager::FETCH_STREAM);
105
106
        $buildStream->onFrame($this->logger->getBuildCallback());
107
        $buildStream->wait();
108
109
        try {
110
            return $this->docker->getImageManager()->find($job->getName());
111
        } catch (ClientErrorException $e) {
112
            if ($e->getResponse()->getStatusCode() == 404) {
113
                return false;
114
            }
115
116
            throw $e;
117
        }
118
    }
119
120
    /**
121
     * Run a build (it's suppose the image exist in docker
122
     *
123
     * @param Job $job Build to run
124
     * @param string|array $command Command to use when run the build (null, by default, will use the command registered to the image)
125
     *
126
     * @return integer The exit code of the command run inside (0 = success, otherwise it has failed)
127
     */
128
    public function run(Job $job, $command)
129
    {
130
        if (is_string($command)) {
131
            $command = ['/bin/bash', '-c', $command];
132
        }
133
134
        $image = $this->docker->getImageManager()->find($job->getName());
135
136
        $hostConfig = new HostConfig();
137
138
        $config = new ContainerConfig();
139
        $config->setCmd($command);
140
        $config->setImage($image->getId());
0 ignored issues
show
Bug introduced by
The method getId does only exist in Docker\API\Model\Image, but not in Psr\Http\Message\ResponseInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
141
        $config->setHostConfig($hostConfig);
142
        $config->setLabels(new \ArrayObject([
143
            'com.jolici.container=true'
144
        ]));
145
        $config->setAttachStderr(true);
146
        $config->setAttachStdout(true);
147
148
        $links = [];
149
150
        foreach ($job->getServices() as $service) {
151
            if ($service->getContainer()) {
152
                $serviceContainer = $this->docker->getContainerManager()->find($service->getContainer());
153
154
                $links[] = sprintf('%s:%s', $serviceContainer->getName(), $service->getName());
155
            }
156
        }
157
158
        $hostConfig->setLinks($links);
159
160
        $containerCreateResult = $this->docker->getContainerManager()->create($config);
161
        $attachStream = $this->docker->getContainerManager()->attach($containerCreateResult->getId(), [
0 ignored issues
show
Bug introduced by
The method getId does only exist in Docker\API\Model\ContainerCreateResult, but not in Psr\Http\Message\ResponseInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
162
            'stream' => true,
163
            'stdout' => true,
164
            'stderr' => true,
165
        ], ContainerManager::FETCH_STREAM);
166
167
        $attachStream->onStdout($this->logger->getRunStdoutCallback());
168
        $attachStream->onStderr($this->logger->getRunStderrCallback());
169
170
        $this->docker->getContainerManager()->start($containerCreateResult->getId());
171
172
        $attachStream->wait();
173
174
        $containerWait = $this->docker->getContainerManager()->wait($containerCreateResult->getId());
175
176
        return $containerWait->getStatusCode();
177
    }
178
}
179