sebastianfeldmann /
phpbu
| 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
Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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 |