-
-
Notifications
You must be signed in to change notification settings - Fork 109
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #382 from ddattee
Feat/Google Cloud Storage sync
- Loading branch information
Showing
12 changed files
with
744 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"type": "googlecloudstorage", | ||
"options": { | ||
"secret": "backup/google/client_secret.json", | ||
"bucket": "my-bucket", | ||
"path": "remote/path" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<sync type="googlecloudstorage"> | ||
<!-- google secret file --> | ||
<option name="secret" value="backup/google/client_secret.json"/> | ||
|
||
<!-- bucket name to upload to --> | ||
<option name="bucket" value="backup/google/client_secret.json"/> | ||
|
||
<!-- the remote path to upload to into the bucket --> | ||
<option name="path" value="remote/path"/> | ||
|
||
<!-- optional cleanup configuration --> | ||
<option name="cleanup.type" value="quantity"/> | ||
<option name="cleanup.amount" value="10"/> | ||
</sync> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -278,6 +278,7 @@ | |
"Dropbox", | ||
"Ftp", | ||
"GoogleDrive", | ||
"GoogleCloudStorage", | ||
"RSync", | ||
"Sftp", | ||
"Softlayer", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
|
||
namespace phpbu\App\Backup\Collector; | ||
|
||
use Google\Cloud\Storage\Bucket; | ||
use Google\Cloud\Storage\StorageObject; | ||
use phpbu\App\Backup\Collector; | ||
use phpbu\App\Backup\File; | ||
use phpbu\App\Backup\Path; | ||
use phpbu\App\Backup\Target; | ||
|
||
/** | ||
* GoogleCloud class. | ||
* | ||
* @package phpbu | ||
* @subpackage Backup | ||
* @author David Dattee <[email protected]> | ||
* @copyright Sebastian Feldmann <[email protected]> | ||
* @license https://opensource.org/licenses/MIT The MIT License (MIT) | ||
* @link http://phpbu.de/ | ||
*/ | ||
class GoogleCloudStorage extends Remote implements Collector | ||
{ | ||
/** | ||
* Bucket to read from. | ||
* | ||
* @var Bucket | ||
*/ | ||
private Bucket $bucket; | ||
|
||
/** | ||
* @param Target $target | ||
* @param Bucket $bucket | ||
* @param Path $path | ||
*/ | ||
public function __construct(Target $target, Bucket $bucket, Path $path) | ||
{ | ||
$this->setUp($target, $path); | ||
$this->bucket = $bucket; | ||
} | ||
|
||
/** | ||
* Collect all created backups. | ||
* | ||
* @return void | ||
*/ | ||
protected function collectBackups() | ||
{ | ||
/** @var StorageObject $object */ | ||
foreach ($this->bucket->objects() as $object) { | ||
if ($this->isFileMatch($this->path->getPath() . '/' . $object->name())) { | ||
$file = new File\GoogleCloudStorage($object); | ||
|
||
$this->files[$this->getFileIndex($file)] = $file; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<?php | ||
|
||
namespace phpbu\App\Backup\File; | ||
|
||
use Google\Cloud\Storage\StorageObject; | ||
use phpbu\App\Exception; | ||
|
||
/** | ||
* Google Drive file class. | ||
* | ||
* @package phpbu | ||
* @subpackage Backup | ||
* @author David Dattee <[email protected]> | ||
* @copyright Sebastian Feldmann <[email protected]> | ||
* @license https://opensource.org/licenses/MIT The MIT License (MIT) | ||
* @link http://phpbu.de/ | ||
*/ | ||
class GoogleCloudStorage extends Remote | ||
{ | ||
/** | ||
* Google Storage Object | ||
* | ||
* @var StorageObject | ||
*/ | ||
private StorageObject $object; | ||
|
||
/** | ||
* Constructor. | ||
* | ||
* @param StorageObject $googleStorageObject | ||
*/ | ||
public function __construct(StorageObject $googleStorageObject) | ||
{ | ||
$this->object = $googleStorageObject; | ||
$this->filename = $this->object->name(); | ||
$this->pathname = $this->object->name(); | ||
$this->size = (int)$this->object->info()['size']; | ||
$this->lastModified = strtotime($this->object->info()['updated']); | ||
} | ||
|
||
/** | ||
* Deletes the file from Google Cloud Storage. | ||
* | ||
* @throws Exception | ||
*/ | ||
public function unlink() | ||
{ | ||
try { | ||
$this->object->delete(); | ||
} catch (\Exception $e) { | ||
throw new Exception($e->getMessage()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
<?php | ||
|
||
namespace phpbu\App\Backup\Sync; | ||
|
||
use Google\Cloud\Storage\StorageClient; | ||
use phpbu\App\Backup\Collector; | ||
use phpbu\App\Backup\Path; | ||
use phpbu\App\Backup\Target; | ||
use phpbu\App\Configuration; | ||
use phpbu\App\Result; | ||
use phpbu\App\Util; | ||
|
||
/** | ||
* Google Drive | ||
* | ||
* @package phpbu | ||
* @subpackage Backup | ||
* @author David Dattee <[email protected]> | ||
* @copyright Sebastian Feldmann <[email protected]> | ||
* @license https://opensource.org/licenses/MIT The MIT License (MIT) | ||
* @link http://phpbu.de/ | ||
*/ | ||
class GoogleCloudStorage implements Simulator | ||
{ | ||
use Cleanable; | ||
|
||
/** | ||
* Google Gloud Storage client. | ||
* | ||
* @var StorageClient | ||
*/ | ||
private $client; | ||
|
||
/** | ||
* Google json secret file. | ||
* | ||
* @var string | ||
*/ | ||
private $secret; | ||
|
||
/** | ||
* Bucket to upload to | ||
* | ||
* @var string | ||
*/ | ||
private $bucket; | ||
|
||
/** | ||
* Path to upload to in the bucket | ||
* | ||
* @var string | ||
*/ | ||
private $parent; | ||
|
||
/** | ||
* (non-PHPDoc) | ||
* | ||
* @param array $options | ||
* @throws \phpbu\App\Exception | ||
* @see \phpbu\App\Backup\Sync::setup() | ||
*/ | ||
public function setup(array $options) | ||
{ | ||
if (!class_exists('\\Google\\Cloud\\Storage\\StorageClient')) { | ||
throw new Exception('google cloud api client not loaded: use composer to install "google/cloud-storage"'); | ||
} | ||
if (!Util\Arr::isSetAndNotEmptyString($options, 'secret')) { | ||
throw new Exception('google secret json file is mandatory'); | ||
} | ||
if (!Util\Arr::isSetAndNotEmptyString($options, 'bucket')) { | ||
throw new Exception('bucket to upload to is mandatory'); | ||
} | ||
$this->parent = Util\Arr::getValue($options, 'path', ''); | ||
|
||
$this->setupAuthFiles($options); | ||
$this->setUpCleanable($options); | ||
} | ||
|
||
/** | ||
* Make sure google authentication files exist and determine absolute path to them. | ||
* | ||
* @param array $config | ||
* @throws \phpbu\App\Backup\Sync\Exception | ||
*/ | ||
private function setupAuthFiles(array $config) | ||
{ | ||
$secret = Util\Path::toAbsolutePath($config['secret'], Configuration::getWorkingDirectory()); | ||
if (!file_exists($secret)) { | ||
throw new Exception(sprintf('google secret json file not found at %s', $secret)); | ||
} | ||
|
||
$this->secret = $secret; | ||
$this->bucket = $config['bucket']; | ||
} | ||
|
||
/** | ||
* Execute the Sync | ||
* | ||
* @param \phpbu\App\Backup\Target $target | ||
* @param \phpbu\App\Result $result | ||
* @throws \phpbu\App\Backup\Sync\Exception | ||
* @see \phpbu\App\Backup\Sync::sync() | ||
*/ | ||
public function sync(Target $target, Result $result) | ||
{ | ||
try { | ||
$this->client = $this->createGoogleCloudClient(); | ||
$bucket = $this->client->bucket($this->bucket); | ||
$hash = $this->calculateHash($target->getPathname()); | ||
|
||
$sentObject = $bucket->upload( | ||
fopen($target->getPathname(), 'rb'), | ||
[ | ||
'name' => ($this->parent ? $this->parent . '/' : '') . $target->getFilename(), | ||
'metadata' => [ | ||
'crc32c' => $hash, | ||
], | ||
], | ||
); | ||
|
||
$result->debug(sprintf('upload: done: %s', $sentObject->name())); | ||
$this->cleanup($target, $result); | ||
} catch (\Exception $e) { | ||
throw new Exception($e->getMessage(), 0, $e); | ||
} | ||
} | ||
|
||
private function calculateHash(string $filePath): string | ||
{ | ||
return base64_encode(hex2bin(hash_file('crc32c', $filePath))); | ||
} | ||
|
||
/** | ||
* Simulate the sync execution. | ||
* | ||
* @param \phpbu\App\Backup\Target $target | ||
* @param \phpbu\App\Result $result | ||
*/ | ||
public function simulate(Target $target, Result $result) | ||
{ | ||
$result->debug('sync backup to google cloud storage' . PHP_EOL); | ||
|
||
$this->isSimulation = true; | ||
$this->simulateRemoteCleanup($target, $result); | ||
} | ||
|
||
/** | ||
* Setup google api client and google drive service. | ||
*/ | ||
protected function createGoogleCloudClient(): StorageClient | ||
{ | ||
if (!$this->client) { | ||
$this->client = new StorageClient(['keyFilePath' => $this->secret]); | ||
} | ||
|
||
return $this->client; | ||
} | ||
|
||
/** | ||
* Creates collector for remote cleanup. | ||
* | ||
* @param \phpbu\App\Backup\Target $target | ||
* @return \phpbu\App\Backup\Collector | ||
*/ | ||
protected function createCollector(Target $target): Collector | ||
{ | ||
return new Collector\GoogleCloudStorage( | ||
$target, | ||
$this->createGoogleCloudClient()->bucket($this->bucket), | ||
new Path($this->parent) | ||
); | ||
} | ||
} |
Oops, something went wrong.