diff --git a/_config/aws.yml b/_config/aws.yml index 9f18080..7f0ca9b 100644 --- a/_config/aws.yml +++ b/_config/aws.yml @@ -1,6 +1,6 @@ --- Name: spindb-aws --- -SilverStripe\Core\Injector\Injector: +Injector: Aws\S3\S3Client.spindb: factory: LittleGiant\SpinDB\Storage\S3ClientFactory diff --git a/_config/environmentcheck.yml b/_config/environmentcheck.yml index 8e9bca1..72db6d3 100644 --- a/_config/environmentcheck.yml +++ b/_config/environmentcheck.yml @@ -1,11 +1,11 @@ --- Name: spindb-envcheck --- -SilverStripe\EnvironmentCheck\EnvironmentCheckSuite: +EnvironmentCheckSuite: + registered_suites: + health: + - spindb registered_checks: spindb: definition: 'LittleGiant\SpinDB\Health\BackupCheck' title: 'Is the database backup saving to S3 daily?' - registered_suites: - check: - - spindb diff --git a/_config/spindb.yml b/_config/spindb.yml index dd15f89..27e336e 100644 --- a/_config/spindb.yml +++ b/_config/spindb.yml @@ -1,7 +1,7 @@ --- Name: spindb --- -SilverStripe\Core\Injector\Injector: +Injector: Spatie\DbDumper\DbDumper: factory: LittleGiant\SpinDB\Database\DBDumperFactory LittleGiant\SpinDB\Database\Dumper: diff --git a/composer.json b/composer.json index 90ed2cb..9b5470f 100644 --- a/composer.json +++ b/composer.json @@ -4,12 +4,11 @@ "license": "BSD-3-Clause", "description": "Backup DB periodically and backup to AWS S3 storage with rotation", "require": { - "silverstripe/framework": "^4.1", - "silverstripe/vendor-plugin": "^1.0", - "silverstripe/crontask": "^2.1", + "silverstripe/framework": "^3.1", + "silverstripe/crontask": "^1.2.0", "aws/aws-sdk-php": "^3.69", - "silverstripe/environmentcheck": "^2.1", - "spatie/db-dumper": "^2.13" + "silverstripe/environmentcheck": "^1.3.0", + "spatie/db-dumper": "~1.5.1" }, "authors": [ { diff --git a/readme.md b/readme.md index 6e3960a..78e10a8 100644 --- a/readme.md +++ b/readme.md @@ -43,73 +43,72 @@ composer require littlegiant/silverstripe-spindb Configure access to your AWS bucket. The below environment variables should be configured either directly as env on your server, or in `.env` in your project root. -```dotenv -SPINDB_AWS_S3_BUCKET="" -SPINDB_AWS_REGION="ap-southeast-2" # Or your aws region -SPINDB_AWS_ACCESS_KEY_ID="" -SPINDB_AWS_SECRET_ACCESS_KEY="" +```php +define('SPINDB_AWS_S3_BUCKET', ''); +define('SPINDB_AWS_REGION', 'ap-southeast-2'); # Or your aws region +define('SPINDB_AWS_ACCESS_KEY_ID', ''); +define('SPINDB_AWS_SECRET_ACCESS_KEY', ''); ``` If you want to authenticate via your local AWS credentials (Stored in `~/.aws/credentials`) then you can provide a profile name instead. -```dotenv -SPINDB_AWS_S3_BUCKET="" -SPINDB_AWS_REGION="ap-southeast-2" # Or your aws region -SPINDB_AWS_PROFILE="profilename" # The profile name containing your authentication credentials. Can be `default` +```php +define('SPINDB_AWS_S3_BUCKET', ''); +define('SPINDB_AWS_REGION', 'ap-southeast-2'); # Or your aws region +define('SPINDB_AWS_PROFILE', 'profilename'); # The profile name containing your authentication credentials. Can be `default` ``` If you are running this site on AWS you can provide access via IAM instead, and you only need to specify the following. This is the bare minimum configuration necessary for the module to run. -```dotenv -SPINDB_AWS_S3_BUCKET="" -SPINDB_AWS_REGION="ap-southeast-2" # Or your aws region +```php +define('SPINDB_AWS_S3_BUCKET', ''); +define('SPINDB_AWS_REGION', 'ap-southeast-2'); # Or your aws region ``` By default DB backups are written to the `{baseurl}/db_{date}{ext}` path within the bucket, but this can be configured. -```dotenv -SPINDB_PATH="{baseurl}/db_{date}{ext}" +```php +define('SPINDB_PATH', '{baseurl}/db_{date}{ext}" ``` Supported vars: - `{baseurl}` Value of `BASE_URL` var - `{date}` Date the archive was created (ISO_8601) - `{time}` Time the archive was created (ISO_8601) - - `{ext}` File extension created, e.g. `.sql` or `.zip` depending on archive method + - `{ext}` File extension created, e.g. `.sql` ## Schedule / rotation configuration You can configure the time of day that the task occurs, or even how frequently it runs. -```dotenv -SPINDB_SCHEDULE="0 2 * * *" # Every night at 2am +```php +define('SPINDB_SCHEDULE', '0 2 * * *'); # Every night at 2am ``` If you want to backup less frequently you can adjust the day -```dotenv -SPINDB_SCHEDULE="0 2 */2 * *" # Every second night at 2am +```php +define('SPINDB_SCHEDULE', '0 2 */2 * *'); # Every second night at 2am ``` You can configure the number of daily, weekly, monthly, and yearly backups For each of the below, 0 means keep no backups, -1 means keep unlimited backups (use with care) -```dotenv -SPINDB_KEEP_DAILY="7" # Default to 1 week of backups -SPINDB_KEEP_WEEKLY="0" # Default to no weekly backups -SPINDB_KEEP_WEEKLY_DAY="0" # If keeping weekly backups set the day of the week to keep (0/7 = sunday, 1 = monday, etc). -SPINDB_KEEP_MONTHLY="4" # Default to 4 months of monthly backups -SPINDB_KEEP_MONTHLY_DAY="1" # Day of the month to keep. Archaic 1-based index sorry. -SPINDB_KEEP_YEARLY="-1" # Default to keep unlimited yearly backups. -SPINDB_KEEP_YEARLY_DAY="0" # Day of the year to keep. 0-365. (0 is Jan 1) -SPINDB_ARCHIVE="gzip" # Set archive mode. Supports `gzip` / `none` +```php +define('SPINDB_KEEP_DAILY', '7'); # Default to 1 week of backups +define('SPINDB_KEEP_WEEKLY', '0'); # Default to no weekly backups +define('SPINDB_KEEP_WEEKLY_DAY', '0'); # If keeping weekly backups set the day of the week to keep (0/7 = sunday, 1 = monday, etc). +define('SPINDB_KEEP_MONTHLY', '4'); # Default to 4 months of monthly backups +define('SPINDB_KEEP_MONTHLY_DAY', '1'); # Day of the month to keep. Archaic 1-based index sorry. +define('SPINDB_KEEP_YEARLY', '-1'); # Default to keep unlimited yearly backups. +define('SPINDB_KEEP_YEARLY_DAY', '0'); # Day of the year to keep. 0-365. (0 is Jan 1) ``` You can also configure an alert email to notify when a backup is created. -```dotenv -SPINDB_ALERT_EMAIL="webmaster@littlegiant.co.nz" +```php +define('SPINDB_ALERT_EMAIL', 'webmaster@littlegiant.co.nz'); ``` diff --git a/src/Configuration/RotateConfig.php b/src/Configuration/RotateConfig.php index c2b9800..d5155fb 100644 --- a/src/Configuration/RotateConfig.php +++ b/src/Configuration/RotateConfig.php @@ -3,16 +3,12 @@ namespace LittleGiant\SpinDB\Configuration; use Exception; -use SilverStripe\Control\Director; -use SilverStripe\Core\Convert; -use SilverStripe\Core\Environment; -use SilverStripe\ORM\FieldType\DBDate; -use SilverStripe\ORM\FieldType\DBDatetime; +use Director; +use Convert; +use SS_Datetime; class RotateConfig { - const METHOD_GZIP = 'gzip'; - const METHOD_NONE = 'none'; /** @@ -38,8 +34,8 @@ protected static function getFixedArgs(): array public static function getCurrentArgs(): array { return [ - 'date' => DBDatetime::now()->Format(DBDate::ISO_DATE), - 'time' => DBDatetime::now()->Format('HH.mm.ss'), + 'date' => SS_Datetime::now()->Format('y-MM-dd'), + 'time' => SS_Datetime::now()->Format('HH.mm.ss'), ]; } @@ -63,7 +59,7 @@ protected static function getVariableArgPatterns(): array */ public static function schedule(): string { - return Environment::getEnv('SPINDB_SCHEDULE') ?: '0 2 * * *'; + return defined('SPINDB_SCHEDULE') ? SPINDB_SCHEDULE : '0 2 * * *'; } /** @@ -76,7 +72,7 @@ public static function schedule(): string public static function path($arguments = []): string { $arguments = array_merge(self::getFixedArgs(), $arguments); - $pattern = Environment::getEnv('SPINDB_PATH') ?: '{baseurl}/db_{date}{ext}'; + $pattern = defined('SPINDB_PATH') ? SPINDB_PATH : '{baseurl}/db_{date}{ext}'; if (!strstr($pattern, '{date}')) { throw new Exception('SPINDB_PATH variable must contain {date}'); } @@ -132,7 +128,7 @@ public static function parse($path): ?array */ public static function bucket(): ?string { - return Environment::getEnv('SPINDB_AWS_S3_BUCKET') ?: null; + return defined('SPINDB_AWS_S3_BUCKET') ? SPINDB_AWS_S3_BUCKET : null; } /** @@ -142,7 +138,7 @@ public static function bucket(): ?string */ public static function region(): ?string { - return Environment::getEnv('SPINDB_AWS_REGION') ?: null; + return defined('SPINDB_AWS_REGION') ? SPINDB_AWS_REGION : null; } /** @@ -152,7 +148,7 @@ public static function region(): ?string */ public static function accesKeyID(): ?string { - return Environment::getEnv('SPINDB_AWS_ACCESS_KEY_ID') ?: null; + return defined('SPINDB_AWS_ACCESS_KEY_ID') ? SPINDB_AWS_ACCESS_KEY_ID : null; } /** @@ -162,7 +158,7 @@ public static function accesKeyID(): ?string */ public static function secretAccessKey(): ?string { - return Environment::getEnv('SPINDB_AWS_SECRET_ACCESS_KEY') ?: null; + return defined('SPINDB_AWS_SECRET_ACCESS_KEY') ? SPINDB_AWS_SECRET_ACCESS_KEY : null; } /** @@ -172,7 +168,7 @@ public static function secretAccessKey(): ?string */ public static function profile(): ?string { - return Environment::getEnv('SPINDB_AWS_PROFILE') ?: null; + return defined('SPINDB_AWS_PROFILE') ? SPINDB_AWS_PROFILE : null; } /** @@ -254,29 +250,13 @@ public static function keepYearlyDay(): int */ protected static function getNumeric(string $var, int $default): int { - $daily = Environment::getEnv($var); + $daily = defined($var) ? constant($var) : null; if (is_numeric($daily)) { return (int)$daily; } return $default; } - /** - * Get archive method to use - * - * @return string|null - */ - public static function archiveMethod(): ?string - { - $method = Environment::getEnv('SPINDB_ARCHIVE'); - switch ($method) { - case self::METHOD_NONE: - return null; - case self::METHOD_GZIP: - default: - return self::METHOD_GZIP; - } - } /** * Get file extension to use (not including .) @@ -285,9 +265,6 @@ public static function archiveMethod(): ?string */ public static function extension(): string { - $method = static::archiveMethod(); - return $method - ? "sql.{$method}" - : 'sql'; + return 'sql'; } } diff --git a/src/Database/DBDumperFactory.php b/src/Database/DBDumperFactory.php index 784b836..7608fde 100644 --- a/src/Database/DBDumperFactory.php +++ b/src/Database/DBDumperFactory.php @@ -3,13 +3,9 @@ namespace LittleGiant\SpinDB\Database; use InvalidArgumentException; -use LittleGiant\SpinDB\Configuration\RotateConfig; -use SilverStripe\Core\Injector\Factory; -use SilverStripe\ORM\DB; -use Spatie\DbDumper\Compressors\GzipCompressor; +use SilverStripe\Framework\Injector\Factory; use Spatie\DbDumper\Databases\MySql; use Spatie\DbDumper\Databases\PostgreSql; -use Spatie\DbDumper\Databases\Sqlite; use Spatie\DbDumper\DbDumper; /** @@ -27,7 +23,10 @@ class DBDumperFactory implements Factory public function create($service, array $params = array()) { // Build class from type field - $args = DB::getConfig(); + + global $databaseConfig; + + $args = $databaseConfig; $backend = $this->getBackend($args['type']); // Mandatory arguments @@ -41,17 +40,12 @@ public function create($service, array $params = array()) $backend->setPort((int)$args['port']); } - // Set compression (note: Only GZIP supported at the moment) - if (RotateConfig::archiveMethod() === RotateConfig::METHOD_GZIP) { - $backend->useCompressor(new GzipCompressor()); - } - return $backend; } /** * @param string $type - * @return DbDumper + * @return DbDumper|MySql|PostgreSql */ protected function getBackend($type) { @@ -65,7 +59,8 @@ protected function getBackend($type) case 'SQLite3Database': case 'SQLitePDODatabase': case 'SQLiteDatabase': - return Sqlite::create(); + throw new InvalidArgumentException("{$type} is not supported"); + break; case 'PostgrePDODatabase': case 'PostgreSQLDatabase': return PostgreSql::create(); diff --git a/src/Health/BackupCheck.php b/src/Health/BackupCheck.php index e20c729..276e584 100644 --- a/src/Health/BackupCheck.php +++ b/src/Health/BackupCheck.php @@ -2,10 +2,10 @@ namespace LittleGiant\SpinDB\Health; +use EnvironmentCheck; use Exception; use LittleGiant\SpinDB\Storage\RotateStorage; -use SilverStripe\EnvironmentCheck\EnvironmentCheck; -use SilverStripe\ORM\FieldType\DBDatetime; +use SS_Datetime; class BackupCheck implements EnvironmentCheck { @@ -36,11 +36,12 @@ public function check() // If best result is < 1 day ago, success $message = "Last backup {$lastBackupText}"; + switch (true) { - case $lastBackup > strtotime("-1 day", DBDatetime::now()->getTimestamp()): + case $lastBackup > strtotime("-1 day", strtotime(SS_Datetime::now()->getValue())): $status = EnvironmentCheck::OK; break; - case $lastBackup > strtotime("-2 day", DBDatetime::now()->getTimestamp()): + case $lastBackup > strtotime("-2 day", strtotime(SS_Datetime::now()->getValue())): $status = EnvironmentCheck::WARNING; break; default: diff --git a/src/Storage/RotateStorage.php b/src/Storage/RotateStorage.php index c1cd04b..b39a15a 100644 --- a/src/Storage/RotateStorage.php +++ b/src/Storage/RotateStorage.php @@ -6,13 +6,10 @@ use Aws\S3\S3Client; use Exception; use LittleGiant\SpinDB\Configuration\RotateConfig; -use SilverStripe\Core\Injector\Injectable; -use SilverStripe\Core\Injector\Injector; +use Object; -class RotateStorage +class RotateStorage extends Object { - use Injectable; - /** * s3 client * diff --git a/src/Storage/S3ClientFactory.php b/src/Storage/S3ClientFactory.php index 66eadf5..f29ce66 100644 --- a/src/Storage/S3ClientFactory.php +++ b/src/Storage/S3ClientFactory.php @@ -5,7 +5,6 @@ use Aws\Credentials\Credentials; use Aws\S3\S3Client; use LittleGiant\SpinDB\Configuration\RotateConfig; -use SilverStripe\Core\Injector\Factory; class S3ClientFactory implements Factory { diff --git a/src/Storage/TempPath.php b/src/Storage/TempPath.php index 2d52455..6fd3057 100644 --- a/src/Storage/TempPath.php +++ b/src/Storage/TempPath.php @@ -15,7 +15,7 @@ class TempPath public static function tempdir($prefix = 'tmp_', $mode = 0700, $maxAttempts = 1000) { /* Trim trailing slashes from $dir. */ - $dir = rtrim(TEMP_PATH, DIRECTORY_SEPARATOR); + $dir = rtrim(TEMP_FOLDER, DIRECTORY_SEPARATOR); /* If we don't have permission to create a directory, fail, otherwise we will * be stuck in an endless loop. diff --git a/src/Tasks/RotateDBTask.php b/src/Tasks/RotateDBTask.php index 242a0d4..c2a1e49 100644 --- a/src/Tasks/RotateDBTask.php +++ b/src/Tasks/RotateDBTask.php @@ -2,20 +2,20 @@ namespace LittleGiant\SpinDB\Tasks; +use BuildTask; +use Controller; +use Convert; +use CronTask; +use Director; use Exception; +use Filesystem; +use Injector; use LittleGiant\SpinDB\Configuration\RotateConfig; use LittleGiant\SpinDB\Database\Dumper; use LittleGiant\SpinDB\Storage\DBBackup; use LittleGiant\SpinDB\Storage\RotateStorage; use LittleGiant\SpinDB\Storage\TempPath; -use SilverStripe\Assets\Filesystem; -use SilverStripe\Control\Director; -use SilverStripe\Control\HTTPRequest; -use SilverStripe\Core\Convert; -use SilverStripe\Core\Injector\Injector; -use SilverStripe\Core\Path; -use SilverStripe\CronTask\Interfaces\CronTask; -use SilverStripe\Dev\BuildTask; +use SS_HTTPRequest; class RotateDBTask extends BuildTask implements CronTask { @@ -82,7 +82,7 @@ protected function findFile(array $files, string $date) * Implement this method in the task subclass to * execute via the TaskRunner * - * @param HTTPRequest $request + * @param SS_HTTPRequest $request * @throws Exception */ public function run($request) @@ -130,7 +130,7 @@ protected function createNewBackup(array $nowParts): DBBackup // Build a new temporary folder to backup to $tempFolder = TempPath::tempdir(); - $tempPath = Path::join($tempFolder, basename($uploadPath)); + $tempPath = Controller::join_links($tempFolder, basename($uploadPath)); try { // Dump the database /** @var Dumper $dumper */