From 91976bbc02fe8e465e44b75de3576d52d9475565 Mon Sep 17 00:00:00 2001 From: david-fairbanks42 Date: Tue, 25 Jul 2023 13:23:01 -0400 Subject: [PATCH] Update for PHP 8.2 --- README.md | 5 ++++ app/Dates.php | 20 ++++++------- app/Ec2Backup.php | 64 +++++++++++++++++++++--------------------- app/MachineDetails.php | 54 +++++++++++++++++------------------ app/functions.php | 47 +++++++++++++------------------ composer.json | 7 +++-- 6 files changed, 95 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index 87492cc..ce43659 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ Stand-alone script designed to run via cron or systemd to back-up EC2 volumes on All volumes associated with the EC2 instance are backed up. +This script does not freeze the volume or a database or wait for inactivity. It just creates snapshots. High traffic +servers should not use this as their only means of backup since there is a chance the database could be mid-write +during the snapshot start which will break the database. + ## Usage After the required configuration settings are set up in the `.env` file, simply executing `php backup.php` will perform the backup. The script has additional options such as `--no-prune` to prevent the script from removing old @@ -89,4 +93,5 @@ WantedBy=timers.target ``` To activate the schedule, execute the command (noting the sudo use) + `sudo systemctl start ec2-backup.timer` diff --git a/app/Dates.php b/app/Dates.php index 9f85929..e120d7a 100644 --- a/app/Dates.php +++ b/app/Dates.php @@ -1,10 +1,8 @@ date, $date->timezone); - } elseif(is_array($date)) { + } elseif (is_array($date)) { return Carbon::parse($date['date'], $date['timezone']); - } elseif(is_numeric($date)) { + } elseif (is_numeric($date)) { return Carbon::createFromTimestamp($date); - } elseif(is_string($date)) { + } elseif (is_string($date)) { return Carbon::parse($date); } diff --git a/app/Ec2Backup.php b/app/Ec2Backup.php index 243dc89..fd33f5f 100644 --- a/app/Ec2Backup.php +++ b/app/Ec2Backup.php @@ -1,9 +1,8 @@ ec2Client = $ec2Client; @@ -45,11 +44,11 @@ class Ec2Backup $this->maxBackupCount = 4; } - public function create(array $options=[]) + public function create(array $options=[]): void { $options = array_merge(['noPrune' => false, 'force' => false], $options); - if($this->enable == false && $options['force'] != true) { + if (!$this->enable && !$options['force']) { app_echo('EC2 Backup is disabled in environment'); return; } @@ -69,7 +68,7 @@ class Ec2Backup return; } - if($machine['type'] != 'ec2') { + if ($machine['type'] != 'ec2') { app_echo('Instance type is wrong to do an EC2 backup', $machine); return; } @@ -77,8 +76,8 @@ class Ec2Backup $volumes = $this->getVolumes($machine['instanceId']); $tags = $this->getTags($machine['instanceId']); - foreach($volumes as $volume) { - if($options['noPrune'] == true) { + foreach ($volumes as $volume) { + if ($options['noPrune']) { $pruneWording = '(pruning disabled)'; } else { $pruneCount = $this->pruneBackups($volume['volumeId']); @@ -86,11 +85,10 @@ class Ec2Backup } $name = (isset($tags['Name'])) ? $tags['Name'] : $machine['instanceId']; - if(count($volumes) > 1) + if (count($volumes) > 1) $name .= " ({$volume['device']})"; - $r = $this->backup(['volumeId' => $volume['volumeId'], 'name' => $name]); - if($r == true) { + if ($this->backup(['volumeId' => $volume['volumeId'], 'name' => $name])) { app_echo("Successfully started snapshot for {$volume['volumeId']} {$pruneWording}"); } else { app_echo("Error starting snapshot for {$volume['volumeId']} {$pruneWording}"); @@ -98,7 +96,7 @@ class Ec2Backup } } - public function getVolumes($instanceId) + public function getVolumes($instanceId): array { try { $result = $this->ec2Client->describeVolumes( @@ -118,7 +116,7 @@ class Ec2Backup } $volumes = []; - foreach($result['Volumes'] as $volume) { + foreach ($result['Volumes'] as $volume) { $volumes[$volume['VolumeId']] = [ 'device' => $volume['Attachments'][0]['Device'], 'volumeId' => $volume['VolumeId'] @@ -128,7 +126,7 @@ class Ec2Backup return $volumes; } - public function getTags($instanceId) + public function getTags($instanceId): array { try { $result = $this->ec2Client->describeTags( @@ -148,14 +146,14 @@ class Ec2Backup } $tags = []; - foreach($result['Tags'] as $tag) { + foreach ($result['Tags'] as $tag) { $tags[$tag['Key']] = $tag['Value']; } return $tags; } - public function getBackups($volumeId) + public function getBackups($volumeId): array { try { $result = $this->ec2Client->describeSnapshots( @@ -175,19 +173,19 @@ class Ec2Backup } $snapshots = []; - foreach($result['Snapshots'] as $snapshot) { - if($snapshot['State'] != 'completed') + foreach ($result['Snapshots'] as $snapshot) { + if ($snapshot['State'] != 'completed') continue; $snapshots[$snapshot['SnapshotId']] = [ - 'started' => Dates::makeCarbon($snapshot['StartTime']), + 'started' => Dates::makeCarbon($snapshot['StartTime']), 'description' => $snapshot['Description'], - 'snapshotId' => $snapshot['SnapshotId'] + 'snapshotId' => $snapshot['SnapshotId'], ]; } uasort($snapshots, function($a, $b) { - if($a['started'] == $b['started']) { + if ($a['started'] == $b['started']) { return 0; } else { return $a['started'] > $b['started'] ? 1 : 0; @@ -197,19 +195,21 @@ class Ec2Backup return $snapshots; } - public function pruneBackups($volumeId) + public function pruneBackups($volumeId): int { $backups = $this->getBackups($volumeId); - if(count($backups) <= $this->maxBackupCount) + if (count($backups) <= $this->maxBackupCount) { return 0; + } $prune = array_slice($backups, 0, count($backups) - $this->maxBackupCount); - if(empty($prune)) + if(empty($prune)) { return 0; + } $count = 0; - foreach($prune as $snapshotId => $snapshot) { + foreach ($prune as $snapshotId => $snapshot) { try { $this->ec2Client->deleteSnapshot( [ @@ -228,9 +228,9 @@ class Ec2Backup return $count; } - public function backup(array $params) + public function backup(array $params): bool { - if(!isset($params['volumeId'])) { + if (!isset($params['volumeId'])) { app_echo('Volume ID is not set in backup parameters'); return false; } @@ -239,8 +239,8 @@ class Ec2Backup $tags = [['Key' => 'Name', 'Value' => $name]]; $snapTags = json_decode(config('TAGS', '[]'), true); - if(is_array($snapTags) && !empty($snapTags)) { - foreach($snapTags as $key => $value) { + if (is_array($snapTags) && !empty($snapTags)) { + foreach ($snapTags as $key => $value) { $tags[] = ['Key' => $key, 'Value' => $value]; } } diff --git a/app/MachineDetails.php b/app/MachineDetails.php index b373ac5..8b8357c 100644 --- a/app/MachineDetails.php +++ b/app/MachineDetails.php @@ -1,9 +1,8 @@ null, 'id' => null, 'machineId' => null, @@ -36,14 +35,14 @@ class MachineDetails /** * PHP config setting for default_socket_timeout to reset to after doing file_get_contents() - * @var int + * @var int|null */ - protected $socketTimeout = null; + protected ?int $socketTimeout = null; /** * @return MachineDetails */ - public static function getInstance() + public static function getInstance(): MachineDetails { if (!isset(static::$instance)) { static::$instance = new static; @@ -55,7 +54,7 @@ class MachineDetails /** * @return array */ - public static function getDetails() + public static function getDetails(): array { if(self::$instance === null) self::$instance = self::getInstance(); @@ -66,7 +65,7 @@ class MachineDetails /** * @return string|null */ - public static function id() + public static function id(): ?string { if(self::$instance === null) self::$instance = self::getInstance(); @@ -77,7 +76,7 @@ class MachineDetails /** * @return string|null */ - public static function machineId() + public static function machineId(): ?string { if(self::$instance === null) self::$instance = self::getInstance(); @@ -88,7 +87,7 @@ class MachineDetails /** * @return string */ - public static function fullMachineId() + public static function fullMachineId(): string { if(self::$instance === null) self::$instance = self::getInstance(); @@ -105,7 +104,7 @@ class MachineDetails /** * @return string|null */ - public static function region() + public static function region(): ?string { if(self::$instance === null) self::$instance = self::getInstance(); @@ -116,19 +115,16 @@ class MachineDetails /** * @return string|null */ - public static function publicIp() + public static function publicIp(): ?string { if(self::$instance === null) self::$instance = self::getInstance(); if(self::$details['publicIp'] === null) { - switch(self::$details['type']) { - case 'ec2' : - self::$details['publicIp'] = self::getEc2PublicIp(); - break; - default : - self::$details['publicIp'] = self::getLocalPublicIp(); - } + self::$details['publicIp'] = match (self::$details['type']) { + 'ec2' => self::getEc2PublicIp(), + default => self::getLocalPublicIp(), + }; } return self::$details['privateIp']; @@ -137,7 +133,7 @@ class MachineDetails /** * @return string|null */ - public static function privateIp() + public static function privateIp(): ?string { if(self::$instance === null) self::$instance = self::getInstance(); @@ -147,12 +143,12 @@ class MachineDetails /* Private methods */ - private static function getLocalPublicIp() + private static function getLocalPublicIp(): string { return '127.0.0.1'; } - private static function getEc2PublicIp() + private static function getEc2PublicIp(): false|string { if(self::$instance === null) self::$instance = self::getInstance(); @@ -186,7 +182,7 @@ class MachineDetails */ private function __clone() {} - private function determineMachineType() + private function determineMachineType(): string { $this->shortenTimeout(); $hostname = @file_get_contents('http://169.254.169.254/latest/meta-data/hostname'); @@ -199,7 +195,7 @@ class MachineDetails } } - private function getLocalData() + private function getLocalData(): array { return [ 'id' => 'dev', @@ -210,7 +206,7 @@ class MachineDetails ]; } - private function getEc2Data() + private function getEc2Data(): array { /* * curl "http://169.254.169.254/latest/dynamic/instance-identity/document" @@ -253,7 +249,7 @@ class MachineDetails return $out; } - private function shortenTimeout() + private function shortenTimeout(): void { if($this->socketTimeout === null) { $this->socketTimeout = ini_get('default_socket_timeout'); @@ -262,7 +258,7 @@ class MachineDetails ini_set('default_socket_timeout', 2); } - private function resetTimeout() + private function resetTimeout(): void { if($this->socketTimeout !== null) { ini_set('default_socket_timeout', $this->socketTimeout); diff --git a/app/functions.php b/app/functions.php index d6e493c..c6c2270 100644 --- a/app/functions.php +++ b/app/functions.php @@ -3,7 +3,6 @@ * Global helper functions * * @copyright (c) 2018, Fairbanks Publishing - * @license Proprietary */ if(!function_exists('array_combine_safe')) { @@ -16,15 +15,15 @@ if(!function_exists('array_combine_safe')) { * * @return array */ - function array_combine_safe(array $keys = [], array $values = [], $default = null) + function array_combine_safe(array $keys = [], array $values = [], $default = null): array { - if(empty($keys)) { + if (empty($keys)) { return []; } $out = []; - foreach($keys as $index => $key) { + foreach ($keys as $index => $key) { $out[$key] = (isset($values[$index])) ? $values[$index] : $default; } @@ -43,7 +42,7 @@ if(!function_exists('array_limit_keys')) { * * @return array */ - function array_limit_keys(array $keys = [], array $input = []) + function array_limit_keys(array $keys = [], array $input = []): array { if(empty($keys)) { return []; @@ -54,8 +53,8 @@ if(!function_exists('array_limit_keys')) { //}, ARRAY_FILTER_USE_KEY); $out = []; - foreach($keys as $key) { - if(array_key_exists($key, $input)) { + foreach ($keys as $key) { + if (array_key_exists($key, $input)) { $out[$key] = $input[$key]; } } @@ -99,28 +98,22 @@ if(!function_exists('boolean')) { * * @return boolean */ - function boolean($var) + function boolean(bool|int|string|null $var): bool { - if(is_bool($var)) { + if (is_bool($var)) { return ($var == true); } - if(is_string($var)) { + if (is_string($var)) { $var = strtolower($var); - switch($var) { - case 'true' : - case 'on' : - case 'yes' : - case 'y' : - case '1' : - return true; - default : - return false; - } + return match ($var) { + 'true', 'on', 'yes', 'y', '1' => true, + default => false, + }; } - if(is_numeric($var)) { + if (is_numeric($var)) { return ($var == 1); } @@ -131,12 +124,12 @@ if(!function_exists('boolean')) { if(!function_exists('config')) { /** * @param string $key - * @param scalar $default - * @return scalar + * @param float|bool|int|string|null $default + * @return float|bool|int|string|null */ - function config($key, $default = null) + function config(string $key, float|bool|int|string $default = null): float|bool|int|string|null { - if(array_key_exists($key, $_ENV)) { + if (array_key_exists($key, $_ENV)) { return $_ENV[$key]; } else { return $default; @@ -149,9 +142,9 @@ if(!function_exists('app_echo')) { * @param string $message * @param array $context */ - function app_echo($message, $context = []) + function app_echo(string $message, array $context = []): void { - if(is_array($context) && !empty($context)) { + if (is_array($context) && !empty($context)) { $message .= ' ' . json_encode($context); } diff --git a/composer.json b/composer.json index fc5aa21..1f0657b 100644 --- a/composer.json +++ b/composer.json @@ -1,9 +1,10 @@ { "require": { + "php": "^8.2", "ext-json": "*", - "aws/aws-sdk-php": "^3.173", - "nesbot/carbon": "^2.45", - "vlucas/phpdotenv": "^5.3" + "aws/aws-sdk-php": "^3.209", + "nesbot/carbon": "^2.68", + "vlucas/phpdotenv": "^5.5" }, "config": { "optimize-autoloader": true,