Test Failed
Push — test ( df0fbb...ffd126 )
by Tom
02:42
created

Repository::getPackageLocalBinary()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

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