Completed
Pull Request — master (#146)
by Vitaly
03:08
created

OpenStack   B

Complexity

Total Complexity 17

Size/Duplication

Total Lines 249
Duplicated Lines 5.22 %

Coupling/Cohesion

Components 1
Dependencies 16

Test Coverage

Coverage 44.12%

Importance

Changes 0
Metric Value
wmc 17
lcom 1
cbo 16
dl 13
loc 249
ccs 30
cts 68
cp 0.4412
rs 8.4614
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getPath() 0 4 1
A setup() 0 20 2
A validateConfig() 0 8 3
B sync() 0 29 4
A simulate() 13 13 1
A createCollector() 0 4 1
A getOrCreateContainer() 0 8 2
A getUploadPath() 0 4 2
A connect() 0 19 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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