Skip to content

Commit

Permalink
fs_storage: add possibility to define options values from env.
Browse files Browse the repository at this point in the history
  • Loading branch information
adrienpeiffer committed Dec 22, 2023
1 parent ad8a4d3 commit f2fe690
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 6 deletions.
34 changes: 33 additions & 1 deletion fs_storage/models/fs_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ def __init__(self, env, ids=(), prefetch_ids=()):
compute="_compute_json_options",
inverse="_inverse_json_options",
)

eval_options_from_env = fields.Boolean(
string="Resolve env vars",
help="Resolve options values starting with $ from environment variables",
)

directory_path = fields.Char(
help="Relative path to the directory to store the file"
)
Expand Down Expand Up @@ -302,6 +308,32 @@ def _recursive_add_odoo_storage_path(self, options: dict) -> dict:
self._recursive_add_odoo_storage_path(target_options)
return options

def _eval_options_from_env(self, options):
values = {}
for key, value in options.items():
if isinstance(value, dict):
values[key] = self._eval_options_from_env(value)
elif isinstance(value, str) and value.startswith("$"):
env_variable_name = value[1:]
env_variable_value = os.getenv(env_variable_name)
if env_variable_value is not None:
values[key] = env_variable_value
else:
values[key] = value
_logger.warning(
f"Environment variable {env_variable_name} is not set for "
f"fs_storage {self.display_name}."
)
else:
values[key] = value

Check warning on line 328 in fs_storage/models/fs_storage.py

View check run for this annotation

Codecov / codecov/patch

fs_storage/models/fs_storage.py#L328

Added line #L328 was not covered by tests
return values

def _get_fs_options(self):
options = self.json_options
if not self.eval_options_from_env:
return options
return self._eval_options_from_env(self.json_options)

def _get_filesystem(self) -> fsspec.AbstractFileSystem:
"""Get the fsspec filesystem for this backend.
Expand All @@ -311,7 +343,7 @@ def _get_filesystem(self) -> fsspec.AbstractFileSystem:
:return: fsspec.AbstractFileSystem
"""
self.ensure_one()
options = self.json_options
options = self._get_fs_options()
if self.protocol == "odoofs":
options["odoo_storage_path"] = self._odoo_storage_path
# Webdav protocol handler does need the auth to be a tuple not a list !
Expand Down
2 changes: 2 additions & 0 deletions fs_storage/readme/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ When you create a new backend, you must specify the following:
* The protocol options. These are the options that will be passed to the
fsspec python package when creating the filesystem. These options depend
on the protocol used and are described in the fsspec documentation.
* Resolve env vars. This options resolves the protocol options values starting
with $ from environment variables

Some protocols defined in the fsspec package are wrappers around other
protocols. For example, the SimpleCacheFileSystem protocol is a wrapper
Expand Down
1 change: 1 addition & 0 deletions fs_storage/readme/newsfragments/303.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add parameter on storage backend to resolve protocol options values starting with $ from environment variables
20 changes: 20 additions & 0 deletions fs_storage/tests/test_fs_storage.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright 2023 ACSONE SA/NV (http://acsone.eu).
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
import warnings
from unittest import mock

from odoo.tests import Form
from odoo.tools import mute_logger
Expand Down Expand Up @@ -130,3 +131,22 @@ def test_interface_values(self):
self.assertTrue("Interface to files on local storage" in description)
# this is still true after saving
self.assertEqual(new_storage.options_protocol, protocol)

def test_options_env(self):
self.backend.json_options = {"key": {"sub_key": "$KEY_VAR"}}
eval_json_options = {"key": {"sub_key": "TEST"}}
options = self.backend._get_fs_options()
self.assertDictEqual(options, self.backend.json_options)
self.backend.eval_options_from_env = True
with mock.patch.dict("os.environ", {"KEY_VAR": "TEST"}):
options = self.backend._get_fs_options()
self.assertDictEqual(options, eval_json_options)
with self.assertLogs(level="WARNING") as log:
options = self.backend._get_fs_options()
self.assertIn(
(
f"Environment variable KEY_VAR is not set for "
f"fs_storage {self.backend.display_name}."
),
log.output[0],
)
11 changes: 6 additions & 5 deletions fs_storage/views/fs_storage_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,17 @@
</div>
<group name="config">
<group>
<field name="code" />
<field name="protocol" />
<field name="directory_path" />
<field
<field name="code" />
<field name="protocol" />
<field name="directory_path" />
<field name="eval_options_from_env" />
<field
name="options"
widget="ace"
options="{'mode': 'python'}"
placeholder="Enter you fsspec options here."
/>
</group>
</group>
<group>
<notebook colspan="2">
<page string="Protocol Descr" colspan="2">
Expand Down

0 comments on commit f2fe690

Please sign in to comment.