Passed
Push — test ( ccd17d...b5a64e )
by Tom
02:39
created

Repository::resolve()   A

Complexity

Conditions 6
Paths 9

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 6
eloc 14
c 1
b 0
f 1
nc 9
nop 1
dl 0
loc 21
ccs 13
cts 13
cp 1
crap 6
rs 9.2222
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines\Runner\Docker\Binary;
6
7
use Ktomk\Pipelines\Cli\Exec;
8
use Ktomk\Pipelines\Lib;
9
use Ktomk\Pipelines\LibFs;
10
use Ktomk\Pipelines\Runner\Directories;
11
12
/**
13
 * Docker Binary Repository
14
 *
15
 * Docker provides a static binary which is useful to have in containers when
16
 * in the container itself docker should be available (e.g. services: - docker).
17
 *
18
 * It "comes" out of this binary repository. In the background there is a
19
 * BinaryUnPackager taking care of network interaction and file verification
20
 * as it is technically possible to have different kind of static docker
21
 * binaries also from different packages.
22
 *
23
 * Also there is a YAML package file reader which can read package information
24
 * out of such files.
25
 *
26
 * @package Ktomk\Pipelines\Runner\Docker
27
 */
28
class Repository implements PackageInterface
29
{
30
    /**
31
     * common package names
32
     *
33
     * the test docker client is a fake / stub
34
     * the previous docker client was in use in the "self-install" example lib/pipelines/docker-client-install.sh
35
     * the integrate docker client is with #1019 --env-file pr which pipelines benefit from
36
     */
37
    const PKG_TEST = 'docker-42.42.1-binsh-test-stub';
38
    const PKG_PREVIOUS = 'docker-17.12.0-ce-linux-static-x86_64';
39
    const PKG_INTEGRATE = 'docker-19.03.1-linux-static-x86_64';
40
41
    /**
42
     * @var Exec
43
     */
44
    private $exec;
45
46
    /**
47
     * @var null|array package definition data
48
     */
49
    private $package;
50
51
    /**
52
     * @var null|UnPackager
53
     */
54
    private $unPackager;
55
56
    /**
57
     * Static factory method.
58
     *
59
     * @param Exec $exec
60
     * @param Directories $directories (UnPackager dependency)
61
     *
62
     * @return Repository
63
     */
64 1
    public static function create(Exec $exec, Directories $directories)
65
    {
66 1
        $unPackager = UnPackager::fromDirectories($exec, $directories);
67
68 1
        return new self($exec, array(), $unPackager);
69
    }
70
71
    /**
72
     * Repository constructor.
73
     *
74
     * @param Exec $exec
75
     * @param array $package [optional - on removal]
76
     * @param UnPackager $unPackager
77
     */
78 2
    public function __construct(Exec $exec, array $package, UnPackager $unPackager)
79
    {
80 2
        $this->exec = $exec;
81 2
        $this->package = $package;
82 2
        $this->unPackager = $unPackager;
83 2
    }
84
85
    /**
86
     * provision docker client into a running container
87
     *
88
     * install a binary as /usr/bin/docker and make it executable.
89
     * show the version.
90
     *
91
     * @param string $containerId
92
     * @param string $path to static docker client binary
93
     *
94
     * @return array array(int $status, string $message) docker client binary version (docker --version) or error
95
     */
96 1
    public function containerProvisionDockerClientBinary($containerId, $path)
97
    {
98 1
        $status = $this->exec->capture(
99 1
            sprintf('2>&1 < %s docker', lib::quoteArg($path)),
100 1
            array('exec', '-i', $containerId, '/bin/sh', '-c', 'mkdir -p /usr/bin && cat - > /usr/bin/docker; chmod +x /usr/bin/docker; docker --version'),
101 1
            $out
102
        );
103
104 1
        return array($status, (string)$out);
105
    }
106
107
    /**
108
     * list all available packages in the repository (built-in)
109
     *
110
     * @return array|string[]
111
     */
112 1
    public function listPackages()
113
    {
114 1
        $list = array();
115
116 1
        $packageDir = LibFs::normalizePath(__DIR__ . '/../../../../lib/package');
117
118 1
        $regex = new \RegexIterator(
119 1
            new \FilesystemIterator($packageDir),
120 1
            '(^(.*)\.yml$)'
121
        );
122
123 1
        foreach ($regex as $item) {
124 1
            $list[] = $item->getBasename('.yml');
125
        }
126
127 1
        sort($list, SORT_NATURAL);
128
129 1
        return $list;
130
    }
131
132
    /**
133
     * Resolve a binary package name in this repository
134
     *
135
     * @param string $packageName
136
     *
137
     * @throws \InvalidArgumentException
138
     * @return Repository
139
     */
140 8
    public function resolve($packageName)
141
    {
142 8
        if ('yml' === pathinfo($packageName, PATHINFO_EXTENSION)) {
143 1
            $ymlFile = $packageName;
144 7
        } elseif (false !== strpos($packageName, '/') && LibFs::isReadableFile($packageName)) {
145 2
            $localBinary = $packageName;
146
        } else {
147 5
            $packageDir = __DIR__ . '/../../../../lib/package';
148 5
            $ymlFile = sprintf('%s/%s.yml', $packageDir, $packageName);
149
        }
150
151 8
        if (isset($ymlFile)) {
152 6
            $reader = new PackageYamlFileReader($ymlFile);
153 6
            $packageArray = $reader->asPackageArray();
154
        } else {
155 2
            $packageArray = array('prep' => array('bin_local' => isset($localBinary) ? $localBinary : null));
156
        }
157
158 7
        $this->package = $packageArray;
159
160 7
        return $this;
161
    }
162
163
    /**
164
     * @param string $containerId
165
     *
166
     * @throws \InvalidArgumentException
167
     * @return array array(int $status, string $message) docker client binary version (docker --version) or error
168
     */
169 1
    public function inject($containerId)
170
    {
171 1
        $package = $this->asPackageArray();
172 1
        $localBinary = $this->getLocalBinary($package);
173
174 1
        return $this->containerProvisionDockerClientBinary($containerId, $localBinary);
175
    }
176
177
    /**
178
     * Inject binary docker client package by name into container
179
     *
180
     * @param string $name
181
     * @param string $containerId
182
     */
183 1
    public function injectPackage($name, $containerId)
184
    {
185 1
        $this->resolve($name);
186 1
        $this->inject($containerId);
187 1
    }
188
189
    /**
190
     * Get binary path from local store.
191
     *
192
     * @param array $package
193
     *
194
     * @return string
195
     */
196 2
    public function getLocalBinary(array $package)
197
    {
198 2
        if (isset($package['prep']['bin_local'])) {
199 1
            return $package['prep']['bin_local'];
200
        }
201
202 1
        return $this->unPackager->getLocalBinary($package);
0 ignored issues
show
Bug introduced by
The method getLocalBinary() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

202
        return $this->unPackager->/** @scrutinizer ignore-call */ getLocalBinary($package);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
203
    }
204
205
    /**
206
     * @inheritDoc
207
     * @throws \InvalidArgumentException
208
     */
209 6
    public function asPackageArray()
210
    {
211 6
        $package = $this->package;
212 6
        if (!$package) {
213 1
            $package = $this->resolve(self::PKG_INTEGRATE)->package;
214
        }
215
216 6
        return $package;
217
    }
218
}
219