Completed
Pull Request — master (#140)
by Vitaly
04:25
created

Openstack::setup()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4.0047

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 14
cts 15
cp 0.9333
rs 8.9197
c 0
b 0
f 0
cc 4
eloc 14
nc 4
nop 1
crap 4.0047
1
<?php
2
namespace phpbu\App\Backup\Sync;
3
4
use GuzzleHttp\Psr7\Stream;
5
use OpenStack\ObjectStore\v1\Models\Container;
6
use OpenStack\ObjectStore\v1\Service as ObjectStoreService;
7
use GuzzleHttp\Client;
8
use OpenStack\Common\Transport\HandlerStack;
9
use OpenStack\Common\Transport\Utils;
10
use OpenStack\Identity\v2\Service;
11
use phpbu\App\Backup\Collector;
12
use phpbu\App\Backup\Target;
13
use phpbu\App\Result;
14
use phpbu\App\Util\Arr;
15
use phpbu\App\Util\Str;
16
17
/**
18
 * OpenStack Swift Sync
19
 *
20
 * @package    phpbu
21
 * @subpackage Backup
22
 * @author     Vitaly Baev <[email protected]>
23
 * @author     Sebastian Feldmann <[email protected]>
24
 * @copyright  Sebastian Feldmann <[email protected]>
25
 * @license    https://opensource.org/licenses/MIT The MIT License (MIT)
26
 * @link       http://phpbu.de/
27
 * @since      Class available since Release 5.1
28
 */
29
class Openstack implements Simulator
30
{
31
    use Clearable;
32
33
    /**
34
     * OpenStack identify url
35
     *
36
     * @var string
37
     */
38
    protected $authUrl;
39
40
    /**
41
     * OpenStack region
42
     *
43
     * @var string
44
     */
45
    protected $region;
46
47
    /**
48
     * @var string
49
     */
50
    protected $username;
51
52
    /**
53
     * @var string
54
     */
55
    protected $password;
56
57
    /**
58
     * Object Store container name
59
     *
60
     * @var string
61
     */
62
    protected $containerName;
63
64
    /**
65
     * OpenStack service name
66
     *
67
     * @var string
68
     */
69
    protected $serviceName;
70
71
    /**
72
     * Max stream upload size, files over this size have to be uploaded as Dynamic Large Objects
73
     *
74
     * @var int
75
     */
76
    protected $maxStreamUploadSize = 5368709120;
77
78
    /**
79
     * Path where to copy the backup.
80
     *
81
     * @var string
82
     */
83
    protected $path = '';
84
85
    /**
86
     * @var Container
87
     */
88
    protected $container;
89
90
    /**
91
     * @return string
92
     */
93
    public function getPath(): string
94
    {
95
        return $this->path;
96
    }
97
98
    /**
99
     * (non-PHPDoc)
100
     *
101
     * @see    \phpbu\App\Backup\Sync::setup()
102
     * @param  array $config
103
     * @throws \phpbu\App\Backup\Sync\Exception
104
     * @throws \phpbu\App\Exception
105
     */
106 9
    public function setup(array $config)
107
    {
108 9
        if (!class_exists('\\OpenStack\\OpenStack')) {
109
            throw new Exception('OpeStack SDK not loaded: use composer to install "php-opencloud/openstack"');
110
        }
111
112
        // check for mandatory options
113 9
        $this->validateConfig($config);
114
115 4
        $this->authUrl       = $config['auth_url'];
116 4
        $this->region        = $config['region'];
117 4
        $this->username      = $config['username'];
118 4
        $this->password      = $config['password'];
119 4
        $this->containerName = $config['container_name'];
120 4
        $this->serviceName   = Arr::getValue($config, 'service_name', 'swift');
121 4
        if (Arr::getValue($config, 'path')) {
122 1
            $this->path = Str::withTrailingSlash(Str::replaceDatePlaceholders($config['path']));
123 1
            $this->path = substr($this->path, 0, 1) == '/' ? substr($this->path, 1) : $this->path;
124
        }
125
126 4
        $this->setUpClearable($config);
127 4
    }
128
129
    /**
130
     * Make sure all mandatory keys are present in given config.
131
     *
132
     * @param  array $config
133
     * @throws \phpbu\App\Backup\Sync\Exception
134
     */
135 9
    protected function validateConfig(array $config)
136
    {
137 9
        foreach (['auth_url', 'region', 'username', 'password', 'container_name'] as $option) {
138 9
            if (!Arr::isSetAndNotEmptyString($config, $option)) {
139 9
                throw new Exception($option . ' is mandatory');
140
            }
141
        }
142 4
    }
143
144
    /**
145
     * Execute the sync.
146
     *
147
     * @see    \phpbu\App\Backup\Sync::sync()
148
     * @param  \phpbu\App\Backup\Target $target
149
     * @param  \phpbu\App\Result        $result
150
     * @throws \phpbu\App\Backup\Sync\Exception
151
     * @throws \OpenStack\Common\Error\BadResponseError
152
     */
153
    public function sync(Target $target, Result $result)
154
    {
155
        if (!$this->container) {
156
            $this->connect($result);
157
        }
158
159
        try {
160
            if ($target->getSize() > $this->maxStreamUploadSize) {
161
                // use Dynamic Large Objects
162
                $uploadOptions = [
163
                    'name'   => $this->getUploadPath($target),
164
                    'stream' => new Stream(fopen($target->getPathname(), 'r')),
165
                ];
166
                $this->container->createLargeObject($uploadOptions);
167
            } else {
168
                // create an object
169
                $uploadOptions = [
170
                    'name' => $this->getUploadPath($target),
171
                    'content' => file_get_contents($target->getPathname()),
172
                ];
173
                $this->container->createObject($uploadOptions);
174
            }
175
        } catch (\Exception $e) {
176
            throw new Exception($e->getMessage(), null, $e);
177
        }
178
        // run remote cleanup
179
        $this->cleanup($target, $result);
180
        $result->debug('upload: done');
181
    }
182
183
    /**
184
     * Simulate the sync execution.
185
     *
186
     * @param \phpbu\App\Backup\Target $target
187
     * @param \phpbu\App\Result        $result
188
     */
189 1
    public function simulate(Target $target, Result $result)
190
    {
191 1
        $result->debug(
192 1
            'sync backup to OpenStack' . PHP_EOL
193 1
            . '  region:   ' . $this->region . PHP_EOL
194 1
            . '  key:      ' . $this->username . PHP_EOL
195 1
            . '  password:    ********' . PHP_EOL
196 1
            . '  container: ' . $this->containerName
197 1
            . '  path: "' . $this->path . '"' . PHP_EOL
198
        );
199
200 1
        $this->simulateRemoteCleanup($target, $result);
201 1
    }
202
203
    /**
204
     * Creates collector for OpenStack
205
     *
206
     * @param \phpbu\App\Backup\Target $target
207
     * @return \phpbu\App\Backup\Collector
208
     */
209
    protected function createCollector(Target $target): Collector
210
    {
211
        return new \phpbu\App\Backup\Collector\OpenStack($target, $this->container, $this->path);
212
    }
213
214
    /**
215
     * @param ObjectStoreService $service
216
     * @param Result             $result
217
     * @return \OpenStack\ObjectStore\v1\Models\Container
218
     * @throws \OpenStack\Common\Error\BadResponseError
219
     */
220
    protected function getOrCreateContainer(ObjectStoreService $service, Result $result)
221
    {
222
        if (!$service->containerExists($this->containerName)) {
223
            $result->debug('create container');
224
            return $service->createContainer(['name' => $this->containerName]);
225
        }
226
        return $service->getContainer($this->containerName);
227
    }
228
229
    /**
230
     * Get the upload path
231
     *
232
     * @param \phpbu\App\Backup\Target $target
233
     * @return string
234
     */
235 2
    public function getUploadPath(Target $target)
236
    {
237 2
        return $this->path . $target->getFilename();
238
    }
239
240
    /**
241
     * @param Result $result
242
     * @return void
243
     * @throws \OpenStack\Common\Error\BadResponseError
244
     */
245
    protected function connect(Result $result)
246
    {
247
        $httpClient = new Client([
248
            'base_uri' => Utils::normalizeUrl($this->authUrl),
249
            'handler' => HandlerStack::create(),
250
        ]);
251
252
        $options = [
253
            'authUrl' => $this->authUrl,
254
            'region' => $this->region,
255
            'username' => $this->username,
256
            'password' => $this->password,
257
            'identityService' => Service::factory($httpClient),
258
        ];
259
260
        $openStack = new \OpenStack\OpenStack($options);
261
        $objectStoreService = $openStack->objectStoreV1(['catalogName' => $this->serviceName]);
262
        $this->container = $this->getOrCreateContainer($objectStoreService, $result);
263
    }
264
}
265