Completed
Pull Request — master (#140)
by
unknown
05:09
created

Openstack::sync()   B

Complexity

Conditions 4
Paths 14

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

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