Repository   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 221
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 19
eloc 60
c 1
b 0
f 1
dl 0
loc 221
ccs 55
cts 55
cp 1
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A asPackageArray() 0 8 2
A resolve() 0 21 6
A getPackageAsArray() 0 3 1
A containerProvisionDockerClientBinary() 0 12 1
A __construct() 0 5 1
A getPackageLocalBinary() 0 7 2
A inject() 0 6 1
A getBinaryPath() 0 4 1
A listPackages() 0 18 2
A injectPackage() 0 4 1
A create() 0 5 1
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
     * the pushall docker client is for docker push --all-tags (since docker 2.10.0)
38
     */
39
    const PKG_TEST = 'docker-42.42.1-binsh-test-stub';
40
    const PKG_PREVIOUS = 'docker-17.12.0-ce-linux-static-x86_64';
41
    const PKG_ATLBBCPP = 'docker-18.09.1-linux-static-x86_64';
42
    const PKG_INTEGRATE = 'docker-19.03.1-linux-static-x86_64';
43
    const PKG_LAST_INTEGRATE = 'docker-19.03.15-linux-static-x86_64';
44
    const PKG_PUSHALL = 'docker-20.10.24-linux-static-x86_64';
45
46
    /**
47
     * @var Exec
48
     */
49
    private $exec;
50
51
    /**
52
     * @var null|array package definition data
53
     */
54
    private $package;
55
56
    /**
57
     * @var UnPackager
58
     */
59
    private $unPackager;
60
61
    /**
62
     * Static factory method.
63
     *
64
     * @param Exec $exec
65
     * @param Directories $directories (UnPackager dependency)
66
     *
67
     * @return Repository
68
     */
69 1
    public static function create(Exec $exec, Directories $directories)
70
    {
71 1
        $unPackager = UnPackager::fromDirectories($exec, $directories);
72
73 1
        return new self($exec, array(), $unPackager);
74
    }
75
76
    /**
77
     * Repository constructor.
78
     *
79
     * @param Exec $exec
80
     * @param array $package [optional - on removal]
81
     * @param UnPackager $unPackager
82
     */
83 2
    public function __construct(Exec $exec, array $package, UnPackager $unPackager)
84
    {
85 2
        $this->exec = $exec;
86 2
        $this->package = $package;
87 2
        $this->unPackager = $unPackager;
88
    }
89
90
    /**
91
     * provision docker client into a running container
92
     *
93
     * install a binary as /usr/bin/docker and make it executable.
94
     * show the version.
95
     *
96
     * @param string $containerId
97
     * @param string $path to static docker client binary
98
     *
99
     * @return array array(int $status, string $message) docker client binary version (docker --version) or error
100
     */
101 1
    public function containerProvisionDockerClientBinary($containerId, $path)
102
    {
103 1
        $status = $this->exec->capture(
104 1
            sprintf('2>&1 < %s docker', lib::quoteArg($path)),
105
            array(
106 1
                'exec', '-i', $containerId, '/bin/sh', '-c',
107
                'mkdir -p /usr/bin && cat - > /usr/bin/docker; chmod +x /usr/bin/docker; docker --version',
108
            ),
109
            $out
110
        );
111
112 1
        return array($status, (string)$out);
113
    }
114
115
    /**
116
     * list all available packages in the repository (built-in)
117
     *
118
     * @return array|string[]
119
     */
120 1
    public function listPackages()
121
    {
122 1
        $list = array();
123
124 1
        $packageDir = LibFsPath::normalize(__DIR__ . '/../../../../lib/package');
125
126 1
        $regex = new \RegexIterator(
127 1
            new \FilesystemIterator($packageDir),
128
            '(^(.*)\.yml$)'
129
        );
130
131 1
        foreach ($regex as $item) {
132 1
            $list[] = $item->getBasename('.yml');
133
        }
134
135 1
        sort($list, SORT_STRING);
136
137 1
        return $list;
138
    }
139
140
    /**
141
     * Resolve a binary package name in this repository
142
     *
143
     * @param string $packageName
144
     *
145
     * @throws \InvalidArgumentException
146
     *
147
     * @return Repository
148
     */
149 9
    public function resolve($packageName)
150
    {
151 9
        if ('yml' === pathinfo($packageName, PATHINFO_EXTENSION)) {
152 1
            $ymlFile = $packageName;
153 8
        } elseif (false !== strpos($packageName, '/') && LibFs::isReadableFile($packageName)) {
154 2
            $localBinary = $packageName;
155
        } else {
156 6
            $packageDir = __DIR__ . '/../../../../lib/package';
157 6
            $ymlFile = sprintf('%s/%s.yml', $packageDir, $packageName);
158
        }
159
160 9
        if (isset($ymlFile)) {
161 7
            $reader = new PackageYamlFileReader($ymlFile);
162 7
            $packageArray = $reader->asPackageArray();
163
        } else {
164 2
            $packageArray = array('prep' => array('bin_local' => isset($localBinary) ? $localBinary : null));
165
        }
166
167 8
        $this->package = $packageArray;
168
169 8
        return $this;
170
    }
171
172
    /**
173
     * @param string $containerId
174
     *
175
     * @throws \InvalidArgumentException
176
     *
177
     * @return array array(int $status, string $message) docker client binary version (docker --version) or error
178
     */
179 1
    public function inject($containerId)
180
    {
181 1
        $package = $this->asPackageArray();
182 1
        $localBinary = $this->getPackageLocalBinary($package);
183
184 1
        return $this->containerProvisionDockerClientBinary($containerId, $localBinary);
185
    }
186
187
    /**
188
     * Inject binary docker client package by name into container
189
     *
190
     * @param string $name
191
     * @param string $containerId
192
     *
193
     * @return void
194
     */
195 1
    public function injectPackage($name, $containerId)
196
    {
197 1
        $this->resolve($name);
198 1
        $this->inject($containerId);
199
    }
200
201
    /**
202
     * path to binary (unpacked)
203
     *
204
     * @return string path of unpacked local binary
205
     */
206 1
    public function getBinaryPath()
207
    {
208 1
        return $this->getPackageLocalBinary(
209 1
            $this->asPackageArray()
210
        );
211
    }
212
213
    /**
214
     * Get binary path from local store.
215
     *
216
     * @param array $package
217
     *
218
     * @return string
219
     */
220 2
    public function getPackageLocalBinary(array $package)
221
    {
222 2
        if (isset($package['prep']['bin_local'])) {
223 1
            return $package['prep']['bin_local'];
224
        }
225
226 1
        return $this->unPackager->getLocalBinary($package);
227
    }
228
229
    /**
230
     * @inheritDoc
231
     *
232
     * @throws \InvalidArgumentException
233
     */
234 7
    public function asPackageArray()
235
    {
236 7
        $package = $this->package;
237 7
        if (!$package) {
238 2
            $package = $this->resolve(self::PKG_PUSHALL)->getPackageAsArray();
239
        }
240
241 7
        return $package;
242
    }
243
244
    /**
245
     * @return array
246
     */
247 2
    private function getPackageAsArray()
248
    {
249 2
        return (array)$this->package;
250
    }
251
}
252