238 lines
7.2 KiB
Python
238 lines
7.2 KiB
Python
import os
|
|
import time
|
|
from pathlib import Path
|
|
|
|
import docker
|
|
from loguru import logger
|
|
|
|
from odoo_openupgrade_wizard.tools.tools_docker import (
|
|
exec_container,
|
|
get_docker_client,
|
|
run_container,
|
|
)
|
|
from odoo_openupgrade_wizard.tools.tools_system import get_script_folder
|
|
|
|
|
|
def get_postgres_container(ctx):
|
|
client = get_docker_client()
|
|
image_name = ctx.obj["config"]["postgres_image_name"]
|
|
container_name = ctx.obj["config"]["postgres_container_name"]
|
|
volume_name = ctx.obj["config"]["postgres_volume_name"]
|
|
|
|
# Check if container exists
|
|
containers = client.containers.list(
|
|
all=True, filters={"name": container_name}
|
|
)
|
|
if containers:
|
|
container = containers[0]
|
|
if container.status == "exited":
|
|
logger.warning(
|
|
"Found container %s in a exited status. Removing it..."
|
|
% container_name
|
|
)
|
|
container.remove()
|
|
else:
|
|
return container
|
|
|
|
# Check if volume exists
|
|
try:
|
|
client.volumes.get(volume_name)
|
|
logger.debug("Recovering existing postgres volume: %s" % volume_name)
|
|
except docker.errors.NotFound:
|
|
logger.info("Creating Postgres volume: %s" % volume_name)
|
|
client.volumes.create(volume_name)
|
|
|
|
command = None
|
|
postgres_extra_settings = ctx.obj["config"].get("postgres_extra_settings")
|
|
if postgres_extra_settings:
|
|
command = "postgres"
|
|
for key, value in postgres_extra_settings.items():
|
|
command += f" -c {key}={value}"
|
|
|
|
logger.info("Launching Postgres Container. (Image %s)" % image_name)
|
|
container = run_container(
|
|
image_name,
|
|
container_name,
|
|
command=command,
|
|
environments={
|
|
"POSTGRES_USER": "odoo",
|
|
"POSTGRES_PASSWORD": "odoo",
|
|
"POSTGRES_DB": "postgres",
|
|
"PGDATA": "/var/lib/postgresql/data/pgdata",
|
|
},
|
|
volumes={
|
|
# Data volume
|
|
volume_name: "/var/lib/postgresql/data/pgdata/",
|
|
# main folder path (to pass files)
|
|
ctx.obj["env_folder_path"].absolute(): "/env/",
|
|
},
|
|
detach=True,
|
|
)
|
|
# TODO, improve me.
|
|
# Postgres container doesn't seems available immediately.
|
|
# check in odoo container, i remember that there is
|
|
# some script to do the job
|
|
time.sleep(5)
|
|
return container
|
|
|
|
|
|
def execute_sql_file(ctx, database, sql_file):
|
|
container = get_postgres_container(ctx)
|
|
|
|
# Recreate relative path to make posible to
|
|
# call psql in the container
|
|
if str(ctx.obj["env_folder_path"]) not in str(sql_file):
|
|
raise Exception(
|
|
"The SQL file %s is not in the"
|
|
" main folder %s available"
|
|
" in the postgres container."
|
|
% (sql_file, ctx.obj["env_folder_path"])
|
|
)
|
|
relative_path = Path(
|
|
str(sql_file).replace(str(ctx.obj["env_folder_path"]), ".")
|
|
)
|
|
|
|
container_path = Path("/env/") / relative_path
|
|
command = (
|
|
"psql --username=odoo --dbname={database} --file {file_path}"
|
|
).format(database=database, file_path=container_path)
|
|
logger.info(
|
|
"Executing the script '%s' in postgres container"
|
|
" on database %s" % (relative_path, database)
|
|
)
|
|
exec_container(container, command)
|
|
|
|
|
|
def execute_sql_request(ctx, request, database="postgres"):
|
|
psql_args = ("--tuples-only",)
|
|
output = execute_psql_command(ctx, request, database, psql_args)
|
|
lines = output.split("\n")
|
|
result = []
|
|
for line in lines:
|
|
if not line:
|
|
continue
|
|
result.append([x.strip() for x in line.split("|")])
|
|
return result
|
|
|
|
|
|
def execute_psql_command(
|
|
ctx, request: str, database: str = None, psql_args: tuple = ()
|
|
):
|
|
"""Execute psql request in postgres container with psql_args on database"""
|
|
container = get_postgres_container(ctx)
|
|
command = (
|
|
"psql"
|
|
" --username=odoo"
|
|
f" --dbname={database or 'postgres'}"
|
|
f' --command "{request}"'
|
|
f" {' '.join(psql_args)}"
|
|
)
|
|
logger.debug(
|
|
"Executing the following command in postgres container\n"
|
|
"%s" % (command)
|
|
)
|
|
docker_result = exec_container(container, command)
|
|
return docker_result.output.decode("utf-8")
|
|
|
|
|
|
def ensure_database(ctx, database: str, state="present"):
|
|
"""
|
|
- Connect to postgres container.
|
|
- Check if the database exist.
|
|
- if doesn't exists and state == 'present', create it.
|
|
- if exists and state == 'absent', drop it.
|
|
"""
|
|
request = "select datname FROM pg_database WHERE datistemplate = false;"
|
|
|
|
result = execute_sql_request(ctx, request)
|
|
|
|
if state == "present":
|
|
if [database] in result:
|
|
return
|
|
|
|
logger.info("Create database '%s' ..." % database)
|
|
request = "CREATE DATABASE {database} owner odoo;".format(
|
|
database=database
|
|
)
|
|
execute_sql_request(ctx, request)
|
|
else:
|
|
if [database] not in result:
|
|
return
|
|
|
|
logger.info("Drop database '%s' ..." % database)
|
|
request = "DROP DATABASE {database};".format(database=database)
|
|
execute_sql_request(ctx, request)
|
|
|
|
|
|
def execute_sql_files_pre_migration(
|
|
ctx, database: str, migration_step: dict, sql_files: list = []
|
|
):
|
|
ensure_database(ctx, database, state="present")
|
|
if not sql_files:
|
|
script_folder = get_script_folder(ctx, migration_step)
|
|
sql_files = [
|
|
script_folder / Path(f)
|
|
for f in os.listdir(script_folder)
|
|
if os.path.isfile(os.path.join(script_folder, f))
|
|
and f[-4:] == ".sql"
|
|
]
|
|
sql_files = sorted(sql_files)
|
|
|
|
for sql_file in sql_files:
|
|
execute_sql_file(ctx, database, sql_file)
|
|
|
|
|
|
def chown_to_local_user(ctx, filepath: os.PathLike):
|
|
"""Chown a filepath in the postgres container to the local user"""
|
|
container = get_postgres_container(ctx)
|
|
user_uid = os.getuid()
|
|
command = "chown -R {uid}:{uid} {filepath}".format(
|
|
uid=user_uid, filepath=filepath
|
|
)
|
|
logger.debug(
|
|
"Executing the following command in postgres container: %s"
|
|
% (command,)
|
|
)
|
|
chown_result = exec_container(container, command)
|
|
return chown_result.output.decode("utf8")
|
|
|
|
|
|
def execute_pg_dump(
|
|
ctx,
|
|
database: str,
|
|
dumpformat: str,
|
|
filename: str,
|
|
pg_dump_args="--no-owner",
|
|
):
|
|
"""Execute pg_dump command on the postgres container and dump the
|
|
result to dumpfile.
|
|
"""
|
|
if pg_dump_args and not isinstance(pg_dump_args, str):
|
|
pg_dump_args = " ".join(pg_dump_args)
|
|
container = get_postgres_container(ctx)
|
|
# Generate path for the output file
|
|
filepath = Path("/env") / Path(filename)
|
|
# Generate pg_dump command
|
|
command = (
|
|
"pg_dump"
|
|
" --username=odoo"
|
|
" --format {dumpformat}"
|
|
" --file {filepath}"
|
|
" {pg_dump_args}"
|
|
" {database}"
|
|
).format(
|
|
dumpformat=dumpformat,
|
|
filepath=filepath,
|
|
database=database,
|
|
pg_dump_args=pg_dump_args,
|
|
)
|
|
logger.debug(
|
|
"Executing the following command in postgres container: %s"
|
|
% (command,)
|
|
)
|
|
pg_dump_result = exec_container(container, command)
|
|
|
|
chown_to_local_user(ctx, filepath)
|
|
|
|
return pg_dump_result.output.decode("utf8")
|