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_install_from_csv import install_from_csv
|
||||||
from odoo_openupgrade_wizard.cli.cli_psql import psql
|
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_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_run import run
|
||||||
from odoo_openupgrade_wizard.cli.cli_upgrade import upgrade
|
from odoo_openupgrade_wizard.cli.cli_upgrade import upgrade
|
||||||
from odoo_openupgrade_wizard.tools.tools_system import ensure_folder_exists
|
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(copydb)
|
||||||
|
main.add_command(restoredb)
|
||||||
main.add_command(docker_build)
|
main.add_command(docker_build)
|
||||||
main.add_command(dropdb)
|
main.add_command(dropdb)
|
||||||
main.add_command(dumpdb)
|
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)
|
pg_dump_result = exec_container(container, command)
|
||||||
|
|
||||||
chown_to_local_user(ctx, filepath)
|
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")
|
return pg_dump_result.output.decode("utf8")
|
||||||
|
|
|
||||||
|
|
@ -143,3 +143,33 @@ def dump_filestore(
|
||||||
|
|
||||||
with tarfile.open(destpath, wmode) as tar:
|
with tarfile.open(destpath, wmode) as tar:
|
||||||
tar.add(filestore_path, arcname="filestore")
|
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 plumbum.cmd import mkdir
|
||||||
|
|
||||||
from odoo_openupgrade_wizard.cli.cli import main
|
from odoo_openupgrade_wizard.cli.cli import main
|
||||||
|
from odoo_openupgrade_wizard.tools.tools_postgres import execute_sql_request
|
||||||
|
|
||||||
_logger = logging.getLogger()
|
_logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
@ -91,3 +92,12 @@ def mock_odoo_rpc_url(mocker):
|
||||||
"odoo_openupgrade_wizard.tools.tools_odoo_instance._ODOO_RPC_URL",
|
"odoo_openupgrade_wizard.tools.tools_odoo_instance._ODOO_RPC_URL",
|
||||||
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 odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
|
assert_database,
|
||||||
build_ctx_from_config_file,
|
build_ctx_from_config_file,
|
||||||
cli_runner_invoke,
|
cli_runner_invoke,
|
||||||
mock_odoo_rpc_url,
|
mock_odoo_rpc_url,
|
||||||
|
|
@ -46,5 +47,9 @@ def test_cli_copydb(mocker):
|
||||||
# check filestore exists
|
# check filestore exists
|
||||||
assert dest_filestore_path.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)
|
shutil.rmtree(dest_filestore_path)
|
||||||
|
ensure_database(ctx, db_dest_name, state="absent")
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
import pathlib
|
import pathlib
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from odoo_openupgrade_wizard.tools.tools_postgres import (
|
from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
|
||||||
ensure_database,
|
|
||||||
execute_sql_request,
|
|
||||||
)
|
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
|
assert_database,
|
||||||
build_ctx_from_config_file,
|
build_ctx_from_config_file,
|
||||||
cli_runner_invoke,
|
cli_runner_invoke,
|
||||||
mock_odoo_rpc_url,
|
mock_odoo_rpc_url,
|
||||||
|
|
@ -45,9 +43,7 @@ def test_cli_dropdb(mocker):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check database does not exists
|
# Check database does not exists
|
||||||
request = "select datname FROM pg_database WHERE datistemplate = false;"
|
assert_database(ctx, db_name, "absent")
|
||||||
results = execute_sql_request(ctx, request)
|
|
||||||
assert [db_name] not in results
|
|
||||||
|
|
||||||
# Check filestore does not exists
|
# Check filestore does not exists
|
||||||
assert not filestore_path.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