]> BookStack Code Mirror - system-cli/commitdiff
Started testing of backup command
authorDan Brown <redacted>
Mon, 3 Apr 2023 16:26:09 +0000 (17:26 +0100)
committerDan Brown <redacted>
Mon, 3 Apr 2023 16:26:09 +0000 (17:26 +0100)
Came across a couple of issues during build:
1. DB needed init SQL query to fix user permissions to allow backup,
   added detail to readme in new "known issues".
2. App container work needed to wait for database to be alive.

docker-compose.yml
docker-mysql-init.sql [new file with mode: 0644]
readme.md
src/Commands/BackupCommand.php
src/Services/MySqlRunner.php
tests/CommandResult.php
tests/Commands/BackupCommandTest.php [new file with mode: 0644]

index 9499d982ca09f4c6c207005b987ece0e939722c2..e41da6229171d3e21d0c702c082f77a9eb48ebef 100644 (file)
@@ -1,4 +1,4 @@
-version: "3.8"
+version: "3.9"
 
 services:
   app:
@@ -7,11 +7,14 @@ services:
     volumes:
       - ./:/cli
     depends_on:
-      - db
+      db:
+        condition: service_completed_successfully
   db:
     image: mysql:8.0
     environment:
       MYSQL_ROOT_PASSWORD: password
       MYSQL_DATABASE: bookstack
       MYSQL_USER: bookstack
-      MYSQL_PASSWORD: bookstack
\ No newline at end of file
+      MYSQL_PASSWORD: bookstack
+    volumes:
+      - ./docker-mysql-init.sql:/docker-entrypoint-initdb.d/init.sql
\ No newline at end of file
diff --git a/docker-mysql-init.sql b/docker-mysql-init.sql
new file mode 100644 (file)
index 0000000..e90c66d
--- /dev/null
@@ -0,0 +1 @@
+GRANT RELOAD ON *.* TO 'bookstack'@'%';
\ No newline at end of file
index 22bd0c37e3052f74e9cefcc65d8a8ff0d2be2ee5..064e1348bc146c6a8f7287e5bfc34c801e91b552 100644 (file)
--- a/readme.md
+++ b/readme.md
@@ -55,4 +55,15 @@ Within the environment a pre-existing BookStack instance can be found at `/var/w
 ### Contributing
 
 I welcome issues and PRs but keep in mind that I'd like to keep the feature-set narrow to limit support/maintenance burden.
-Therefore, I likely won't leave issues open long, or merge PRs, for requests to add new features or for changes that increase the scope of what this script already supports.
\ No newline at end of file
+Therefore, I likely won't leave issues open long, or merge PRs, for requests to add new features or for changes that increase the scope of what this script already supports.
+
+### Known Issues
+
+#### mysqldump - Couldn't execute 'FLUSH TABLES'
+
+mysqldump may produce the following:
+
+> mysqldump: Couldn't execute 'FLUSH TABLES': Access denied; you need (at least one of) the RELOAD or FLUSH_TABLES privilege(s) for this operation (1227)
+
+This was due to 8.0.32 mysqldump, changing the required permissions, and this should be largely [fixed as per 8.0.33](https://bugs.mysql.com/bug.php?id=109685).
+Temporary workaround is to provide the database user RELOAD permissions: `GRANT RELOAD ON *.* TO 'bookstack'@'%';`
\ No newline at end of file
index 7c3730a1b6a39678be6fd208b2d074ca6fea9c97..05d90c72bf3afc06ce6e35bac83319c0e776a940 100644 (file)
@@ -49,9 +49,12 @@ final class BackupCommand extends Command
         $zip = new ZipArchive();
         $zip->open($zipTempFile, ZipArchive::CREATE);
 
-        // Add default files (.env config file and this CLI)
+        // Add default files (.env config file and this CLI if existing)
         $zip->addFile($appDir . DIRECTORY_SEPARATOR . '.env', '.env');
-        $zip->addFile($appDir . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . 'run', 'run');
+        $cliPath = $appDir . DIRECTORY_SEPARATOR . 'bookstack-system-cli';
+        if (file_exists($cliPath)) {
+            $zip->addFile($cliPath, 'bookstack-system-cli');
+        }
 
         if ($handleDatabase) {
             $output->writeln("<info>Dumping the database via mysqldump...</info>");
index b61bf66657a784735e13bf36a38fb26758f78cc5..5b130a5bbd7b11b467b0fcabaa2a1bd14cccbedb 100644 (file)
@@ -86,7 +86,9 @@ class MySqlRunner
                     fwrite($file, $data);
                     $hasOutput = true;
                 }, function ($error) use (&$errors) {
-                    $errors .= $error . "\n";
+                    if (!str_contains($error, '[Warning] ')) {
+                        $errors .= $error . "\n";
+                    }
                 });
         } catch (\Exception $exception) {
             fclose($file);
index 492e216e3022dcbf054eb4a57f768502e4613933..011f02226238ae2d7556f343175139e313d9e2a5 100644 (file)
@@ -20,7 +20,21 @@ class CommandResult
 
     public function assertSuccessfulExit(): void
     {
-        Assert::assertEquals(0, $this->tester->getStatusCode());
+        try {
+            $statusCode = $this->tester->getStatusCode();
+        } catch (\Exception $exception) {
+            $statusCode = 1;
+        }
+
+        Assert::assertEquals(0, $statusCode);
+    }
+
+    public function dumpError(): void
+    {
+        if ($this->error) {
+            echo $this->error->getMessage() . "\n" .
+                $this->error->getTraceAsString();
+        }
     }
 
     public function assertErrorExit(): void
diff --git a/tests/Commands/BackupCommandTest.php b/tests/Commands/BackupCommandTest.php
new file mode 100644 (file)
index 0000000..7f598d7
--- /dev/null
@@ -0,0 +1,47 @@
+<?php declare(strict_types=1);
+
+namespace Tests\Commands;
+
+use Tests\TestCase;
+
+class BackupCommandTest extends TestCase
+{
+    static string $uniqueUserEmail = '';
+
+    public static function setUpBeforeClass(): void
+    {
+        static::$uniqueUserEmail = bin2hex(random_bytes(10)) . '@example.com';
+        chdir('/var/www/bookstack');
+        // Ensure the database is created and create an admin user we can look out for in the data.
+        exec('php artisan migrate --force');
+        exec('php artisan bookstack:create-admin --email="' . static::$uniqueUserEmail . '" --name="Bazza" --password="hunter200"');
+    }
+    public function test_command_does_full_backup_of_cwd_instance_by_default()
+    {
+        chdir('/var/www/bookstack');
+
+        $this->assertCount(0, glob('bookstack-backup-*.zip'));
+
+        file_put_contents('/var/www/bookstack/themes/test.txt', static::$uniqueUserEmail . '-themes');
+        file_put_contents('/var/www/bookstack/public/uploads/test.txt', static::$uniqueUserEmail . '-public-uploads');
+        file_put_contents('/var/www/bookstack/storage/uploads/test.txt', static::$uniqueUserEmail . '-storage-uploads');
+
+        $result = $this->runCommand('backup');
+        $result->dumpError();
+        $result->assertSuccessfulExit();
+        $result->assertStdoutContains("Backup finished.");
+
+        $this->assertCount(1, glob('bookstack-backup-*.zip'));
+        $zipFile = glob('bookstack-backup-*.zip')[0];
+
+        $zip = new \ZipArchive();
+        $zip->open($zipFile);
+        $dbDump = $zip->getFromName('db.sql');
+        $this->assertStringContainsString('APP_KEY=', $zip->getFromName('.env'));
+        $this->assertStringContainsString(static::$uniqueUserEmail, $dbDump);
+        $this->assertStringContainsString('page_revisions', $dbDump);
+        $this->assertStringContainsString(static::$uniqueUserEmail . '-public-uploads', $zip->getFromName('public/uploads/test.txt'));
+        $this->assertStringContainsString(static::$uniqueUserEmail . '-storage-uploads', $zip->getFromName('storage/uploads/test.txt'));
+        $this->assertStringContainsString(static::$uniqueUserEmail . '-themes', $zip->getFromName('themes/test.txt'));
+    }
+}
\ No newline at end of file