From b1112ea1abdcc585c243453c09f4c67344b443c0 Mon Sep 17 00:00:00 2001 From: tphan025 Date: Tue, 26 May 2026 12:09:07 +0200 Subject: [PATCH 1/7] feat(extensions): add valkey_client interface support to 12-factor extensions - Add conditional dpcharmlibs-interfaces injection via charm-python-packages when valkey_client interface is declared in requires - Add commented valkey relation to all framework init templates - Add Valkey integration to integrations.rst documentation - Add Valkey environment variables to environment_variables.rst - Add regression test for valkey python package injection Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- charmcraft/extensions/app.py | 24 +++++++++++++++++++ .../init-django-framework/charmcraft.yaml.j2 | 4 ++++ .../charmcraft.yaml.j2 | 4 ++++ .../init-fastapi-framework/charmcraft.yaml.j2 | 4 ++++ .../init-flask-framework/charmcraft.yaml.j2 | 4 ++++ .../init-go-framework/charmcraft.yaml.j2 | 4 ++++ .../charmcraft.yaml.j2 | 4 ++++ .../extensions/environment_variables.rst | 18 ++++++++++++++ .../reference/extensions/integrations.rst | 9 +++++++ tests/extensions/test_app.py | 20 ++++++++++++++++ 10 files changed, 95 insertions(+) diff --git a/charmcraft/extensions/app.py b/charmcraft/extensions/app.py index 4b61ab04e..c59d18cb1 100644 --- a/charmcraft/extensions/app.py +++ b/charmcraft/extensions/app.py @@ -71,6 +71,8 @@ } COS_SUBDIRS = {"grafana_dashboards", "loki_alert_rules", "prometheus_alert_rules"} +VALKEY_INTERFACE = "valkey_client" +VALKEY_PYTHON_PACKAGES = ["dpcharmlibs-interfaces"] class _AppBase(Extension): @@ -118,6 +120,10 @@ def is_experimental(base: tuple[str, ...] | None) -> bool: # noqa: ARG004 "oauth": OAUTH_DYNAMIC_OPTIONS } + endpoint_extra_dependencies: dict[str, list[str]] = { + VALKEY_INTERFACE: VALKEY_PYTHON_PACKAGES, + } + def _get_nested(self, obj: dict, path: str) -> dict: """Get a nested object using a path (a dot-separated list of keys).""" for key in path.split("."): @@ -268,6 +274,10 @@ def get_root_snippet(self) -> dict[str, Any]: root_snippet, interface_name, config_options ) root_snippet["config"]["options"].update(dynamic_config_options) + if extra_deps := self._get_extra_dependencies(): + root_snippet["parts"]["charm"].setdefault( + "charm-python-packages", [] + ).extend(extra_deps) return root_snippet def _get_dynamic_config_options( @@ -291,6 +301,20 @@ def _get_dynamic_config_options( dynamic_config_options.update(updated_config_options) return dynamic_config_options + def _get_extra_dependencies(self) -> list[str]: + require_interfaces = [ + require.get("interface") + for _endpoint_name, require in self._get_nested( + self.yaml_data, "requires" + ).items() + ] + return [ + dependency + for interface_name, extra_dependencies in self.endpoint_extra_dependencies.items() + if interface_name in require_interfaces + for dependency in extra_dependencies + ] + def _get_updated_dynamic_config_options( self, endpoint_name: str, config_options: dict[str, dict[str, Any]] ) -> dict[str, dict[str, Any]]: diff --git a/charmcraft/templates/init-django-framework/charmcraft.yaml.j2 b/charmcraft/templates/init-django-framework/charmcraft.yaml.j2 index a50a4930e..5bccbe6ee 100644 --- a/charmcraft/templates/init-django-framework/charmcraft.yaml.j2 +++ b/charmcraft/templates/init-django-framework/charmcraft.yaml.j2 @@ -46,6 +46,10 @@ extensions: # interface: redis # optional: false # limit: 1 +# valkey: +# interface: valkey_client +# optional: true +# limit: 1 # s3: # interface: s3 # optional: false diff --git a/charmcraft/templates/init-expressjs-framework/charmcraft.yaml.j2 b/charmcraft/templates/init-expressjs-framework/charmcraft.yaml.j2 index 6109c5568..7af066512 100644 --- a/charmcraft/templates/init-expressjs-framework/charmcraft.yaml.j2 +++ b/charmcraft/templates/init-expressjs-framework/charmcraft.yaml.j2 @@ -46,6 +46,10 @@ extensions: # interface: redis # optional: false # limit: 1 +# valkey: +# interface: valkey_client +# optional: true +# limit: 1 # s3: # interface: s3 # optional: false diff --git a/charmcraft/templates/init-fastapi-framework/charmcraft.yaml.j2 b/charmcraft/templates/init-fastapi-framework/charmcraft.yaml.j2 index b5e0c69e4..f64467e1a 100644 --- a/charmcraft/templates/init-fastapi-framework/charmcraft.yaml.j2 +++ b/charmcraft/templates/init-fastapi-framework/charmcraft.yaml.j2 @@ -46,6 +46,10 @@ extensions: # interface: redis # optional: false # limit: 1 +# valkey: +# interface: valkey_client +# optional: true +# limit: 1 # s3: # interface: s3 # optional: false diff --git a/charmcraft/templates/init-flask-framework/charmcraft.yaml.j2 b/charmcraft/templates/init-flask-framework/charmcraft.yaml.j2 index 09553a5ef..005f12446 100644 --- a/charmcraft/templates/init-flask-framework/charmcraft.yaml.j2 +++ b/charmcraft/templates/init-flask-framework/charmcraft.yaml.j2 @@ -46,6 +46,10 @@ extensions: # interface: redis # optional: false # limit: 1 +# valkey: +# interface: valkey_client +# optional: true +# limit: 1 # s3: # interface: s3 # optional: false diff --git a/charmcraft/templates/init-go-framework/charmcraft.yaml.j2 b/charmcraft/templates/init-go-framework/charmcraft.yaml.j2 index 8b750be48..bb44201f1 100644 --- a/charmcraft/templates/init-go-framework/charmcraft.yaml.j2 +++ b/charmcraft/templates/init-go-framework/charmcraft.yaml.j2 @@ -46,6 +46,10 @@ extensions: # interface: redis # optional: false # limit: 1 +# valkey: +# interface: valkey_client +# optional: true +# limit: 1 # s3: # interface: s3 # optional: false diff --git a/charmcraft/templates/init-spring-boot-framework/charmcraft.yaml.j2 b/charmcraft/templates/init-spring-boot-framework/charmcraft.yaml.j2 index 4fff81455..e955623d5 100644 --- a/charmcraft/templates/init-spring-boot-framework/charmcraft.yaml.j2 +++ b/charmcraft/templates/init-spring-boot-framework/charmcraft.yaml.j2 @@ -46,6 +46,10 @@ extensions: # interface: redis # optional: false # limit: 1 +# valkey: +# interface: valkey_client +# optional: true +# limit: 1 # s3: # interface: s3 # optional: false diff --git a/docs/reuse/reference/extensions/environment_variables.rst b/docs/reuse/reference/extensions/environment_variables.rst index 354483d37..8756436b1 100644 --- a/docs/reuse/reference/extensions/environment_variables.rst +++ b/docs/reuse/reference/extensions/environment_variables.rst @@ -71,6 +71,24 @@ Ingress relation. - ``REDIS_DB_HOSTNAME`` - ``REDIS_DB_PORT`` - ``REDIS_DB_NAME`` + * - Valkey + - + - ``VALKEY_DB_CONNECT_STRING`` + - ``VALKEY_DB_SCHEME`` + - ``VALKEY_DB_NETLOC`` + - ``VALKEY_DB_PATH`` + - ``VALKEY_DB_PARAMS`` + - ``VALKEY_DB_QUERY`` + - ``VALKEY_DB_FRAGMENT`` + - ``VALKEY_DB_USERNAME`` + - ``VALKEY_DB_PASSWORD`` + - ``VALKEY_DB_HOSTNAME`` + - ``VALKEY_DB_PORT`` + - ``VALKEY_DB_NAME`` + - ``VALKEY_DB_TLS`` + - ``VALKEY_DB_MODE`` + - ``VALKEY_DB_TLS_CA`` + - ``VALKEY_DB_VERSION`` * - SAML - - ``SAML_ENTITY_ID`` (required) diff --git a/docs/reuse/reference/extensions/integrations.rst b/docs/reuse/reference/extensions/integrations.rst index 6ec9ab7f1..4bbba0d59 100644 --- a/docs/reuse/reference/extensions/integrations.rst +++ b/docs/reuse/reference/extensions/integrations.rst @@ -75,6 +75,15 @@ the following charms: optional: True limit: 1 + * - `Valkey `__ charm + - .. code-block:: yaml + + requires: + valkey: + interface: valkey_client + optional: True + limit: 1 + * - `SAML `__ charm - .. code-block:: yaml diff --git a/tests/extensions/test_app.py b/tests/extensions/test_app.py index 6854ff5eb..26e2dc158 100644 --- a/tests/extensions/test_app.py +++ b/tests/extensions/test_app.py @@ -687,6 +687,26 @@ def test_handle_charm_part_adds_part(flask_input_yaml, tmp_path): } +def test_valkey_relation_adds_python_package(flask_input_yaml, tmp_path): + flask_input_yaml["requires"] = { + "valkey": { + "interface": "valkey_client", + "optional": True, + "limit": 1, + } + } + + applied = extensions.apply_extensions(tmp_path, flask_input_yaml) + + assert applied["parts"]["charm"] == { + "plugin": "charm", + "source": ".", + "build-snaps": ["rustup"], + "override-build": "rustup default stable\ncraftctl default", + "charm-python-packages": ["dpcharmlibs-interfaces"], + } + + @pytest.mark.parametrize( ("input_yaml", "requires", "expected_options"), [ From 38d41e4df84245877820f049aa07684edbc3103d Mon Sep 17 00:00:00 2001 From: tphan025 Date: Tue, 26 May 2026 18:13:47 +0200 Subject: [PATCH 2/7] Remove charmlibs dependency injection in the extension code since we'll add it as a dependency for paas-charm --- charmcraft/extensions/app.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/charmcraft/extensions/app.py b/charmcraft/extensions/app.py index c59d18cb1..4b61ab04e 100644 --- a/charmcraft/extensions/app.py +++ b/charmcraft/extensions/app.py @@ -71,8 +71,6 @@ } COS_SUBDIRS = {"grafana_dashboards", "loki_alert_rules", "prometheus_alert_rules"} -VALKEY_INTERFACE = "valkey_client" -VALKEY_PYTHON_PACKAGES = ["dpcharmlibs-interfaces"] class _AppBase(Extension): @@ -120,10 +118,6 @@ def is_experimental(base: tuple[str, ...] | None) -> bool: # noqa: ARG004 "oauth": OAUTH_DYNAMIC_OPTIONS } - endpoint_extra_dependencies: dict[str, list[str]] = { - VALKEY_INTERFACE: VALKEY_PYTHON_PACKAGES, - } - def _get_nested(self, obj: dict, path: str) -> dict: """Get a nested object using a path (a dot-separated list of keys).""" for key in path.split("."): @@ -274,10 +268,6 @@ def get_root_snippet(self) -> dict[str, Any]: root_snippet, interface_name, config_options ) root_snippet["config"]["options"].update(dynamic_config_options) - if extra_deps := self._get_extra_dependencies(): - root_snippet["parts"]["charm"].setdefault( - "charm-python-packages", [] - ).extend(extra_deps) return root_snippet def _get_dynamic_config_options( @@ -301,20 +291,6 @@ def _get_dynamic_config_options( dynamic_config_options.update(updated_config_options) return dynamic_config_options - def _get_extra_dependencies(self) -> list[str]: - require_interfaces = [ - require.get("interface") - for _endpoint_name, require in self._get_nested( - self.yaml_data, "requires" - ).items() - ] - return [ - dependency - for interface_name, extra_dependencies in self.endpoint_extra_dependencies.items() - if interface_name in require_interfaces - for dependency in extra_dependencies - ] - def _get_updated_dynamic_config_options( self, endpoint_name: str, config_options: dict[str, dict[str, Any]] ) -> dict[str, dict[str, Any]]: From 9fe3984ce9643fa08a22a868edeb57574e2b6d0b Mon Sep 17 00:00:00 2001 From: tphan025 Date: Tue, 26 May 2026 18:21:58 +0200 Subject: [PATCH 3/7] revert tests since now we don't inject the dependency on the charmcraft level anymore --- tests/extensions/test_app.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/tests/extensions/test_app.py b/tests/extensions/test_app.py index 26e2dc158..6854ff5eb 100644 --- a/tests/extensions/test_app.py +++ b/tests/extensions/test_app.py @@ -687,26 +687,6 @@ def test_handle_charm_part_adds_part(flask_input_yaml, tmp_path): } -def test_valkey_relation_adds_python_package(flask_input_yaml, tmp_path): - flask_input_yaml["requires"] = { - "valkey": { - "interface": "valkey_client", - "optional": True, - "limit": 1, - } - } - - applied = extensions.apply_extensions(tmp_path, flask_input_yaml) - - assert applied["parts"]["charm"] == { - "plugin": "charm", - "source": ".", - "build-snaps": ["rustup"], - "override-build": "rustup default stable\ncraftctl default", - "charm-python-packages": ["dpcharmlibs-interfaces"], - } - - @pytest.mark.parametrize( ("input_yaml", "requires", "expected_options"), [ From 22dc49c508358e9f5274625c495d6a29872eb29c Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Thu, 4 Jun 2026 10:16:33 +0200 Subject: [PATCH 4/7] fix(lint): add Valkey as accepted spelling --- docs/.custom_wordlist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/.custom_wordlist.txt b/docs/.custom_wordlist.txt index 128f0ab5e..b30131a68 100644 --- a/docs/.custom_wordlist.txt +++ b/docs/.custom_wordlist.txt @@ -131,6 +131,7 @@ URIs? URL utils uv +Valkey venv VM's VMs From 2e48af88c948723ffd0431cc57236a5f7b6bfee8 Mon Sep 17 00:00:00 2001 From: tphan025 Date: Thu, 4 Jun 2026 16:02:07 +0200 Subject: [PATCH 5/7] Fix broken link to valkey charmhub page --- docs/reuse/reference/extensions/integrations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reuse/reference/extensions/integrations.rst b/docs/reuse/reference/extensions/integrations.rst index 4bbba0d59..aea648790 100644 --- a/docs/reuse/reference/extensions/integrations.rst +++ b/docs/reuse/reference/extensions/integrations.rst @@ -75,7 +75,7 @@ the following charms: optional: True limit: 1 - * - `Valkey `__ charm + * - `Valkey `__ charm - .. code-block:: yaml requires: From 69c27160b1e9c7d2cdaefa983cf25bc0dd1ced8c Mon Sep 17 00:00:00 2001 From: tphan025 Date: Wed, 17 Jun 2026 20:13:56 +0200 Subject: [PATCH 6/7] chore: bump ops version to ~= 3.7 in framework templates Bumps ops dependency from ~= 2.17 to ~= 3.7 in all framework requirements.txt.j2 templates to ensure compatibility with dpcharmlibs-interfaces>=1.0.0 which requires ops>=3. This enables compatibility with paas-charm 1.11.3+. --- charmcraft/templates/init-django-framework/requirements.txt.j2 | 2 +- .../templates/init-expressjs-framework/requirements.txt.j2 | 2 +- charmcraft/templates/init-fastapi-framework/requirements.txt.j2 | 2 +- charmcraft/templates/init-flask-framework/requirements.txt.j2 | 2 +- charmcraft/templates/init-go-framework/requirements.txt.j2 | 2 +- .../templates/init-spring-boot-framework/requirements.txt.j2 | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/charmcraft/templates/init-django-framework/requirements.txt.j2 b/charmcraft/templates/init-django-framework/requirements.txt.j2 index d58a30c21..575081c8f 100644 --- a/charmcraft/templates/init-django-framework/requirements.txt.j2 +++ b/charmcraft/templates/init-django-framework/requirements.txt.j2 @@ -1,2 +1,2 @@ -ops ~= 2.17 +ops ~= 3.7 paas-charm>=1.0,<2 diff --git a/charmcraft/templates/init-expressjs-framework/requirements.txt.j2 b/charmcraft/templates/init-expressjs-framework/requirements.txt.j2 index 113bb427b..575081c8f 100644 --- a/charmcraft/templates/init-expressjs-framework/requirements.txt.j2 +++ b/charmcraft/templates/init-expressjs-framework/requirements.txt.j2 @@ -1,2 +1,2 @@ -ops>2.17,<4 +ops ~= 3.7 paas-charm>=1.0,<2 diff --git a/charmcraft/templates/init-fastapi-framework/requirements.txt.j2 b/charmcraft/templates/init-fastapi-framework/requirements.txt.j2 index d58a30c21..575081c8f 100644 --- a/charmcraft/templates/init-fastapi-framework/requirements.txt.j2 +++ b/charmcraft/templates/init-fastapi-framework/requirements.txt.j2 @@ -1,2 +1,2 @@ -ops ~= 2.17 +ops ~= 3.7 paas-charm>=1.0,<2 diff --git a/charmcraft/templates/init-flask-framework/requirements.txt.j2 b/charmcraft/templates/init-flask-framework/requirements.txt.j2 index d58a30c21..575081c8f 100644 --- a/charmcraft/templates/init-flask-framework/requirements.txt.j2 +++ b/charmcraft/templates/init-flask-framework/requirements.txt.j2 @@ -1,2 +1,2 @@ -ops ~= 2.17 +ops ~= 3.7 paas-charm>=1.0,<2 diff --git a/charmcraft/templates/init-go-framework/requirements.txt.j2 b/charmcraft/templates/init-go-framework/requirements.txt.j2 index d58a30c21..575081c8f 100644 --- a/charmcraft/templates/init-go-framework/requirements.txt.j2 +++ b/charmcraft/templates/init-go-framework/requirements.txt.j2 @@ -1,2 +1,2 @@ -ops ~= 2.17 +ops ~= 3.7 paas-charm>=1.0,<2 diff --git a/charmcraft/templates/init-spring-boot-framework/requirements.txt.j2 b/charmcraft/templates/init-spring-boot-framework/requirements.txt.j2 index d58a30c21..575081c8f 100644 --- a/charmcraft/templates/init-spring-boot-framework/requirements.txt.j2 +++ b/charmcraft/templates/init-spring-boot-framework/requirements.txt.j2 @@ -1,2 +1,2 @@ -ops ~= 2.17 +ops ~= 3.7 paas-charm>=1.0,<2 From c21b4241fc95d1eddcce6adc62379d4f1ab6902d Mon Sep 17 00:00:00 2001 From: tphan025 Date: Thu, 18 Jun 2026 16:36:47 +0200 Subject: [PATCH 7/7] chore: remove ops dependency from framework init templates --- charmcraft/templates/init-django-framework/requirements.txt.j2 | 1 - .../templates/init-expressjs-framework/requirements.txt.j2 | 1 - charmcraft/templates/init-fastapi-framework/requirements.txt.j2 | 1 - charmcraft/templates/init-flask-framework/requirements.txt.j2 | 1 - charmcraft/templates/init-go-framework/requirements.txt.j2 | 1 - .../templates/init-spring-boot-framework/requirements.txt.j2 | 1 - 6 files changed, 6 deletions(-) diff --git a/charmcraft/templates/init-django-framework/requirements.txt.j2 b/charmcraft/templates/init-django-framework/requirements.txt.j2 index 575081c8f..e30202513 100644 --- a/charmcraft/templates/init-django-framework/requirements.txt.j2 +++ b/charmcraft/templates/init-django-framework/requirements.txt.j2 @@ -1,2 +1 @@ -ops ~= 3.7 paas-charm>=1.0,<2 diff --git a/charmcraft/templates/init-expressjs-framework/requirements.txt.j2 b/charmcraft/templates/init-expressjs-framework/requirements.txt.j2 index 575081c8f..e30202513 100644 --- a/charmcraft/templates/init-expressjs-framework/requirements.txt.j2 +++ b/charmcraft/templates/init-expressjs-framework/requirements.txt.j2 @@ -1,2 +1 @@ -ops ~= 3.7 paas-charm>=1.0,<2 diff --git a/charmcraft/templates/init-fastapi-framework/requirements.txt.j2 b/charmcraft/templates/init-fastapi-framework/requirements.txt.j2 index 575081c8f..e30202513 100644 --- a/charmcraft/templates/init-fastapi-framework/requirements.txt.j2 +++ b/charmcraft/templates/init-fastapi-framework/requirements.txt.j2 @@ -1,2 +1 @@ -ops ~= 3.7 paas-charm>=1.0,<2 diff --git a/charmcraft/templates/init-flask-framework/requirements.txt.j2 b/charmcraft/templates/init-flask-framework/requirements.txt.j2 index 575081c8f..e30202513 100644 --- a/charmcraft/templates/init-flask-framework/requirements.txt.j2 +++ b/charmcraft/templates/init-flask-framework/requirements.txt.j2 @@ -1,2 +1 @@ -ops ~= 3.7 paas-charm>=1.0,<2 diff --git a/charmcraft/templates/init-go-framework/requirements.txt.j2 b/charmcraft/templates/init-go-framework/requirements.txt.j2 index 575081c8f..e30202513 100644 --- a/charmcraft/templates/init-go-framework/requirements.txt.j2 +++ b/charmcraft/templates/init-go-framework/requirements.txt.j2 @@ -1,2 +1 @@ -ops ~= 3.7 paas-charm>=1.0,<2 diff --git a/charmcraft/templates/init-spring-boot-framework/requirements.txt.j2 b/charmcraft/templates/init-spring-boot-framework/requirements.txt.j2 index 575081c8f..e30202513 100644 --- a/charmcraft/templates/init-spring-boot-framework/requirements.txt.j2 +++ b/charmcraft/templates/init-spring-boot-framework/requirements.txt.j2 @@ -1,2 +1 @@ -ops ~= 3.7 paas-charm>=1.0,<2