Completed
Push — master ( 72cf29...ecec76 )
by Sebastian
03:13
created

Openstack::createCollector()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 2
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;
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 without leading or trailing slashes.
79
     *
80
     * @var string
81
     */
82
    protected $path = '';
83
84
    /**
85
     * Path where to copy the backup still containing possible date placeholders.
86
     *
87
     * @var string
88
     */
89
    protected $pathRaw = '';
90
91
    /**
92
     * @var Container
93
     */
94
    protected $container;
95
96
    /**
97
     * @return string
98
     */
99
    public function getPath(): string
100
    {
101
        return $this->path;
102
    }
103
104
    /**
105
     * (non-PHPDoc)
106
     *
107
     * @see    \phpbu\App\Backup\Sync::setup()
108
     * @param  array $config
109
     * @throws \phpbu\App\Backup\Sync\Exception
110
     * @throws \phpbu\App\Exception
111
     */
112 9
    public function setup(array $config)
113
    {
114 9
        if (!class_exists('\\OpenStack\\OpenStack')) {
115
            throw new Exception('OpeStack SDK not loaded: use composer to install "php-opencloud/openstack"');
116
        }
117
118
        // check for mandatory options
119 9
        $this->validateConfig($config, ['auth_url', 'region', 'username', 'password', 'container_name']);
120
121 4
        $this->authUrl       = $config['auth_url'];
122 4
        $this->region        = $config['region'];
123 4
        $this->username      = $config['username'];
124 4
        $this->password      = $config['password'];
125 4
        $this->containerName = $config['container_name'];
126 4
        $this->serviceName   = Util\Arr::getValue($config, 'service_name', 'swift');
127 4
        $clearedPath         = Util\Path::withoutLeadingOrTrailingSlash(Util\Arr::getValue($config, 'path', ''));
128
129 4
        if (!empty($clearedPath)) {
130 1
            $this->path    = Util\Path::replaceDatePlaceholders($clearedPath);
131 1
            $this->pathRaw = $clearedPath;
132
        }
133
134 4
        $this->setUpClearable($config);
135 4
    }
136
137
    /**
138
     * Make sure all mandatory keys are present in given config.
139
     *
140
     * @param  array $config
141
     * @param  array $keys
142
     * @throws Exception
143
     */
144 9
    protected function validateConfig(array $config, array $keys)
145
    {
146 9
        foreach ($keys as $option) {
147 9
            if (!Util\Arr::isSetAndNotEmptyString($config, $option)) {
148 9
                throw new Exception($option . ' is mandatory');
149
            }
150
        }
151 4
    }
152
153
    /**
154
     * Execute the sync.
155
     *
156
     * @see    \phpbu\App\Backup\Sync::sync()
157
     * @param  \phpbu\App\Backup\Target $target
158
     * @param  \phpbu\App\Result        $result
159
     * @throws \phpbu\App\Backup\Sync\Exception
160
     * @throws \OpenStack\Common\Error\BadResponseError
161
     */
162
    public function sync(Target $target, Result $result)
163
    {
164
        if (!$this->container) {
165
            $this->connect($result);
166
        }
167
168
        try {
169
            if ($target->getSize() > $this->maxStreamUploadSize) {
170
                // use Dynamic Large Objects
171
                $uploadOptions = [
172
                    'name'   => $this->getUploadPath($target),
173
                    'stream' => new Stream(fopen($target->getPathname(), 'r')),
174
                ];
175
                $this->container->createLargeObject($uploadOptions);
176
            } else {
177
                // create an object
178
                $uploadOptions = [
179
                    'name'    => $this->getUploadPath($target),
180
                    'content' => file_get_contents($target->getPathname()),
181
                ];
182
                $this->container->createObject($uploadOptions);
183
            }
184
        } catch (\Exception $e) {
185
            throw new Exception($e->getMessage(), null, $e);
186
        }
187
        // run remote cleanup
188
        $this->cleanup($target, $result);
189
        $result->debug('upload: done');
190
    }
191
192
    /**
193
     * Simulate the sync execution.
194
     *
195
     * @param \phpbu\App\Backup\Target $target
196
     * @param \phpbu\App\Result        $result
197
     */
198 1
    public function simulate(Target $target, Result $result)
199
    {
200 1
        $result->debug(
201 1
            'sync backup to OpenStack' . PHP_EOL
202 1
            . '  region:   ' . $this->region . PHP_EOL
203 1
            . '  key:      ' . $this->username . PHP_EOL
204 1
            . '  password:    ********' . PHP_EOL
205 1
            . '  container: ' . $this->containerName
206 1
            . '  path: "' . $this->path . '"' . PHP_EOL
207
        );
208
209 1
        $this->simulateRemoteCleanup($target, $result);
210 1
    }
211
212
    /**
213
     * Creates collector for OpenStack.
214
     *
215
     * @param  \phpbu\App\Backup\Target $target
216
     * @return \phpbu\App\Backup\Collector
217
     */
218
    protected function createCollector(Target $target): Collector
219
    {
220
        return new Collector\OpenStack($target, $this->container, $this->path);
221
    }
222
223
    /**
224
     * @param  \OpenStack\ObjectStore\v1\Service $service
225
     * @param  \phpbu\App\Result                 $result
226
     * @return \OpenStack\ObjectStore\v1\Models\Container
227
     * @throws \OpenStack\Common\Error\BadResponseError
228
     */
229
    protected function getOrCreateContainer(ObjectStoreService $service, Result $result)
230
    {
231
        if (!$service->containerExists($this->containerName)) {
232
            $result->debug('create container');
233
            return $service->createContainer(['name' => $this->containerName]);
234
        }
235
        return $service->getContainer($this->containerName);
236
    }
237
238
    /**
239
     * Get the upload path
240
     *
241
     * @param \phpbu\App\Backup\Target $target
242
     * @return string
243
     */
244 2
    public function getUploadPath(Target $target)
245
    {
246 2
        return (!empty($this->path) ? $this->path . '/' : '') . $target->getFilename();
247
    }
248
249
    /**
250
     * @param  \phpbu\App\Result $result
251
     * @return void
252
     * @throws \OpenStack\Common\Error\BadResponseError
253
     */
254
    protected function connect(Result $result)
255
    {
256
        $httpClient = new Client([
257
            'base_uri' => Utils::normalizeUrl($this->authUrl),
258
            'handler'  => HandlerStack::create(),
259
        ]);
260
261
        $options = [
262
            'authUrl'         => $this->authUrl,
263
            'region'          => $this->region,
264
            'username'        => $this->username,
265
            'password'        => $this->password,
266
            'identityService' => Service::factory($httpClient),
267
        ];
268
269
        $openStack = new \OpenStack\OpenStack($options);
270
        $objectStoreService = $openStack->objectStoreV1(['catalogName' => $this->serviceName]);
271
        $this->container = $this->getOrCreateContainer($objectStoreService, $result);
272
    }
273
}
274