Compare commits

...
Author SHA1 Message Date
fogelito 9142e44144 Remove Dsn from database Restore.php 2023-08-15 15:48:21 +03:00
fogelito a8ee7f42e8 info 2023-08-15 12:51:16 +03:00
fogelito 8a965b2ce3 Merge remote-tracking branch 'origin/db-pools-backups' into db-pools-backups 2023-08-15 12:49:22 +03:00
fogelito 4fdd76dc95 Move check to start function 2023-08-15 12:49:08 +03:00
shimon fbae9f859f container id fix 2023-08-15 10:42:14 +03:00
shimon 1c857c982e container id fix 2023-08-15 10:36:14 +03:00
Jake Barnby 40f0936dc7 Merge remote-tracking branch 'origin/cl-1.1.x' into db-pools-backups
# Conflicts:
#	composer.lock
2023-08-14 21:09:31 -04:00
fogelito bebe3a217f Merge remote-tracking branch 'origin/db-pools-backups' into db-pools-backups 2023-08-14 12:23:22 +03:00
fogelito b3222534ed .env 2023-08-14 12:23:12 +03:00
shimon 3ccb38f9ec Merge branch 'db-pools-backups' of github.com:appwrite/appwrite into db-pools-backups 2023-08-14 10:48:29 +03:00
shimon bb3aa1a172 container id fix 2023-08-14 10:48:15 +03:00
fogelito 77deacbd40 Remove xtra line 2023-08-13 20:55:44 +03:00
shimon 9b97317bdc container id fix 2023-08-13 20:54:05 +03:00
shimon ce03231ca0 container id fix 2023-08-13 19:39:38 +03:00
shimon 43759ae273 container id fix 2023-08-13 19:18:22 +03:00
shimon ecd960a6fd container id fix 2023-08-13 18:34:26 +03:00
shimon 9da6a6f5ec container id fix 2023-08-13 18:24:31 +03:00
shimon 20bc1c4c98 container id fix 2023-08-13 17:53:41 +03:00
fogelito f787acecdb Merge remote-tracking branch 'origin/db-pools-backups' into db-pools-backups 2023-08-13 15:43:28 +03:00
fogelito f2070cac15 default value for App::getEnv 2023-08-13 15:43:16 +03:00
shimon 95841c0418 container id fix 2023-08-13 14:48:31 +03:00
shimon 08daa7cf6d container id fix 2023-08-13 14:47:26 +03:00
fogelito 47352e4062 composer.lock 2023-08-13 14:05:20 +03:00
fogelito 1488bf1d20 App::getEnv default value 2023-08-13 13:39:06 +03:00
fogelito 2aa3b1e296 nproc + setContainerId as function 2023-08-13 13:36:25 +03:00
fogelito aaeddfe780 Restore title logs 2023-08-13 12:31:44 +03:00
fogelito 66fe902152 Merge remote-tracking branch 'origin/db-pools-backups' into db-pools-backups 2023-08-13 12:11:48 +03:00
fogelito 4c3ea3c073 logs title update 2023-08-13 12:11:18 +03:00
shimon b59d6051b6 container id fix 2023-08-13 11:36:38 +03:00
shimon ab2b37fc86 container id fix 2023-08-12 08:09:35 +03:00
shimon 777340a626 container id fix 2023-08-12 08:03:22 +03:00
shimon b27a16ccaf container id fix 2023-08-11 00:45:37 +03:00
shimon 92e08d3a2d container id fix 2023-08-11 00:03:30 +03:00
shimon d6128282f5 container id fix 2023-08-10 21:32:50 +03:00
shimon 63ed3ae19c container id fix 2023-08-10 18:18:00 +03:00
shimon a294bf24fc container id fix 2023-08-10 18:15:01 +03:00
shimon 0e9c61ac86 container id fix 2023-08-10 17:54:40 +03:00
shimon 2cec0f5469 connection debug 2023-08-09 23:37:31 +03:00
shimon b2f0249146 connection debug 2023-08-09 23:24:56 +03:00
fogelito b0807da8ee check empty 2023-08-09 23:13:52 +03:00
fogelito 125e388ded Exec by id 2023-08-09 23:08:07 +03:00
fogelito d9ece4c761 CHANGES 2023-08-09 21:06:10 +03:00
shimon d3607471eb connection debug 2023-08-09 11:45:05 +03:00
shimon d1f7bd26c8 connection debug 2023-08-09 11:31:50 +03:00
shimon 7296027e43 Merge remote-tracking branch 'origin/db-pools-backups' into db-pools-backups 2023-08-09 11:18:11 +03:00
shimon 1ce00b139d connection debug 2023-08-09 11:16:17 +03:00
fogelito 2ebd500d6b .gitignore remove unused folders 2023-08-09 11:12:06 +03:00
fogelito 6b5b3ec865 _DO_SPACES keys 2023-08-09 11:10:49 +03:00
shimon 5fa1da3610 init::pools replica addition 2023-08-09 10:55:52 +03:00
shimon e5f841e353 init::pools replica addition 2023-08-09 10:14:26 +03:00
shimon 3a38b492f5 ci file ref update 2023-08-08 22:57:34 +03:00
fogelito 5f4f6cd9c5 remove chown 2023-08-08 18:12:16 +03:00
fogelito f2a337d86e varlibmysql resolve 2023-08-08 17:06:46 +03:00
fogelito 71b4c483c2 chown 2023-08-08 15:39:50 +03:00
fogelito d9569df1cc pushd popd 2023-08-08 14:53:08 +03:00
fogelito d1efcd6a61 s3 constructor 2023-08-08 12:32:24 +03:00
fogelito cea7ab8d4c Check file before download 2023-08-08 11:52:33 +03:00
fogelito cfc178b046 addressing comments part 1 2023-08-08 08:46:13 +03:00
50aba6a1c2 Update src/Appwrite/Platform/Tasks/Backup.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-08-07 23:52:45 +03:00
5fd5c875fc Update src/Appwrite/Platform/Tasks/Backup.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-08-07 23:52:14 +03:00
1ceaa599b5 Update src/Appwrite/Platform/Tasks/Backup.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-08-07 23:50:34 +03:00
fogelito 1138b4fd59 named volumes 2023-08-07 22:11:01 +03:00
fogelito fae5c5b92e last changes 2023-08-07 18:45:17 +03:00
fogelito 9a446ec0ec last changes 2023-08-07 18:45:02 +03:00
fogelito 675ad1d0c6 check stopped container 2023-08-07 17:58:54 +03:00
fogelito a2463e114e last changes 2023-08-07 17:53:21 +03:00
fogelito 9fab5c46f1 dsn changes 2023-08-07 15:54:09 +03:00
fogelito d730b41b5f varlibmysql 2023-08-06 21:50:05 +03:00
fogelito 66228a2462 docker-compose.yml mounts 2023-08-06 21:47:41 +03:00
fogelito fddac97835 Restore.php 2023-08-06 15:55:33 +03:00
Christy Jacob 9499cbf9f7 feat: udpate console branch name 2023-08-06 13:59:28 +04:00
Christy Jacob 01f55445f1 feat: add new database cluster 2023-08-03 19:29:01 +04:00
Christy JacobandGitHub 875662c066 Merge pull request #5932 from appwrite/fix-missing-active-deployment
Bump console to fix missing active deployment bug
2023-08-02 22:56:13 +04:00
Steven Nguyen 30ef106406 Bump console to fix missing active deployment bug 2023-08-02 11:36:50 -07:00
fogelito e4f653be13 Restore.php 2023-08-02 19:10:45 +03:00
fogelito 53df65a3c4 Compress 2023-08-01 17:57:33 +03:00
fogelito 2f8b526c01 Restore.php 2023-08-01 10:53:51 +03:00
fogelito c1cf6acc7a Fix Incremental Base Bug 2023-07-31 15:22:33 +03:00
fogelito dd25899f74 Fullbackup functions 2023-07-31 14:05:51 +03:00
fogelito fc2f1cef31 Incremental Backup.php 2023-07-30 20:41:14 +03:00
Eldad A. FuxandGitHub 03aeea857c Merge pull request #5874 from appwrite/feat-upgrade-task
Feat upgrade task
2023-07-27 22:27:16 +03:00
fogelito b2c7a3eb33 Transfer big data 2023-07-27 14:55:25 +03:00
Steven Nguyen 128b9d1b1b Prompt developer to confirm installing over existing install 2023-07-25 13:50:43 -07:00
fogelito 55a6732444 Transfer big data 2023-07-25 18:38:26 +03:00
Steven Nguyen d8ea72b49e Create a dedicated upgrade task
Before, we used the same command for both installation and upgrades.
This lead to problems because developers would try to upgrade in the
wrong folder and end up creating a new installation.

This new upgrade command validates the existence of an existing
installation before proceeding with the upgrade to ensure no new
installation is created when upgrading.
2023-07-24 15:34:38 -07:00
Steven Nguyen c32f8e49e2 Make the install path a class property
This makes it so the property can be used by subclasses.
2023-07-24 15:21:34 -07:00
Steven Nguyen 1989ff873e Fix install analytics 2023-07-24 15:17:52 -07:00
Steven Nguyen f164676b44 Fix install template paths 2023-07-24 15:15:31 -07:00
fogelito 970c35b31a Transfer big data 2023-07-23 19:05:35 +03:00
fogelito 38eb24d0ad Restore.php 2023-07-19 12:34:00 +03:00
fogelito 186b92d5af Restore.php + name Changes 2023-07-17 20:38:31 +03:00
fogelito 7ad08bfef2 SO spaces 2023-07-13 15:39:29 +03:00
fogelito 6f87757c53 SO spaces 2023-07-13 15:38:49 +03:00
fogelito 4f0dd56afc clean up 2023-07-12 15:27:06 +03:00
fogelito 7918ee2f20 Add extract directory 2023-07-12 15:22:06 +03:00
fogelito 748abb29cf structure 2023-07-12 14:55:09 +03:00
19 changed files with 868 additions and 90 deletions
+2
View File
@@ -27,12 +27,14 @@ _APP_DB_PASS=password
_APP_DB_ROOT_PASS=rootsecretpassword
_APP_CONNECTIONS_MAX=3100
_APP_POOL_CLIENTS=14
_APP_CONNECTIONS_DB_REPLICAS=db_fra1_03=mariadb://root:rootsecretpassword@mariadb:3306/appwrite
_APP_CONNECTIONS_DB_PROJECT=db_fra1_02=mariadb://user:password@mariadb:3306/appwrite
_APP_CONNECTIONS_DB_CONSOLE=db_fra1_01=mariadb://user:password@mariadb:3306/appwrite
_APP_CONNECTIONS_CACHE=redis_fra1_01=redis://redis:6379
_APP_CONNECTIONS_QUEUE=redis_fra1_01=redis://redis:6379
_APP_CONNECTIONS_PUBSUB=redis_fra1_01=redis://redis:6379
_APP_CONNECTIONS_STORAGE=local://localhost
_APP_CONNECTIONS_BACKUPS_STORAGE=dospaces://ACCESS:SECRET@default/backups-v1?region=fra1
_APP_STORAGE_ANTIVIRUS=disabled
_APP_STORAGE_ANTIVIRUS_HOST=clamav
_APP_STORAGE_ANTIVIRUS_PORT=3310
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
with:
fetch-depth: 2
submodules: recursive
ref: feat-db-pools-master
ref: db-pools-backups
- name: Login to Docker Hub
uses: docker/login-action@v2
+1
View File
@@ -11,3 +11,4 @@ debug/
app/sdks
dev/yasd_init.php
.phpunit.result.cache
/backup-data/
+1 -1
View File
@@ -1,4 +1,4 @@
[submodule "app/console"]
path = app/console
url = https://github.com/appwrite/console
branch = cloud-cards
branch = cloud-1.1.x
+8
View File
@@ -123,6 +123,8 @@ RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/calc-tier-stats && \
chmod +x /usr/local/bin/patch-delete-project-collections && \
chmod +x /usr/local/bin/maintenance && \
chmod +x /usr/local/bin/db-backup && \
chmod +x /usr/local/bin/db-restore && \
chmod +x /usr/local/bin/volume-sync && \
chmod +x /usr/local/bin/usage && \
chmod +x /usr/local/bin/install && \
@@ -159,6 +161,12 @@ RUN echo "default_socket_timeout=-1" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN echo "opcache.jit_buffer_size=100M" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN echo "opcache.jit=1235" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN \
apk update \
&& apk add --no-cache \
docker \
&& rm -rf /var/cache/apk/*
EXPOSE 80
CMD [ "php", "app/http.php", "-dopcache.preload=opcache.preload=/usr/src/code/app/preload.php" ]
+2 -1
View File
@@ -95,6 +95,7 @@ App::post('/v1/projects')
$backups['database_db_fra1_03'] = ['from' => '10:30', 'to' => '11:15'];
$backups['database_db_fra1_04'] = ['from' => '13:30', 'to' => '14:15'];
$backups['database_db_fra1_05'] = ['from' => '4:30', 'to' => '5:15'];
$backups['database_db_fra1_06'] = ['from' => '16:30', 'to' => '17:15'];
$databases = Config::getParam('pools-database', []);
@@ -118,7 +119,7 @@ App::post('/v1/projects')
}
}
if ($index = array_search('database_db_fra1_05', $databases)) {
if ($index = array_search('database_db_fra1_06', $databases)) {
$database = $databases[$index];
} else {
$database = $databases[array_rand($databases)];
+6
View File
@@ -532,6 +532,12 @@ $register->set('pools', function () {
'multiple' => true,
'schemes' => ['mariadb', 'mysql'],
],
'replica' => [
'type' => 'database',
'dsns' => App::getEnv('_APP_CONNECTIONS_DB_REPLICAS', $fallbackForDB),
'multiple' => true,
'schemes' => ['mariadb', 'mysql'],
],
'queue' => [
'type' => 'queue',
'dsns' => App::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis),
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
php /usr/src/code/app/cli.php backup $@
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
php /usr/src/code/app/cli.php restore $@
Executable
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
php /usr/src/code/app/cli.php upgrade $@
+1 -1
View File
@@ -63,7 +63,7 @@
"utopia-php/preloader": "0.2.*",
"utopia-php/queue": "0.5.*",
"utopia-php/registry": "0.5.*",
"utopia-php/storage": "0.13.*",
"utopia-php/storage": "dev-feat-transfer as 0.13.3",
"utopia-php/swoole": "0.5.*",
"utopia-php/websocket": "0.1.0",
"resque/php-resque": "1.3.6",
Generated
+75 -65
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "329498d12fabaa764c4178881cb07788",
"content-hash": "ff72579b1906a3ee181ec1209198c13f",
"packages": [
{
"name": "adhocore/jwt",
@@ -534,16 +534,16 @@
},
{
"name": "guzzlehttp/promises",
"version": "2.0.0",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6"
"reference": "111166291a0f8130081195ac4556a5587d7f1b5d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
"url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d",
"reference": "111166291a0f8130081195ac4556a5587d7f1b5d",
"shasum": ""
},
"require": {
@@ -597,7 +597,7 @@
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
"source": "https://github.com/guzzle/promises/tree/2.0.0"
"source": "https://github.com/guzzle/promises/tree/2.0.1"
},
"funding": [
{
@@ -613,20 +613,20 @@
"type": "tidelift"
}
],
"time": "2023-05-21T13:50:22+00:00"
"time": "2023-08-03T15:11:55+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "2.5.0",
"version": "2.6.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "b635f279edd83fc275f822a1188157ffea568ff6"
"reference": "8bd7c33a0734ae1c5d074360512beb716bef3f77"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6",
"reference": "b635f279edd83fc275f822a1188157ffea568ff6",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/8bd7c33a0734ae1c5d074360512beb716bef3f77",
"reference": "8bd7c33a0734ae1c5d074360512beb716bef3f77",
"shasum": ""
},
"require": {
@@ -713,7 +713,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/2.5.0"
"source": "https://github.com/guzzle/psr7/tree/2.6.0"
},
"funding": [
{
@@ -729,7 +729,7 @@
"type": "tidelift"
}
],
"time": "2023-04-17T16:11:26+00:00"
"time": "2023-08-03T15:06:02+00:00"
},
{
"name": "influxdb/influxdb-php",
@@ -2622,26 +2622,32 @@
},
{
"name": "utopia-php/storage",
"version": "0.13.2",
"version": "dev-feat-transfer",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/storage.git",
"reference": "ad1c00f24ca56e73888acc2af3deee4919b1194b"
"reference": "0c510a85509719a45cc6e7f9861d3bc1b4c0f1e0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/ad1c00f24ca56e73888acc2af3deee4919b1194b",
"reference": "ad1c00f24ca56e73888acc2af3deee4919b1194b",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/0c510a85509719a45cc6e7f9861d3bc1b4c0f1e0",
"reference": "0c510a85509719a45cc6e7f9861d3bc1b4c0f1e0",
"shasum": ""
},
"require": {
"ext-brotli": "*",
"ext-fileinfo": "*",
"ext-lz4": "*",
"ext-snappy": "*",
"ext-xz": "*",
"ext-zlib": "*",
"ext-zstd": "*",
"php": ">=8.0",
"utopia-php/framework": "0.*.*"
"utopia-php/framework": "0.*.*",
"utopia-php/system": "0.*.*"
},
"require-dev": {
"laravel/pint": "1.2.*",
"phpunit/phpunit": "^9.3",
"vimeo/psalm": "4.0.1"
},
@@ -2655,12 +2661,6 @@
"license": [
"MIT"
],
"authors": [
{
"name": "Eldad Fux",
"email": "eldad@appwrite.io"
}
],
"description": "A simple Storage library to manage application storage",
"keywords": [
"framework",
@@ -2671,9 +2671,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/storage/issues",
"source": "https://github.com/utopia-php/storage/tree/0.13.2"
"source": "https://github.com/utopia-php/storage/tree/feat-transfer"
},
"time": "2022-12-20T11:11:35+00:00"
"time": "2023-08-12T10:18:42+00:00"
},
{
"name": "utopia-php/swoole",
@@ -3253,16 +3253,16 @@
},
{
"name": "nikic/php-parser",
"version": "v4.16.0",
"version": "v4.17.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "19526a33fb561ef417e822e85f08a00db4059c17"
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17",
"reference": "19526a33fb561ef417e822e85f08a00db4059c17",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"shasum": ""
},
"require": {
@@ -3303,9 +3303,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
},
"time": "2023-06-25T14:52:30+00:00"
"time": "2023-08-13T19:53:39+00:00"
},
{
"name": "phar-io/manifest",
@@ -3530,16 +3530,16 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.7.2",
"version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d"
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d",
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"shasum": ""
},
"require": {
@@ -3582,9 +3582,9 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3"
},
"time": "2023-05-30T18:13:47+00:00"
"time": "2023-08-12T11:01:26+00:00"
},
{
"name": "phpspec/prophecy",
@@ -3656,16 +3656,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.22.1",
"version": "1.23.1",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0"
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/65c39594fbd8c67abfc68bb323f86447bab79cc0",
"reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/846ae76eef31c6d7790fac9bc399ecee45160b26",
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26",
"shasum": ""
},
"require": {
@@ -3697,22 +3697,22 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.1"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.1"
},
"time": "2023-06-29T20:46:06+00:00"
"time": "2023-08-03T16:32:59+00:00"
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.26",
"version": "9.2.27",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1"
"reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b0a88255cb70d52653d80c890bd7f38740ea50d1",
"reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1",
"shasum": ""
},
"require": {
@@ -3768,7 +3768,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26"
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.27"
},
"funding": [
{
@@ -3776,7 +3777,7 @@
"type": "github"
}
],
"time": "2023-03-06T12:58:08+00:00"
"time": "2023-07-26T13:44:30+00:00"
},
{
"name": "phpunit/php-file-iterator",
@@ -4628,16 +4629,16 @@
},
{
"name": "sebastian/global-state",
"version": "5.0.5",
"version": "5.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
"reference": "bde739e7565280bda77be70044ac1047bc007e34"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34",
"reference": "bde739e7565280bda77be70044ac1047bc007e34",
"shasum": ""
},
"require": {
@@ -4680,7 +4681,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6"
},
"funding": [
{
@@ -4688,7 +4689,7 @@
"type": "github"
}
],
"time": "2022-02-14T08:28:10+00:00"
"time": "2023-08-02T09:26:13+00:00"
},
{
"name": "sebastian/lines-of-code",
@@ -5451,16 +5452,16 @@
},
{
"name": "twig/twig",
"version": "v3.6.1",
"version": "v3.7.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd"
"reference": "5cf942bbab3df42afa918caeba947f1b690af64b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd",
"reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/5cf942bbab3df42afa918caeba947f1b690af64b",
"reference": "5cf942bbab3df42afa918caeba947f1b690af64b",
"shasum": ""
},
"require": {
@@ -5506,7 +5507,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.6.1"
"source": "https://github.com/twigphp/Twig/tree/v3.7.0"
},
"funding": [
{
@@ -5518,12 +5519,21 @@
"type": "tidelift"
}
],
"time": "2023-06-08T12:52:13+00:00"
"time": "2023-07-26T07:16:09+00:00"
}
],
"aliases": [
{
"package": "utopia-php/storage",
"version": "dev-feat-transfer",
"alias": "0.13.3",
"alias_normalized": "0.13.3.0"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {
"utopia-php/storage": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
@@ -5547,5 +5557,5 @@
"platform-overrides": {
"php": "8.0"
},
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.2.0"
}
+53 -1
View File
@@ -126,6 +126,7 @@ services:
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_CONNECTIONS_DB_PROJECT
- _APP_CONNECTIONS_DB_REPLICAS
- _APP_CONNECTIONS_DB_CONSOLE
- _APP_CONNECTIONS_CACHE
- _APP_CONNECTIONS_QUEUE
@@ -721,7 +722,7 @@ services:
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
mariadb:
image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p
image: mysql:8.0.33 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
<<: *x-logging
networks:
@@ -748,6 +749,56 @@ services:
# - SMARTHOST_HOST=smtp
# - SMARTHOST_PORT=587
xtrabackup:
image: percona/percona-xtrabackup:8.0.33
container_name: xtrabackup
command: sleep infinity
volumes:
- backup-data:/backups:rw
- appwrite-mariadb:/var/lib/mysql:r
networks:
- appwrite
user: 'root:root'
appwrite-backup:
entrypoint: db-backup
command:
- --database=db_fra1_03
<<: *x-logging
container_name: appwrite-backup
image: appwrite-dev
networks:
- appwrite
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
- backup-data:/backups:rw
depends_on:
- redis
environment:
- _APP_CONNECTIONS_DB_REPLICAS
- _APP_CONNECTIONS_BACKUPS_STORAGE
appwrite-restore:
command: sleep infinity
container_name: appwrite-backup-restore
image: appwrite-dev
networks:
- appwrite
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
- backup-data:/backups:rw
- appwrite-mariadb:/var/lib/mysql:rw
depends_on:
- redis
environment:
- _APP_CONNECTIONS_DB_REPLICAS
- _APP_CONNECTIONS_BACKUPS_STORAGE
redis:
image: redis:7.0.4-alpine
<<: *x-logging
@@ -910,4 +961,5 @@ volumes:
appwrite-config:
appwrite-functions:
appwrite-builds:
backup-data:
# appwrite-chronograf:
+6
View File
@@ -2,6 +2,8 @@
namespace Appwrite\Platform\Services;
use Appwrite\Platform\Tasks\Backup;
use Appwrite\Platform\Tasks\Restore;
use Utopia\Platform\Service;
use Appwrite\Platform\Tasks\Doctor;
use Appwrite\Platform\Tasks\Install;
@@ -22,6 +24,7 @@ use Appwrite\Platform\Tasks\VolumeSync;
use Appwrite\Platform\Tasks\CalcUsersStats;
use Appwrite\Platform\Tasks\CalcTierStats;
use Appwrite\Platform\Tasks\PatchDeleteProjectCollections;
use Appwrite\Platform\Tasks\Upgrade;
class Tasks extends Service
{
@@ -36,6 +39,7 @@ class Tasks extends Service
->addAction(Hamster::getName(), new Hamster())
->addAction(Doctor::getName(), new Doctor())
->addAction(Install::getName(), new Install())
->addAction(Upgrade::getName(), new Upgrade())
->addAction(Maintenance::getName(), new Maintenance())
->addAction(PatchCreateMissingSchedules::getName(), new PatchCreateMissingSchedules())
->addAction(ClearCardCache::getName(), new ClearCardCache())
@@ -48,6 +52,8 @@ class Tasks extends Service
->addAction(CalcUsersStats::getName(), new CalcUsersStats())
->addAction(CalcTierStats::getName(), new CalcTierStats())
->addAction(PatchDeleteProjectCollections::getName(), new PatchDeleteProjectCollections())
->addAction(Backup::getName(), new Backup())
->addAction(Restore::getName(), new Restore())
;
}
}
+305
View File
@@ -0,0 +1,305 @@
<?php
namespace Appwrite\Platform\Tasks;
use Exception;
use Utopia\DSN\DSN;
use Utopia\Platform\Action;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Pools\Group;
use Utopia\Storage\Device;
use Utopia\Storage\Device\DOSpaces;
use Utopia\Storage\Device\Local;
use Utopia\Validator\Text;
class Backup extends Action
{
public const BACKUPS_PATH = '/backups';
public const BACKUP_INTERVAL_SECONDS = 60 * 60 * 1; // 4 hours;
public const COMPRESS_ALGORITHM = 'lz4';
public const CONFIG_PATH = '/etc/my.cnf';
protected ?DSN $dsn = null;
protected ?string $database = null;
protected ?DOSpaces $s3 = null;
protected string $xtrabackupContainerId;
protected int $processors = 1;
/**
* @throws Exception
*/
public function __construct()
{
$this
->desc('Backup a database')
->param('database', null, new Text(20), 'Database name, for example db_fra1_01')
->inject('pools')
->callback(fn(string $database, Group $pools) => $this->action($database, $pools));
}
public static function getName(): string
{
return 'backup';
}
/**
* @throws Exception
*/
public function action(string $database, Group $pools): void
{
$this->checkEnvVariables();
$this->database = $database;
$this->dsn = $this->getDsn($database);
if (is_null($this->dsn)) {
Console::error('No DSN match');
Console::exit();
}
console::info('Trying to connect to ' . $this->dsn->getHost() . ' : ' . $this->dsn->getPort() . ' user: ' . $this->dsn->getUser() . ' password: ' . $this->dsn->getPassword());
try {
$dsn = new DSN(App::getEnv('_APP_CONNECTIONS_BACKUPS_STORAGE', ''));
$this->s3 = new DOSpaces('/' . $database . '/full', $dsn->getUser(), $dsn->getPassword(), $dsn->getPath(), $dsn->getParam('region'));
} catch (\Exception $e) {
Console::error($e->getMessage());
Console::exit();
}
$attempts = 0;
$max = 10;
$sleep = 5;
do {
try {
$attempts++;
$pools
->get('replica_' . $database)
->pop()
->getResource();
break; // leave the do-while if successful
} catch (Exception $e) {
Console::warning("Database not ready. Retrying connection ({$attempts})...");
if ($attempts >= $max) {
throw new Exception('Failed to connect to database: ' . $e->getMessage());
}
sleep($sleep);
}
} while ($attempts < $max);
$this->setContainerId();
$this->setProcessors();
sleep(20);
Console::loop(function () {
$this->start();
}, self::BACKUP_INTERVAL_SECONDS);
}
public function start(): void
{
$start = microtime(true);
$time = date('Y_m_d-H_i_s');
self::log('--- Backup Start ' . $time . ' --- ');
$filename = $time . '.tar.gz';
$local = new Local(self::BACKUPS_PATH . '/' . $this->database . '/full/' . $time);
$local->setTransferChunkSize(5 * 1024 * 1024); // 5MB
$backups = $local->getRoot() . '/files';
$tarFile = $local->getPath($filename);
$this->fullBackup($backups);
$this->tar($backups, $tarFile);
$this->upload($tarFile, $local);
self::log('--- Backup Finish ' . (microtime(true) - $start) . ' seconds --- ' . PHP_EOL . PHP_EOL);
}
public function fullBackup(string $target)
{
if (!file_exists(self::BACKUPS_PATH)) {
Console::error('Mount directory does not exist');
Console::exit();
}
if (!file_exists($target) && !mkdir($target, 0755, true)) {
Console::error('Error creating directory: ' . $target);
Console::exit();
}
$logfile = $target . '/../backup.log';
$args = [
'xtrabackup',
'--user=' . $this->dsn->getUser(),
'--password=' . $this->dsn->getPassword(),
'--host=' . $this->dsn->getHost(),
'--port=' . $this->dsn->getPort(),
'--backup',
'--strict',
'--history=' . $this->database, // logs PERCONA_SCHEMA.xtrabackup_history name attribute
'--slave-info',
'--safe-slave-backup',
'--safe-slave-backup-timeout=300',
'--check-privileges', // checks if Percona XtraBackup has all the required privileges.
'--target-dir=' . $target,
'--parallel=' . $this->processors,
'--compress=' . self::COMPRESS_ALGORITHM,
'--compress-threads=' . $this->processors,
'--rsync', // https://docs.percona.com/percona-xtrabackup/8.0/accelerate-backup-process.html
'2> ' . $logfile,
];
$cmd = 'docker exec ' . $this->xtrabackupContainerId . ' ' . implode(' ', $args);
self::log('Xtrabackup start');
shell_exec($cmd);
$stderr = shell_exec('tail -1 ' . $logfile);
if (!str_contains($stderr, 'completed OK!') || !file_exists($target . '/xtrabackup_checkpoints')) {
Console::error(date('Y-m-d H:i:s') . ' Backup failed:' . $stderr);
Console::exit();
}
if (!unlink($logfile)) {
Console::error('Error deleting: ' . $logfile);
Console::exit();
}
}
public function tar(string $directory, string $file)
{
self::log('Tar start');
$stdout = '';
$stderr = '';
$cmd = 'cd ' . $directory . ' && tar zcf ' . $file . ' . && cd ' . getcwd();
Console::execute($cmd, '', $stdout, $stderr);
if (!empty($stderr)) {
Console::error(date('Y-m-d H:i:s') . ' Tar failed:' . $stderr);
Console::exit();
}
if (!file_exists($file)) {
Console::error('Can\'t find tar file: ' . $file);
Console::exit();
}
$filesize = \filesize($file);
if ($filesize < (2 * 1024 * 1024)) {
Console::error('Tar file size is very small: ' . $file);
Console::exit();
}
}
public function upload(string $file, Device $local)
{
self::log('Upload start');
$filename = basename($file);
if (!$this->s3->exists('/')) {
Console::error('Can\'t read s3 root directory');
Console::exit();
}
try {
$destination = $this->s3->getRoot() . '/' . $filename;
if (!$local->transfer($file, $destination, $this->s3)) {
Console::error('Error uploading to ' . $destination);
Console::exit();
}
if (!$this->s3->exists($destination)) {
Console::error('File not found in destination: ' . $destination);
Console::exit();
}
} catch (Exception $e) {
Console::error($e->getMessage());
Console::exit();
}
if (!unlink($file)) {
Console::error('Error deleting: ' . $file);
Console::exit();
}
}
public static function log(string $message): void
{
if (!empty($message)) {
Console::log(date('Y-m-d H:i:s') . ' ' . $message);
}
}
public function checkEnvVariables(): void
{
foreach (
[
'_APP_CONNECTIONS_BACKUPS_STORAGE',
'_APP_CONNECTIONS_DB_REPLICAS',
] as $env
) {
if (empty(App::getEnv($env))) {
Console::error('Can\'t read ' . $env);
Console::exit();
}
}
}
public function getDsn(string $database): ?DSN
{
foreach (explode(',', App::getEnv('_APP_CONNECTIONS_DB_REPLICAS', '')) as $project) {
[$db, $dsn] = explode('=', $project);
if ($db === $database) {
return new DSN($dsn);
}
}
return null;
}
public function setContainerId()
{
$stdout = '';
$stderr = '';
Console::execute('docker ps -f "name=xtrabackup" --format "{{.ID}}"', '', $stdout, $stderr);
if (!empty($stderr)) {
Console::error('Error setting container Id: ' . $stderr);
Console::exit();
}
$containerId = str_replace(PHP_EOL, '', $stdout);
if (empty($containerId)) {
Console::error('Xtrabackup Container ID not found');
Console::exit();
}
$this->xtrabackupContainerId = $containerId;
}
public function setProcessors()
{
$stdout = '';
$stderr = '';
Console::execute('docker exec ' . $this->xtrabackupContainerId . ' nproc', '', $stdout, $stderr);
if (!empty($stderr)) {
Console::error('Error setting processors: ' . $stderr);
Console::exit();
}
$processors = str_replace(PHP_EOL, '', $stdout);
$processors = intval($processors);
if ($processors === 0) {
Console::error('Set Processors Error');
Console::exit();
}
$this->processors = $processors;
}
}
+46 -19
View File
@@ -6,7 +6,8 @@ use Appwrite\Auth\Auth;
use Appwrite\Docker\Compose;
use Appwrite\Docker\Env;
use Appwrite\Utopia\View;
use Utopia\Analytics\GoogleAnalytics;
use Utopia\Analytics\Adapter\GoogleAnalytics;
use Utopia\Analytics\Event;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Validator\Text;
@@ -14,6 +15,8 @@ use Utopia\Platform\Action;
class Install extends Action
{
protected string $path = '/usr/src/code/appwrite';
public static function getName(): string
{
return 'install';
@@ -50,7 +53,6 @@ class Install extends Action
* 6. Run data migration
*/
$config = Config::getParam('variables');
$path = '/usr/src/code/appwrite';
$defaultHTTPPort = '80';
$defaultHTTPSPort = '443';
$vars = [];
@@ -70,19 +72,28 @@ class Install extends Action
Console::success('Starting Appwrite installation...');
// Create directory with write permissions
if (null !== $path && !\file_exists(\dirname($path))) {
if (!@\mkdir(\dirname($path), 0755, true)) {
Console::error('Can\'t create directory ' . \dirname($path));
if (null !== $this->path && !\file_exists(\dirname($this->path))) {
if (!@\mkdir(\dirname($this->path), 0755, true)) {
Console::error('Can\'t create directory ' . \dirname($this->path));
Console::exit(1);
}
}
$data = @file_get_contents($path . '/docker-compose.yml');
$data = @file_get_contents($this->path . '/docker-compose.yml');
if ($data !== false) {
if ($interactive == 'Y' && Console::isInteractive()) {
$answer = Console::confirm('Previous installation found, do you want to overwrite it? (Y/n)');
if ($answer !== 'Y') {
Console::info('No action taken.');
return;
}
}
$time = \time();
Console::info('Compose file found, creating backup: docker-compose.yml.' . $time . '.backup');
file_put_contents($path . '/docker-compose.yml.' . $time . '.backup', $data);
file_put_contents($this->path . '/docker-compose.yml.' . $time . '.backup', $data);
$compose = new Compose($data);
$appwrite = $compose->getService('appwrite');
$oldVersion = ($appwrite) ? $appwrite->getImageVersion() : null;
@@ -116,11 +127,11 @@ class Install extends Action
}
}
$data = @file_get_contents($path . '/.env');
$data = @file_get_contents($this->path . '/.env');
if ($data !== false) { // Fetch all env vars from previous .env file
Console::info('Env file found, creating backup: .env.' . $time . '.backup');
file_put_contents($path . '/.env.' . $time . '.backup', $data);
file_put_contents($this->path . '/.env.' . $time . '.backup', $data);
$env = new Env($data);
foreach ($env->list() as $key => $value) {
@@ -198,8 +209,8 @@ class Install extends Action
}
}
$templateForCompose = new View(__DIR__ . '/../views/install/compose.phtml');
$templateForEnv = new View(__DIR__ . '/../views/install/env.phtml');
$templateForCompose = new View(__DIR__ . '/../../../../app/views/install/compose.phtml');
$templateForEnv = new View(__DIR__ . '/../../../../app/views/install/env.phtml');
$templateForCompose
->setParam('httpPort', $httpPort)
@@ -213,16 +224,24 @@ class Install extends Action
->setParam('vars', $input)
;
if (!file_put_contents($path . '/docker-compose.yml', $templateForCompose->render(false))) {
if (!file_put_contents($this->path . '/docker-compose.yml', $templateForCompose->render(false))) {
$message = 'Failed to save Docker Compose file';
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
$event = new Event();
$event->setName(APP_VERSION_STABLE . ' - ' . $message)
->addProp('category', 'install/server')
->addProp('action', 'install');
$analytics->createEvent($event);
Console::error($message);
Console::exit(1);
}
if (!file_put_contents($path . '/.env', $templateForEnv->render(false))) {
if (!file_put_contents($this->path . '/.env', $templateForEnv->render(false))) {
$message = 'Failed to save environment variables file';
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
$event = new Event();
$event->setName(APP_VERSION_STABLE . ' - ' . $message)
->addProp('category', 'install/server')
->addProp('action', 'install');
$analytics->createEvent($event);
Console::error($message);
Console::exit(1);
}
@@ -237,19 +256,27 @@ class Install extends Action
}
}
Console::log("Running \"docker compose -f {$path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes\"");
Console::log("Running \"docker compose -f {$this->path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes\"");
$exit = Console::execute("${env} docker compose -f {$path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr);
$exit = Console::execute("${env} docker compose -f {$this->path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr);
if ($exit !== 0) {
$message = 'Failed to install Appwrite dockers';
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
$event = new Event();
$event->setName(APP_VERSION_STABLE . ' - ' . $message)
->addProp('category', 'install/server')
->addProp('action', 'install');
$analytics->createEvent($event);
Console::error($message);
Console::error($stderr);
Console::exit($exit);
} else {
$message = 'Appwrite installed successfully';
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
$event = new Event();
$event->setName(APP_VERSION_STABLE . ' - ' . $message)
->addProp('category', 'install/server')
->addProp('action', 'install');
$analytics->createEvent($event);
Console::success($message);
}
}
+309
View File
@@ -0,0 +1,309 @@
<?php
namespace Appwrite\Platform\Tasks;
use Exception;
use Utopia\App;
use Utopia\DSN\DSN;
use Utopia\Platform\Action;
use Utopia\CLI\Console;
use Utopia\Storage\Device;
use Utopia\Storage\Device\DOSpaces;
use Utopia\Storage\Device\Local;
use Utopia\Validator\Boolean;
use Utopia\Validator\Text;
class Restore extends Action
{
public const BACKUPS_PATH = '/backups';
public const DATADIR = '/var/lib/mysql';
protected ?DOSpaces $s3 = null;
protected string $xtrabackupContainerId;
protected int $processors = 1;
public function __construct()
{
$this
->desc('Restore a DB')
->param('id', '', new Text(20), 'The backup identification')
->param('cloud', null, new Boolean(true), 'Download backup from cloud or use local directory')
->param('database', null, new Text(10), 'The Database name for example db_fra1_01')
->callback(fn ($id, $cloud, $project) => $this->action($id, $cloud, $project));
}
public static function getName(): string
{
return 'restore';
}
public function action(string $id, string $cloud, string $database): void
{
$this->checkEnvVariables();
$this->setContainerId();
$this->setProcessors();
$datadir = self::DATADIR;
try {
$dsn = new DSN(App::getEnv('_APP_CONNECTIONS_BACKUPS_STORAGE', ''));
$this->s3 = new DOSpaces('/' . $database . '/full', $dsn->getUser(), $dsn->getPassword(), $dsn->getPath(), $dsn->getParam('region'));
} catch (\Exception $e) {
Console::error($e->getMessage() . 'Invalid DSN.');
Console::exit();
}
if (!file_exists($datadir)) {
Console::error('Datadir not found: ' . $datadir);
Console::exit();
}
if (file_exists($datadir . '/sys') || file_exists($datadir . '/appwrite')) {
Console::error('Datadir ' . $datadir . ' must be empty!');
Console::exit();
}
$this->log('--- Restore Start ' . $id . ' --- ');
$filename = $id . '.tar.gz';
$start = microtime(true);
$cloud = $cloud === 'true' || $cloud === '1';
if ($cloud) {
$local = new Local(self::BACKUPS_PATH . '/downloads/' . $id);
$files = $local->getRoot() . '/files';
if (!file_exists($files) && !mkdir($files, 0755, true)) {
Console::error('Error creating directory: ' . $files);
Console::exit();
}
$file = $local->getPath($filename);
if (!file_exists($file)) {
$this->download($file, $local);
}
$this->untar($file, $files);
} else {
$local = new Local(self::BACKUPS_PATH . '/' . $database . '/full/' . $id);
$files = $local->getRoot() . '/files';
}
if (!file_exists($files)) {
Console::error('Directory not found: ' . $files);
Console::exit();
}
$this->decompress($files);
$this->prepare($files);
$this->restore($files, $cloud, $datadir);
$this->log('Restore Finish in ' . (microtime(true) - $start) . ' seconds');
}
public function download(string $file, Device $local)
{
$this->log('Download start');
$filename = basename($file);
try {
$path = $this->s3->getPath($filename);
if (!$this->s3->exists($path)) {
Console::error('File: ' . $path . ' does not exist on cloud');
Console::exit();
}
if (!$this->s3->transfer($path, $file, $local)) {
Console::error('Error Downloading ' . $file);
Console::exit();
}
} catch (Exception $e) {
Console::error($e->getMessage());
Console::exit();
}
}
public function untar(string $file, string $directory)
{
$this->log('Untar Start');
$stdout = '';
$stderr = '';
$cmd = 'tar -xzf ' . $file . ' -C ' . $directory;
Console::execute($cmd, '', $stdout, $stderr);
if (!empty($stderr)) {
Console::error($stderr);
Console::exit();
}
if (!file_exists($file)) {
Console::error('Restore file not found: ' . $file);
Console::exit();
}
}
public function decompress(string $target)
{
$this->log('Decompress start');
$logfile = $target . '/../log.txt';
$args = [
'xtrabackup',
'--decompress',
'--strict',
'--remove-original', // Removes *.lz4 compressed files
'--parallel=' . $this->processors,
'--compress-threads=' . $this->processors,
'--target-dir=' . $target,
'2> ' . $logfile,
];
$cmd = 'docker exec ' . $this->xtrabackupContainerId . ' ' . implode(' ', $args);
shell_exec($cmd);
$stderr = shell_exec('tail -1 ' . $logfile);
if (!str_contains($stderr, 'completed OK!')) {
Console::error('Decompress failed');
Console::exit();
}
}
public function prepare(string $target)
{
$this->log('Prepare start');
if (!file_exists($target)) {
Console::error('prepare error directory not found: ' . $target);
Console::exit();
}
$logfile = $target . '/../log.txt';
$args = [
'xtrabackup',
'--prepare',
'--strict',
'--target-dir=' . $target,
'2> ' . $logfile,
];
$cmd = 'docker exec ' . $this->xtrabackupContainerId . ' ' . implode(' ', $args);
shell_exec($cmd);
$stderr = shell_exec('tail -1 ' . $logfile);
if (!str_contains($stderr, 'completed OK!')) {
Console::error(date('Y-m-d H:i:s') . ' Prepare failed:' . $stderr);
Console::exit();
}
}
public function restore(string $target, bool $cloud, string $datadir)
{
$this->log('Restore start');
if (!file_exists($target)) {
Console::error('restore error directory not found: ' . $target);
Console::exit();
}
$logfile = $target . '/../log.txt';
$args = [
'xtrabackup',
$cloud ? '--move-back' : '--copy-back',
'--strict',
'--target-dir=' . $target,
'--datadir=' . $datadir,
'--parallel=' . $this->processors,
'2> ' . $logfile,
];
$cmd = 'docker exec ' . $this->xtrabackupContainerId . ' ' . implode(' ', $args);
shell_exec($cmd);
$stderr = shell_exec('tail -1 ' . $logfile);
if (!str_contains($stderr, 'completed OK!')) {
Console::error(date('Y-m-d H:i:s') . ' Restore failed:' . $stderr);
Console::exit();
}
}
public function checkEnvVariables(): void
{
foreach (
[
'_APP_CONNECTIONS_BACKUPS_STORAGE',
'_APP_CONNECTIONS_DB_REPLICAS',
] as $env
) {
if (empty(App::getEnv($env))) {
Console::error('Can\'t read ' . $env);
Console::exit();
}
}
}
public function log(string $message): void
{
if (!empty($message)) {
Console::log(date('Y-m-d H:i:s') . ' ' . $message);
}
}
public function getDsn(string $database): ?DSN
{
foreach (explode(',', App::getEnv('_APP_CONNECTIONS_DB_REPLICAS', '')) as $project) {
[$db, $dsn] = explode('=', $project);
if ($db === $database) {
return new DSN($dsn);
}
}
return null;
}
public function setContainerId()
{
$stdout = '';
$stderr = '';
Console::execute('docker ps -f "name=xtrabackup" --format "{{.ID}}"', '', $stdout, $stderr);
if (!empty($stderr)) {
Console::error('Error setting container Id: ' . $stderr);
Console::exit();
}
$containerId = str_replace(PHP_EOL, '', $stdout);
if (empty($containerId)) {
Console::error('Xtrabackup Container ID not found');
Console::exit();
}
$this->xtrabackupContainerId = $containerId;
}
public function setProcessors()
{
$stdout = '';
$stderr = '';
Console::execute('docker exec ' . $this->xtrabackupContainerId . ' nproc', '', $stdout, $stderr);
if (!empty($stderr)) {
Console::error('Error setting processors: ' . $stderr);
Console::exit();
}
$processors = str_replace(PHP_EOL, '', $stdout);
$processors = intval($processors);
if ($processors === 0) {
Console::error('Set Processors Error');
Console::exit();
}
$this->processors = $processors;
}
}
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace Appwrite\Platform\Tasks;
use Utopia\CLI\Console;
use Utopia\Validator\Text;
class Upgrade extends Install
{
public static function getName(): string
{
return 'upgrade';
}
public function __construct()
{
$this
->desc('Upgrade Appwrite')
->param('httpPort', '', new Text(4), 'Server HTTP port', true)
->param('httpsPort', '', new Text(4), 'Server HTTPS port', true)
->param('organization', 'appwrite', new Text(0), 'Docker Registry organization', true)
->param('image', 'appwrite', new Text(0), 'Main appwrite docker image', true)
->param('interactive', 'Y', new Text(1), 'Run an interactive session', true)
->callback(fn ($httpPort, $httpsPort, $organization, $image, $interactive) => $this->action($httpPort, $httpsPort, $organization, $image, $interactive));
}
public function action(string $httpPort, string $httpsPort, string $organization, string $image, string $interactive): void
{
// Check for previous installation
$data = @file_get_contents($this->path . '/docker-compose.yml');
if (empty($data)) {
Console::error('Appwrite installation not found.');
Console::log('The command was not run in the parent folder of your appwrite installation.');
Console::log('Please navigate to the parent directory of the Appwrite installation and try again.');
Console::log(' parent_directory <= you run the command in this directory');
Console::log(' └── appwrite');
Console::log(' └── docker-compose.yml');
Console::exit(1);
}
parent::action($httpPort, $httpsPort, $organization, $image, $interactive);
}
}