diff --git a/attachment_azure/README.rst b/attachment_azure/README.rst index 48e3c99..f3837eb 100644 --- a/attachment_azure/README.rst +++ b/attachment_azure/README.rst @@ -13,20 +13,44 @@ Activate Azure Blob storage: * Create or set the system parameter with the key ``ir_attachment.location`` and the value in the form ``azure``. -Configure accesses with environment variables: +* Configure accesses with the following environment variables: -* ``AZURE_STORAGE_CONNECTION_STRING`` or -* ``AZURE_STORAGE_ACCOUNT_NAME`` -* ``AZURE_STORAGE_ACCOUNT_URL`` -* ``AZURE_STORAGE_ACCOUNT_KEY`` +.. list-table:: + :header-rows: 1 + + * - Name + - Description + - Values + * - ``AZURE_STORAGE_CONNECTION_STRING`` + - Connection string. + - Required if using the connection string authentication method. + * - ``AZURE_STORAGE_ACCOUNT_NAME`` + - Name of the storage account. + - Required if using the storage account authentication method. + * - ``AZURE_STORAGE_ACCOUNT_URL`` + - URL of the storage account + - + * - ``AZURE_STORAGE_ACCOUNT_KEY`` + - Key of the storage account + - + * - ``AZURE_STORAGE_NAME`` + - Name of the container within the blob storage. 1 container per database will be + created. + - Optional. The strings ``{db}`` and ``{env}`` can be used inside that variable + and the values will be replaced respectively by the database name and environment + name. + * - ``AZURE_DUPLICATE`` + - If set, the blob storage and all its objects will be copied when the database is + duplicated. + - True + * - ``AZURE_DELETE_ON_DBDROP`` + - If set, the container and all its objects will be deleted when the database is + dropped. + - True One container will be created per database using the `RUNNING_ENV` environment variable and the name of the database. By default, `RUNNING_ENV` is set to `dev`. -The container name can be overridden with environment variable ``AZURE_STORAGE_NAME``. -The strings ``{db}`` and ``{env}`` can be used inside that variable and the values -will be replaced respectively by the database name and environment name. - This addon must be added in the server wide addons with (``--load`` option): ``--load=web,attachment_azure`` diff --git a/attachment_azure/__init__.py b/attachment_azure/__init__.py index 49d7105..41582ca 100644 --- a/attachment_azure/__init__.py +++ b/attachment_azure/__init__.py @@ -1,4 +1,20 @@ # Copyright 2016-2019 Camptocamp SA # Copyright 2021 Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +import os + from . import models +from . import controllers + +from odoo import api, SUPERUSER_ID + + +def _post_init_hook(cr, registry): + # create the S3 bucket after module installation + if os.environ.get("AZURE_STORAGE_CONNECTION_STRING", False): + env = api.Environment(cr, SUPERUSER_ID, {}) + env["ir.attachment"]._get_azure_container() + env["ir.config_parameter"].create( + {"key": "ir_attachment.location", "value": "azure"} + ) + env["ir.attachment"].force_storage() diff --git a/attachment_azure/__manifest__.py b/attachment_azure/__manifest__.py index f685f39..9264084 100644 --- a/attachment_azure/__manifest__.py +++ b/attachment_azure/__manifest__.py @@ -18,5 +18,6 @@ "website": "https://github.com/camptocamp/odoo-cloud-platform", "installable": True, "development_status": "Beta", + "post_init_hook": "_post_init_hook", "maintainers": ["max3903"], } diff --git a/attachment_azure/controllers/__init__.py b/attachment_azure/controllers/__init__.py new file mode 100644 index 0000000..12a7e52 --- /dev/null +++ b/attachment_azure/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/attachment_azure/controllers/main.py b/attachment_azure/controllers/main.py new file mode 100644 index 0000000..2a28987 --- /dev/null +++ b/attachment_azure/controllers/main.py @@ -0,0 +1,66 @@ +import logging +import os + +from odoo.addons.web.controllers.main import Database +from odoo import http +from odoo import exceptions +from odoo.http import request + +_logger = logging.getLogger(__name__) + + +class Database(Database): + @http.route() + def drop(self, master_pwd, name): + res = super().drop(master_pwd, name) + delete = os.environ.get("AZURE_DELETE_ON_DBDROP", False) + if delete: + attachment_obj = request.env["ir.attachment"] + try: + container_name = attachment_obj._get_container_name() + blob_service_client = attachment_obj._get_blob_service_client() + container_client = blob_service_client.get_container_client( + container_name + ) + container_client.delete_container() + except exceptions.UserError: + _logger.exception("Error deleting attachments from object storage.") + return res + + @http.route() + def duplicate(self, master_pwd, name, new_name): + res = super().duplicate(master_pwd, name, new_name) + duplicate = os.environ.get("AWS_DUPLICATE", False) + if duplicate: + attachment_obj = request.env["ir.attachment"] + try: + copy_from_container = attachment_obj._get_container_name() + copy_to_container = attachment_obj._get_container_name(new_name) + blob_service = attachment_obj._get_blob_service_client() + container_client = blob_service.get_container_client( + copy_from_container + ) + + blobs_list = container_client.list_blobs() + for blob in blobs_list: + blob_url = container_client.make_blob_url( + copy_from_container, blob.name + ) + container_client.copy_blob(copy_to_container, blob.name, blob_url) + except exceptions.UserError: + _logger.exception("Error writing attachments to object storage.") + return res + + @http.route() + def restore(self, master_pwd, backup_file, name, copy=False): + res = super().restore(master_pwd, backup_file, name, copy) + attachment_obj = request.env["ir.attachment"] + try: + attachment_obj._get_azure_container() + request.env["ir.config_parameter"].create( + {"key": "ir_attachment.location", "value": "azure"} + ) + attachment_obj.force_storage() + except exceptions.UserError: + _logger.exception("Error writing attachments to object storage.") + return res diff --git a/attachment_azure/models/ir_attachment.py b/attachment_azure/models/ir_attachment.py index dcf76fc..3b6fb3f 100644 --- a/attachment_azure/models/ir_attachment.py +++ b/attachment_azure/models/ir_attachment.py @@ -111,16 +111,15 @@ class IrAttachment(models.Model): return blob_service_client @api.model - def _get_container_name(self): + def _get_container_name(self, db_name=None): """ Container naming rules: https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names """ running_env = os.environ.get("RUNNING_ENV", "dev") - storage_name = os.environ.get('AZURE_STORAGE_NAME', r'{env}-{db}') + storage_name = os.environ.get("AZURE_STORAGE_NAME", r"{env}-{db}") storage_name = storage_name.format( - env=running_env, - db=self.env.cr.dbname + env=running_env, db=db_name or self.env.cr.dbname ) # replace invalid characters by _ storage_name = re.sub(r"[\W_]+", "-", storage_name)