Merge branch 'ADD-restoredb' into 'main'
[ADD] restoredb function See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!40
This commit is contained in:
commit
567ffd4428
|
|
@ -28,6 +28,7 @@ from odoo_openupgrade_wizard.cli.cli_init import init
|
|||
from odoo_openupgrade_wizard.cli.cli_install_from_csv import install_from_csv
|
||||
from odoo_openupgrade_wizard.cli.cli_psql import psql
|
||||
from odoo_openupgrade_wizard.cli.cli_pull_submodule import pull_submodule
|
||||
from odoo_openupgrade_wizard.cli.cli_restoredb import restoredb
|
||||
from odoo_openupgrade_wizard.cli.cli_run import run
|
||||
from odoo_openupgrade_wizard.cli.cli_upgrade import upgrade
|
||||
from odoo_openupgrade_wizard.tools.tools_system import ensure_folder_exists
|
||||
|
|
@ -114,6 +115,7 @@ def main(ctx, env_folder, filestore_folder, log_level):
|
|||
|
||||
|
||||
main.add_command(copydb)
|
||||
main.add_command(restoredb)
|
||||
main.add_command(docker_build)
|
||||
main.add_command(dropdb)
|
||||
main.add_command(dumpdb)
|
||||
|
|
|
|||
78
odoo_openupgrade_wizard/cli/cli_restoredb.py
Normal file
78
odoo_openupgrade_wizard/cli/cli_restoredb.py
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
from pathlib import Path
|
||||
|
||||
import click
|
||||
|
||||
from odoo_openupgrade_wizard.cli.cli_options import database_option_required
|
||||
from odoo_openupgrade_wizard.tools.tools_postgres import execute_pg_restore
|
||||
from odoo_openupgrade_wizard.tools.tools_system import restore_filestore
|
||||
|
||||
|
||||
@click.command()
|
||||
@database_option_required
|
||||
@click.option(
|
||||
"--database-path",
|
||||
required=True,
|
||||
type=click.Path(readable=True, resolve_path=True),
|
||||
help="Path to the database dump relative project folder.",
|
||||
)
|
||||
@click.option(
|
||||
"--database-format",
|
||||
required=True,
|
||||
type=click.Choice(("c", "d", "t")),
|
||||
default="c",
|
||||
help="Database format (see pg_dump options): "
|
||||
"custom format compressed (c), directory (d), tar file (t)."
|
||||
"plain sql text (p) is not implemented",
|
||||
)
|
||||
@click.option(
|
||||
"--filestore-path",
|
||||
required=True,
|
||||
type=click.Path(readable=True, resolve_path=True),
|
||||
help="Path to the filestore backup.",
|
||||
)
|
||||
@click.option(
|
||||
"--filestore-format",
|
||||
required=True,
|
||||
type=click.Choice(("d", "t", "tgz")),
|
||||
default="tgz",
|
||||
help="Filestore format: directory (d), tar file (t), "
|
||||
"tar file compressed with gzip (tgz)",
|
||||
)
|
||||
@click.pass_context
|
||||
def restoredb(
|
||||
ctx,
|
||||
database,
|
||||
database_path,
|
||||
database_format,
|
||||
filestore_path,
|
||||
filestore_format,
|
||||
):
|
||||
"""Restore an Odoo database and associated filestore."""
|
||||
|
||||
database_path = Path(database_path)
|
||||
filestore_path = Path(filestore_path)
|
||||
|
||||
# Check that database_path is inside the env_folder_path
|
||||
absolute_env_folder_path = ctx.obj["env_folder_path"].resolve().absolute()
|
||||
if not str(database_path).startswith(str(absolute_env_folder_path)):
|
||||
ctx.fail(
|
||||
"database-path should be inside the project path to allow "
|
||||
"postgresql to read to it."
|
||||
)
|
||||
# Restore the database
|
||||
output = execute_pg_restore(
|
||||
ctx,
|
||||
database_path.relative_to(absolute_env_folder_path),
|
||||
database,
|
||||
database_format,
|
||||
)
|
||||
if output:
|
||||
click.echo(output)
|
||||
|
||||
# Restore the filestore
|
||||
restore_filestore(
|
||||
ctx,
|
||||
database,
|
||||
filestore_path,
|
||||
filestore_format,
|
||||
)
|
||||
|
|
@ -235,5 +235,28 @@ def execute_pg_dump(
|
|||
pg_dump_result = exec_container(container, command)
|
||||
|
||||
chown_to_local_user(ctx, filepath)
|
||||
|
||||
return pg_dump_result.output.decode("utf8")
|
||||
|
||||
|
||||
def execute_pg_restore(
|
||||
ctx,
|
||||
filepath: Path,
|
||||
database: str,
|
||||
database_format: str,
|
||||
):
|
||||
"""Execute pg_restore command on the postgres container"""
|
||||
container = get_postgres_container(ctx)
|
||||
ensure_database(ctx, database, "absent")
|
||||
ensure_database(ctx, database, "present")
|
||||
command = (
|
||||
"pg_restore"
|
||||
f" {Path('/env') / filepath}"
|
||||
f" --dbname={database}"
|
||||
" --schema=public"
|
||||
" --username=odoo"
|
||||
" --no-owner"
|
||||
f" --format {database_format}"
|
||||
)
|
||||
logger.info(f"Restoring database '{database}'...")
|
||||
pg_dump_result = exec_container(container, command)
|
||||
return pg_dump_result.output.decode("utf8")
|
||||
|
|
|
|||
|
|
@ -143,3 +143,33 @@ def dump_filestore(
|
|||
|
||||
with tarfile.open(destpath, wmode) as tar:
|
||||
tar.add(filestore_path, arcname="filestore")
|
||||
|
||||
|
||||
def restore_filestore(
|
||||
ctx,
|
||||
database: str,
|
||||
src_path: Path,
|
||||
file_format: str = "d",
|
||||
):
|
||||
"""Restore filestore of database from src_path using file_format.
|
||||
file_format can be :
|
||||
'd' for 'directory': a normal copy,
|
||||
't' / 'tgz' for 'tar': an extraction from a tar achive
|
||||
"""
|
||||
valid_format = ("d", "t", "tgz")
|
||||
if file_format not in valid_format:
|
||||
raise ValueError(
|
||||
f"file_format should be one of the following {valid_format}"
|
||||
)
|
||||
|
||||
filestore_path = (
|
||||
ctx.obj["env_folder_path"] / "filestore/filestore" / database
|
||||
)
|
||||
|
||||
logger.info(f"Restoring filestore of '{database}'...")
|
||||
if file_format == "d":
|
||||
shutil.copytree(src_path, filestore_path)
|
||||
|
||||
else: # works for "t" and "tgz"
|
||||
tar = tarfile.open(src_path)
|
||||
tar.extractall(path=filestore_path)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from click.testing import CliRunner
|
|||
from plumbum.cmd import mkdir
|
||||
|
||||
from odoo_openupgrade_wizard.cli.cli import main
|
||||
from odoo_openupgrade_wizard.tools.tools_postgres import execute_sql_request
|
||||
|
||||
_logger = logging.getLogger()
|
||||
|
||||
|
|
@ -91,3 +92,12 @@ def mock_odoo_rpc_url(mocker):
|
|||
"odoo_openupgrade_wizard.tools.tools_odoo_instance._ODOO_RPC_URL",
|
||||
odoo_rpc_url,
|
||||
)
|
||||
|
||||
|
||||
def assert_database(ctx, db_name, state):
|
||||
request = "select datname FROM pg_database WHERE datistemplate = false;"
|
||||
results = execute_sql_request(ctx, request)
|
||||
if state == "present":
|
||||
assert [db_name] in results
|
||||
else:
|
||||
assert [db_name] not in results
|
||||
|
|
|
|||
54
tests/cli_23_restoredb_test.py
Normal file
54
tests/cli_23_restoredb_test.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import pathlib
|
||||
import shutil
|
||||
|
||||
from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
|
||||
|
||||
from . import (
|
||||
assert_database,
|
||||
build_ctx_from_config_file,
|
||||
cli_runner_invoke,
|
||||
mock_odoo_rpc_url,
|
||||
move_to_test_folder,
|
||||
)
|
||||
|
||||
|
||||
def test_cli_restoredb(mocker):
|
||||
move_to_test_folder()
|
||||
mock_odoo_rpc_url(mocker)
|
||||
|
||||
db_name = "database_test_cli___restoredb"
|
||||
ctx = build_ctx_from_config_file()
|
||||
|
||||
# Ensure environment is clean
|
||||
ensure_database(ctx, db_name, state="absent")
|
||||
dest_filestore_path = pathlib.Path(f"./filestore/filestore/{db_name}")
|
||||
shutil.rmtree(dest_filestore_path, ignore_errors=True)
|
||||
|
||||
# Copy database and filestore data in a accessible folder
|
||||
database_path = pathlib.Path("./restoredb.dump")
|
||||
filestore_path = pathlib.Path("./restoredb.tar.gz")
|
||||
|
||||
shutil.copyfile(pathlib.Path("../restoredb/test.dump"), database_path)
|
||||
shutil.copyfile(pathlib.Path("../restoredb/test.tar.gz"), filestore_path)
|
||||
|
||||
cli_runner_invoke(
|
||||
[
|
||||
"--log-level=DEBUG",
|
||||
"restoredb",
|
||||
f"--database={db_name}",
|
||||
f"--database-path={database_path}",
|
||||
"--database-format=c",
|
||||
f"--filestore-path={filestore_path}",
|
||||
"--filestore-format=tgz",
|
||||
],
|
||||
)
|
||||
|
||||
# check filestore exists
|
||||
assert dest_filestore_path.exists()
|
||||
|
||||
# Check database exists
|
||||
assert_database(ctx, db_name, "present")
|
||||
|
||||
# Delete filestore and database
|
||||
shutil.rmtree(dest_filestore_path)
|
||||
ensure_database(ctx, db_name, state="absent")
|
||||
|
|
@ -4,6 +4,7 @@ import shutil
|
|||
from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
|
||||
|
||||
from . import (
|
||||
assert_database,
|
||||
build_ctx_from_config_file,
|
||||
cli_runner_invoke,
|
||||
mock_odoo_rpc_url,
|
||||
|
|
@ -46,5 +47,9 @@ def test_cli_copydb(mocker):
|
|||
# check filestore exists
|
||||
assert dest_filestore_path.exists()
|
||||
|
||||
# Delete filestore
|
||||
# Check database exists
|
||||
assert_database(ctx, db_dest_name, "present")
|
||||
|
||||
# Delete filestore and database
|
||||
shutil.rmtree(dest_filestore_path)
|
||||
ensure_database(ctx, db_dest_name, state="absent")
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import pathlib
|
||||
import shutil
|
||||
|
||||
from odoo_openupgrade_wizard.tools.tools_postgres import (
|
||||
ensure_database,
|
||||
execute_sql_request,
|
||||
)
|
||||
from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
|
||||
|
||||
from . import (
|
||||
assert_database,
|
||||
build_ctx_from_config_file,
|
||||
cli_runner_invoke,
|
||||
mock_odoo_rpc_url,
|
||||
|
|
@ -45,9 +43,7 @@ def test_cli_dropdb(mocker):
|
|||
)
|
||||
|
||||
# Check database does not exists
|
||||
request = "select datname FROM pg_database WHERE datistemplate = false;"
|
||||
results = execute_sql_request(ctx, request)
|
||||
assert [db_name] not in results
|
||||
assert_database(ctx, db_name, "absent")
|
||||
|
||||
# Check filestore does not exists
|
||||
assert not filestore_path.exists()
|
||||
|
|
|
|||
BIN
tests/data/restoredb/test.dump
Normal file
BIN
tests/data/restoredb/test.dump
Normal file
Binary file not shown.
BIN
tests/data/restoredb/test.tar.gz
Normal file
BIN
tests/data/restoredb/test.tar.gz
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user