From 4d56849fb76926c05851f926cc4cd5b6ae05a460 Mon Sep 17 00:00:00 2001 From: javierdelapuente Date: Fri, 10 May 2024 15:26:31 +0200 Subject: [PATCH] Refactor to remove wp eval code (#216) --- src-docs/charm.py.md | 5 +- src-docs/cos.py.md | 52 +++++++++++++++- src-docs/state.py.md | 112 +++++++++++++++++++++++++++++++++++ src-docs/types_.py.md | 2 +- src/charm.py | 40 +++++-------- tests/unit/test_charm.py | 67 ++++++++++----------- tests/unit/wordpress_mock.py | 8 --- 7 files changed, 210 insertions(+), 76 deletions(-) create mode 100644 src-docs/state.py.md diff --git a/src-docs/charm.py.md b/src-docs/charm.py.md index 35827b2c..a819d020 100644 --- a/src-docs/charm.py.md +++ b/src-docs/charm.py.md @@ -8,7 +8,6 @@ Charm for WordPress on kubernetes. **Global Variables** --------------- - **APACHE_LOG_PATHS** -- **WORDPRESS_SCRAPE_JOBS** --- @@ -16,9 +15,7 @@ Charm for WordPress on kubernetes. ## class `WordpressCharm` Charm for WordPress on kubernetes. -Attrs: state: Persistent charm state used to store metadata after various events. - - + ### function `__init__` diff --git a/src-docs/cos.py.md b/src-docs/cos.py.md index fe7480aa..416cfe86 100644 --- a/src-docs/cos.py.md +++ b/src-docs/cos.py.md @@ -8,8 +8,58 @@ COS integration for WordPress charm. **Global Variables** --------------- - **APACHE_PROMETHEUS_SCRAPE_PORT** -- **WORDPRESS_SCRAPE_JOBS** - **APACHE_LOG_PATHS** +- **REQUEST_DURATION_MICROSECONDS_BUCKETS** + + +--- + +## class `ApacheLogProxyConsumer` +Extends LogProxyConsumer to add a metrics pipeline to promtail. + + +--- + +#### property loki_endpoints + +Fetch Loki Push API endpoints sent from LokiPushApiProvider through relation data. + + + +**Returns:** + A list of dictionaries with Loki Push API endpoints, for instance: [ + - `{"url"`: "http://loki1:3100/loki/api/v1/push"}, + - `{"url"`: "http://loki2:3100/loki/api/v1/push"}, ] + +--- + +#### property model + +Shortcut for more simple access the model. + +--- + +#### property rsyslog_config + +Generates a config line for use with rsyslog. + + + +**Returns:** + The rsyslog config line as a string + +--- + +#### property syslog_port + +Gets the port on which promtail is listening for syslog. + + + +**Returns:** + A str representing the port + + --- diff --git a/src-docs/state.py.md b/src-docs/state.py.md new file mode 100644 index 00000000..677547b3 --- /dev/null +++ b/src-docs/state.py.md @@ -0,0 +1,112 @@ + + + + +# module `state.py` +Wordpress charm state. + + + +--- + +## class `CharmConfigInvalidError` +Exception raised when a charm configuration is found to be invalid. + + + +**Attributes:** + + - `msg`: Explanation of the error. + + + +### function `__init__` + +```python +__init__(msg: str) +``` + +Initialize a new instance of the CharmConfigInvalidError exception. + + + +**Args:** + + - `msg`: Explanation of the error. + + + + + +--- + +## class `ProxyConfig` +Configuration for external access through proxy. + + + +**Attributes:** + + - `http_proxy`: The http proxy URL. + - `https_proxy`: The https proxy URL. + - `no_proxy`: Comma separated list of hostnames to bypass proxy. + + + + +--- + + + +### classmethod `from_env` + +```python +from_env() → Optional[ForwardRef('ProxyConfig')] +``` + +Instantiate ProxyConfig from juju charm environment. + + + +**Returns:** + ProxyConfig if proxy configuration is provided, None otherwise. + + +--- + +## class `State` +The Wordpress k8s operator charm state. + + + +**Attributes:** + + - `proxy_config`: Proxy configuration to access Jenkins upstream through. + + + + +--- + + + +### classmethod `from_charm` + +```python +from_charm(_: CharmBase) → State +``` + +Initialize the state from charm. + + + +**Returns:** + Current state of the charm. + + + +**Raises:** + + - `CharmConfigInvalidError`: if invalid state values were encountered. + + diff --git a/src-docs/types_.py.md b/src-docs/types_.py.md index 81c45a3b..25d0b495 100644 --- a/src-docs/types_.py.md +++ b/src-docs/types_.py.md @@ -23,7 +23,7 @@ Attrs: return_code: exit code from executed command. stdout: standard output f ## class `DatabaseConfig` Configuration values required to connect to database. -Attrs: hostname: The hostname under which the database is being served. database: The name of the database to connect to. username: The username to use to authenticate to the database. password: The password to use to authenticat to the database. +Attrs: hostname: The hostname under which the database is being served. port: The port which the database is listening on. database: The name of the database to connect to. username: The username to use to authenticate to the database. password: The password to use to authenticat to the database. diff --git a/src/charm.py b/src/charm.py index 082641d7..ec63b608 100755 --- a/src/charm.py +++ b/src/charm.py @@ -1178,19 +1178,8 @@ def _plugin_akismet_reconciliation(self) -> None: f"Unable to config akismet plugin, {result.message}" ) - def _wp_eval(self, php_code: str): - """Execute arbitrary PHP code. - - Args: - php_code: PHP code to be executed. - - Returns: - An instance of :attr:`types_.ExecResult`. - """ - return self._wrapped_run_wp_cli(["wp", "eval", php_code]) - @staticmethod - def _encode_openid_team_map(team_map: str) -> str: + def _encode_openid_team_map(team_map: str) -> dict: """Convert wp_plugin_openid_team_map setting to openid_teams_trust_list WordPress option. example input: site-sysadmins=administrator,site-editors=editor,site-executives=editor @@ -1199,21 +1188,20 @@ def _encode_openid_team_map(team_map: str) -> str: team_map (str): team definition. Returns: - A PHP array, as a Python string. + A Python structure that will be converted to json """ - array_items = [] + teams_parsed: dict = {} for idx, mapping in enumerate(team_map.split(","), start=1): launchpad_role, wordpress_role = mapping.split("=") launchpad_role = launchpad_role.strip() wordpress_role = wordpress_role.strip() - array_items.append( - f"{idx} => (object) array (" - f"'id'=>{idx}," - f"'team'=>'{launchpad_role}'," - f"'role'=>'{wordpress_role}'," - f"'server' => '0',)," - ) - return f"array({''.join(array_items)})" + teams_parsed[str(idx)] = { + "id": idx, + "team": launchpad_role, + "role": wordpress_role, + "server": "0", + } + return teams_parsed def _plugin_openid_reconciliation(self) -> None: """Reconciliation process for the openid plugin.""" @@ -1254,10 +1242,10 @@ def check_result(): check_result() result = self._activate_plugin("wordpress-teams-integration", {}) check_result() - result = self._wp_eval( - "update_option(" - f"'openid_teams_trust_list', {self._encode_openid_team_map(openid_team_map)}" - ");" + result = self._wp_option_update( + "openid_teams_trust_list", + value=json.dumps(self._encode_openid_team_map(openid_team_map)), + format_="json", ) check_result() result = self._wp_option_update("users_can_register", "1") diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index a5572466..99e059d9 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -560,40 +560,15 @@ def test_team_map(): """ arrange: no arrange. act: convert the team_map config using _encode_openid_team_map method. - assert: the converted result should be a valid PHP array with the meaning matching the config. + assert: the converted result should be a valid dict with the meaning matching the config. """ team_map = "site-sysadmins=administrator,site-editors=editor,site-executives=editor" option = WordpressCharm._encode_openid_team_map(team_map) - assert ( - option.replace(" ", "").replace("\n", "") - == """array ( - 1 => - (object) array( - 'id' => 1, - 'team' => 'site-sysadmins', - 'role' => 'administrator', - 'server' => '0', - ), - 2 => - (object) array( - 'id' => 2, - 'team' => 'site-editors', - 'role' => 'editor', - 'server' => '0', - ), - 3 => - (object) array( - 'id' => 3, - 'team' => 'site-executives', - 'role' => 'editor', - 'server' => '0', - ), - )""".replace( - " ", "" - ).replace( - "\n", "" - ) - ) + assert option == { + "1": {"id": 1, "team": "site-sysadmins", "role": "administrator", "server": "0"}, + "2": {"id": 2, "team": "site-editors", "role": "editor", "server": "0"}, + "3": {"id": 3, "team": "site-executives", "role": "editor", "server": "0"}, + } def test_swift_config( @@ -657,7 +632,7 @@ def test_akismet_plugin(run_standard_plugin_test: typing.Callable): @pytest.mark.usefixtures("attach_storage") -def test_openid_plugin(patch: WordpressPatch, run_standard_plugin_test: typing.Callable): +def test_openid_plugin(run_standard_plugin_test: typing.Callable): """ arrange: after peer relation established and database ready. act: update openid plugin configuration. @@ -669,12 +644,32 @@ def test_openid_plugin(patch: WordpressPatch, run_standard_plugin_test: typing.C plugin_config={ "wp_plugin_openid_team_map": "site-sysadmins=administrator,site-editors=editor,site-executives=editor" }, - excepted_options={"openid_required_for_registration": "1", "users_can_register": "1"}, + excepted_options={ + "openid_required_for_registration": "1", + "users_can_register": "1", + "openid_teams_trust_list": { + "1": { + "id": 1, + "role": "administrator", + "server": "0", + "team": "site-sysadmins", + }, + "2": { + "id": 2, + "role": "editor", + "server": "0", + "team": "site-editors", + }, + "3": { + "id": 3, + "role": "editor", + "server": "0", + "team": "site-executives", + }, + }, + }, excepted_options_after_removed={"users_can_register": "0"}, ) - assert patch.container.wp_eval_history[-1].startswith( - "update_option('openid_teams_trust_list'," - ), "PHP function update_option should be invoked after openid plugin enabled" @pytest.mark.usefixtures("attach_storage") diff --git a/tests/unit/wordpress_mock.py b/tests/unit/wordpress_mock.py index 4f67599e..1e25e697 100644 --- a/tests/unit/wordpress_mock.py +++ b/tests/unit/wordpress_mock.py @@ -359,7 +359,6 @@ def __init__( self._wordpress_database_mock = wordpress_database_mock self.installed_plugins = set(WordpressCharm._WORDPRESS_DEFAULT_PLUGINS) self.installed_themes = set(WordpressCharm._WORDPRESS_DEFAULT_THEMES) - self.wp_eval_history: typing.List[str] = [] def exec( self, cmd, user=None, group=None, working_dir=None, combine_stderr=None, timeout=None @@ -602,13 +601,6 @@ def _mock_wp_option_delete(self, cmd): db.delete_option(option) return ExecProcessMock(return_code=0, stdout="", stderr="") - @_exec_handler.register(lambda cmd: cmd[:2] == ["wp", "eval"]) - def _mock_wp_eval(self, cmd): - """Simulate ``wp eval `` command execution in the container.""" - php_code = cmd[2] - self.wp_eval_history.append(php_code) - return ExecProcessMock(return_code=0, stdout="", stderr="") - @_exec_handler.register(lambda cmd: cmd[0] == "a2enconf") def _mock_a2enconf(self, cmd): """Simulate ``a2enconf `` command execution in the container.