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; |
||||
0 ignored issues
–
show
introduced
by
![]() |
|||||
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( |
|||
137 | Util\Path::withoutLeadingSlash(Util\Arr::getValue($config, 'path', '')), |
||||
138 | 4 | $this->time |
|||
139 | 4 | ); |
|||
140 | |||||
141 | $this->setUpCleanable($config); |
||||
142 | } |
||||
143 | |||||
144 | /** |
||||
145 | * Make sure all mandatory keys are present in given config. |
||||
146 | * |
||||
147 | * @param array $config |
||||
148 | 9 | * @param array $keys |
|||
149 | * @throws Exception |
||||
150 | 9 | */ |
|||
151 | 9 | protected function validateConfig(array $config, array $keys) |
|||
152 | 9 | { |
|||
153 | foreach ($keys as $option) { |
||||
154 | if (!Util\Arr::isSetAndNotEmptyString($config, $option)) { |
||||
155 | 4 | throw new Exception($option . ' is mandatory'); |
|||
156 | } |
||||
157 | } |
||||
158 | } |
||||
159 | |||||
160 | /** |
||||
161 | * Execute the sync. |
||||
162 | * |
||||
163 | * @see \phpbu\App\Backup\Sync::sync() |
||||
164 | * @param \phpbu\App\Backup\Target $target |
||||
165 | * @param \phpbu\App\Result $result |
||||
166 | * @throws \phpbu\App\Backup\Sync\Exception |
||||
167 | * @throws \OpenStack\Common\Error\BadResponseError |
||||
168 | */ |
||||
169 | public function sync(Target $target, Result $result) |
||||
170 | { |
||||
171 | if (!$this->container) { |
||||
172 | $this->connect($result); |
||||
173 | } |
||||
174 | |||||
175 | try { |
||||
176 | if ($target->getSize() > $this->maxStreamUploadSize) { |
||||
177 | // use Dynamic Large Objects |
||||
178 | $uploadOptions = [ |
||||
179 | 'name' => $this->getUploadPath($target), |
||||
180 | 'stream' => new Stream(fopen($target->getPathname(), 'r')), |
||||
0 ignored issues
–
show
It seems like
fopen($target->getPathname(), 'r') can also be of type false ; however, parameter $stream of GuzzleHttp\Psr7\Stream::__construct() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
181 | ]; |
||||
182 | $this->container->createLargeObject($uploadOptions); |
||||
183 | } else { |
||||
184 | // create an object |
||||
185 | $uploadOptions = [ |
||||
186 | 'name' => $this->getUploadPath($target), |
||||
187 | 'content' => file_get_contents($target->getPathname()), |
||||
188 | ]; |
||||
189 | $this->container->createObject($uploadOptions); |
||||
190 | } |
||||
191 | } catch (\Exception $e) { |
||||
192 | throw new Exception($e->getMessage(), null, $e); |
||||
193 | } |
||||
194 | // run remote cleanup |
||||
195 | $this->cleanup($target, $result); |
||||
196 | $result->debug('upload: done'); |
||||
197 | } |
||||
198 | |||||
199 | /** |
||||
200 | * Simulate the sync execution. |
||||
201 | * |
||||
202 | 1 | * @param \phpbu\App\Backup\Target $target |
|||
203 | * @param \phpbu\App\Result $result |
||||
204 | 1 | */ |
|||
205 | 1 | public function simulate(Target $target, Result $result) |
|||
206 | 1 | { |
|||
207 | 1 | $result->debug( |
|||
208 | 1 | 'sync backup to OpenStack' . PHP_EOL |
|||
209 | 1 | . ' region: ' . $this->region . PHP_EOL |
|||
210 | 1 | . ' key: ' . $this->username . PHP_EOL |
|||
211 | . ' password: ********' . PHP_EOL |
||||
212 | . ' container: ' . $this->containerName |
||||
213 | 1 | . ' path: "' . $this->path->getPath() . '"' . PHP_EOL |
|||
214 | 1 | ); |
|||
215 | |||||
216 | $this->simulateRemoteCleanup($target, $result); |
||||
217 | } |
||||
218 | |||||
219 | /** |
||||
220 | * Creates collector for OpenStack. |
||||
221 | * |
||||
222 | * @param \phpbu\App\Backup\Target $target |
||||
223 | * @return \phpbu\App\Backup\Collector |
||||
224 | */ |
||||
225 | protected function createCollector(Target $target): Collector |
||||
226 | { |
||||
227 | return new Collector\OpenStack($target, $this->path, $this->container); |
||||
228 | } |
||||
229 | |||||
230 | /** |
||||
231 | * @param \OpenStack\ObjectStore\v1\Service $service |
||||
232 | * @param \phpbu\App\Result $result |
||||
233 | * @return \OpenStack\ObjectStore\v1\Models\Container |
||||
234 | * @throws \OpenStack\Common\Error\BadResponseError |
||||
235 | */ |
||||
236 | protected function getOrCreateContainer(ObjectStoreService $service, Result $result) |
||||
237 | { |
||||
238 | if (!$service->containerExists($this->containerName)) { |
||||
239 | $result->debug('create container'); |
||||
240 | return $service->createContainer(['name' => $this->containerName]); |
||||
241 | } |
||||
242 | return $service->getContainer($this->containerName); |
||||
243 | } |
||||
244 | |||||
245 | /** |
||||
246 | * Get the upload path |
||||
247 | * |
||||
248 | 2 | * @param \phpbu\App\Backup\Target $target |
|||
249 | * @return string |
||||
250 | 2 | */ |
|||
251 | public function getUploadPath(Target $target) |
||||
252 | { |
||||
253 | return (!empty($this->path->getPath()) ? $this->path->getPath() . '/' : '') . $target->getFilename(); |
||||
254 | } |
||||
255 | |||||
256 | /** |
||||
257 | * @param \phpbu\App\Result $result |
||||
258 | * @return void |
||||
259 | * @throws \OpenStack\Common\Error\BadResponseError |
||||
260 | */ |
||||
261 | protected function connect(Result $result) |
||||
262 | { |
||||
263 | $httpClient = new Client([ |
||||
264 | 'base_uri' => Utils::normalizeUrl($this->authUrl), |
||||
265 | 'handler' => HandlerStack::create(), |
||||
266 | ]); |
||||
267 | |||||
268 | $options = [ |
||||
269 | 'authUrl' => $this->authUrl, |
||||
270 | 'region' => $this->region, |
||||
271 | 'username' => $this->username, |
||||
272 | 'password' => $this->password, |
||||
273 | 'identityService' => Service::factory($httpClient), |
||||
274 | ]; |
||||
275 | |||||
276 | $openStack = new \OpenStack\OpenStack($options); |
||||
277 | $objectStoreService = $openStack->objectStoreV1(['catalogName' => $this->serviceName]); |
||||
278 | $this->container = $this->getOrCreateContainer($objectStoreService, $result); |
||||
279 | } |
||||
280 | } |
||||
281 |