Passed
Push — master ( 9e5fe0...88d9d6 )
by Tom
04:35
created

Repository::getPackageAsArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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