paas-charm 2.0.dev0__tar.gz → 2.0.dev3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/PKG-INFO +8 -5
  2. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/pyproject.toml +27 -24
  3. paas_charm-2.0.dev3/src/paas_charm/__init__.py +13 -0
  4. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/_gunicorn/charm.py +1 -3
  5. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/_gunicorn/webserver.py +2 -30
  6. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/_gunicorn/workload_config.py +18 -14
  7. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/app.py +22 -26
  8. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/charm.py +242 -366
  9. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/charm_state.py +28 -31
  10. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/django/charm.py +1 -1
  11. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/exceptions.py +9 -0
  12. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/expressjs/charm.py +9 -8
  13. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/fastapi/charm.py +11 -11
  14. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/flask/charm.py +1 -1
  15. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/framework.py +8 -14
  16. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/go/charm.py +12 -11
  17. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/oauth.py +15 -4
  18. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/observability.py +26 -65
  19. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/paas_config.py +49 -1
  20. paas_charm-2.0.dev3/src/paas_charm/peers.py +60 -0
  21. paas_charm-2.0.dev3/src/paas_charm/relations.py +213 -0
  22. paas_charm-2.0.dev3/src/paas_charm/secret_key.py +86 -0
  23. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/springboot/charm.py +46 -38
  24. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/tracing.py +4 -2
  25. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/utils.py +0 -19
  26. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm.egg-info/PKG-INFO +8 -5
  27. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm.egg-info/SOURCES.txt +3 -12
  28. paas_charm-2.0.dev3/src/paas_charm.egg-info/requires.txt +9 -0
  29. paas_charm-2.0.dev3/src/paas_charm.egg-info/top_level.txt +1 -0
  30. paas_charm-2.0.dev0/src/paas_app_charmer/__init__.py +0 -13
  31. paas_charm-2.0.dev0/src/paas_app_charmer/django/__init__.py +0 -13
  32. paas_charm-2.0.dev0/src/paas_app_charmer/django/charm.py +0 -15
  33. paas_charm-2.0.dev0/src/paas_app_charmer/fastapi/__init__.py +0 -13
  34. paas_charm-2.0.dev0/src/paas_app_charmer/fastapi/charm.py +0 -16
  35. paas_charm-2.0.dev0/src/paas_app_charmer/flask/__init__.py +0 -13
  36. paas_charm-2.0.dev0/src/paas_app_charmer/flask/charm.py +0 -15
  37. paas_charm-2.0.dev0/src/paas_app_charmer/go/__init__.py +0 -13
  38. paas_charm-2.0.dev0/src/paas_app_charmer/go/charm.py +0 -15
  39. paas_charm-2.0.dev0/src/paas_charm/__init__.py +0 -60
  40. paas_charm-2.0.dev0/src/paas_charm/openfga.py +0 -9
  41. paas_charm-2.0.dev0/src/paas_charm/redis.py +0 -63
  42. paas_charm-2.0.dev0/src/paas_charm/secret_storage.py +0 -169
  43. paas_charm-2.0.dev0/src/paas_charm.egg-info/requires.txt +0 -6
  44. paas_charm-2.0.dev0/src/paas_charm.egg-info/top_level.txt +0 -2
  45. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/README.md +0 -0
  46. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/setup.cfg +0 -0
  47. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/_gunicorn/__init__.py +0 -0
  48. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/_gunicorn/wsgi_app.py +0 -0
  49. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/charm_utils.py +0 -0
  50. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/database_migration.py +0 -0
  51. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/databases.py +0 -0
  52. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/django/__init__.py +0 -0
  53. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/django/cos/grafana_dashboards/django.json +0 -0
  54. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/django/cos/loki_alert_rules/.gitkeep +0 -0
  55. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/django/cos/prometheus_alert_rules/django.rule +0 -0
  56. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/expressjs/__init__.py +0 -0
  57. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/expressjs/cos/grafana_dashboards/expressjs.json +0 -0
  58. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/expressjs/cos/loki_alert_rules/.gitkeep +0 -0
  59. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/expressjs/cos/prometheus_alert_rules/.gitkeep +0 -0
  60. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/fastapi/__init__.py +0 -0
  61. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/fastapi/app.py +0 -0
  62. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/fastapi/cos/grafana_dashboards/fastapi.json +0 -0
  63. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/fastapi/cos/loki_alert_rules/.gitkeep +0 -0
  64. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/fastapi/cos/prometheus_alert_rules/.gitkeep +0 -0
  65. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/flask/__init__.py +0 -0
  66. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/flask/cos/grafana_dashboards/flask.json +0 -0
  67. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/flask/cos/loki_alert_rules/.gitkeep +0 -0
  68. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/flask/cos/prometheus_alert_rules/flask.rule +0 -0
  69. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/go/__init__.py +0 -0
  70. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/go/cos/grafana_dashboards/go.json +0 -0
  71. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/go/cos/loki_alert_rules/.gitkeep +0 -0
  72. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/go/cos/prometheus_alert_rules/.gitkeep +0 -0
  73. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/http_proxy.py +0 -0
  74. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/rabbitmq.py +0 -0
  75. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/s3.py +0 -0
  76. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/saml.py +0 -0
  77. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/springboot/__init__.py +0 -0
  78. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/springboot/cos/grafana_dashboards/spring_boot.json +0 -0
  79. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/springboot/cos/loki_alert_rules/.gitkeep +0 -0
  80. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/springboot/cos/prometheus_alert_rules/.gitkeep +0 -0
  81. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/templates/_gunicorn_json_logging.py +0 -0
  82. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/templates/fastapi/uvicorn-log-config.json +0 -0
  83. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/templates/fastapi/uvicorn_log_handler.py +0 -0
  84. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/templates/gunicorn.conf.py.j2 +0 -0
  85. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/valkey.py +0 -0
  86. {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm.egg-info/dependency_links.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: paas-charm
3
- Version: 2.0.dev0
3
+ Version: 2.0.dev3
4
4
  Summary: Companion library for 12-factor app support in Charmcraft & Rockcraft.
5
5
  Author-email: Canonical IS DevOps team <is-devops-team@canonical.com>
6
6
  Project-URL: Repository, https://github.com/canonical/paas-charm
@@ -11,12 +11,15 @@ Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.12
13
13
  Description-Content-Type: text/markdown
14
- Requires-Dist: cosl==1.9.1
15
- Requires-Dist: dpcharmlibs-interfaces==1.0.2
14
+ Requires-Dist: charmlibs-interfaces-tracing==1.0.0
15
+ Requires-Dist: charmlibs-interfaces-oauth==1.1.0
16
+ Requires-Dist: charmlibs-interfaces-openfga==1.0.0
17
+ Requires-Dist: cosl==1.10.3
18
+ Requires-Dist: dpcharmlibs-interfaces==1.2.0
16
19
  Requires-Dist: Jinja2==3.1.6
17
20
  Requires-Dist: jsonschema==4.26.0
18
- Requires-Dist: ops==3.7.1
19
- Requires-Dist: pydantic==2.13.4
21
+ Requires-Dist: ops==3.8.2
22
+ Requires-Dist: pydantic[email]==2.13.5
20
23
 
21
24
  # PaaS Charm
22
25
 
@@ -2,7 +2,7 @@
2
2
  # See LICENSE file for licensing details.
3
3
  [project]
4
4
  name = "paas-charm"
5
- version = "2.0.dev0"
5
+ version = "2.0.dev3"
6
6
  description = "Companion library for 12-factor app support in Charmcraft & Rockcraft."
7
7
  readme = "README.md"
8
8
  authors = [
@@ -17,12 +17,15 @@ classifiers = [
17
17
  "Programming Language :: Python :: 3",
18
18
  ]
19
19
  dependencies = [
20
- "cosl==1.9.1",
21
- "dpcharmlibs-interfaces==1.0.2",
20
+ "charmlibs-interfaces-tracing==1.0.0",
21
+ "charmlibs-interfaces-oauth==1.1.0",
22
+ "charmlibs-interfaces-openfga==1.0.0",
23
+ "cosl==1.10.3",
24
+ "dpcharmlibs-interfaces==1.2.0",
22
25
  "Jinja2==3.1.6",
23
26
  "jsonschema==4.26.0",
24
- "ops==3.7.1",
25
- "pydantic==2.13.4",
27
+ "ops==3.8.2",
28
+ "pydantic[email]==2.13.5",
26
29
  ]
27
30
 
28
31
  [project.urls]
@@ -37,7 +40,7 @@ where = ["src"]
37
40
 
38
41
  [tool.uv.sources]
39
42
  # opcli is published only from the charm-ci git repository, not PyPI.
40
- opcli = { git = "https://github.com/canonical/charm-ci.git", branch = "main" }
43
+ opcli = { git = "https://github.com/canonical/charm-ci.git", tag = "v1.0.1" }
41
44
 
42
45
  [tool.setuptools.package-data]
43
46
  paas_charm = ["**/cos/**", "**/cos/**/.**", "**/*.j2", "**/templates/**/*.py", "**/templates/**/*.json"]
@@ -134,13 +137,13 @@ line-length = 99
134
137
 
135
138
  [dependency-groups]
136
139
  unit = [
137
- "cosl==1.9.1",
138
- "coverage[toml]==7.14.1",
139
- "gunicorn==26.0.0",
140
- "opentelemetry-api==1.42.1",
141
- "opentelemetry-sdk==1.42.1",
142
- "ops[testing]==3.7.1",
143
- "pydantic[email]==2.13.4",
140
+ "cosl==1.10.3",
141
+ "coverage[toml]==7.16.0",
142
+ "gunicorn==26.2.0",
143
+ "opentelemetry-api==1.44.0",
144
+ "opentelemetry-sdk==1.44.0",
145
+ "ops[testing]==3.8.2",
146
+ "pydantic[email]==2.13.5",
144
147
  "pytest==9.1.1",
145
148
  ]
146
149
  lint = [
@@ -158,8 +161,7 @@ lint = [
158
161
  "pydocstyle",
159
162
  "pylint",
160
163
  "pyproject-flake8",
161
- "pytest_operator",
162
- "snowballstemmer<3.0.0",
164
+ "snowballstemmer<3.1.2",
163
165
  "types-PyYAML",
164
166
  "types-requests",
165
167
  ]
@@ -168,15 +170,16 @@ static = [
168
170
  ]
169
171
  integration-tests = [
170
172
  "pytest>=9.1.1",
171
- "aiohttp==3.11.7",
172
- "cosl==1.9.1",
173
+ "aiohttp==3.14.3",
174
+ "cosl==1.10.3",
173
175
  "saml-test-helper @ git+https://github.com/canonical/saml-test-idp.git",
174
- "jubilant==1.7.0",
175
- "juju==3.6.1.2",
176
- "minio==7.2.14",
177
- "ops==3.7.1",
178
- "tenacity==9.0.0",
179
- "playwright==1.61.0",
180
- "lightkube==0.21.0",
176
+ "jubilant==1.13.0",
177
+ "juju==3.6.1.3",
178
+ "minio==7.2.20",
179
+ "ops==3.8.2",
180
+ "tenacity==9.1.4",
181
+ "playwright==1.62.0",
182
+ "lightkube==1.0.1",
183
+ "pytest-jubilant>=2,<3",
181
184
  "opcli"
182
185
  ]
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # Copyright 2025 Canonical Ltd.
4
+ # See LICENSE file for licensing details.
5
+
6
+ """Module __init__."""
7
+
8
+ import charms.data_platform_libs.v0.data_interfaces # noqa: F401
9
+ import charms.grafana_k8s.v0.grafana_dashboard # noqa: F401
10
+ import charms.loki_k8s.v1.loki_push_api # noqa: F401
11
+ import charms.observability_libs.v0.juju_topology # noqa: F401
12
+ import charms.prometheus_k8s.v0.prometheus_scrape # noqa: F401
13
+ import charms.traefik_k8s.v2.ingress # noqa: F401
@@ -13,7 +13,6 @@ from paas_charm._gunicorn.wsgi_app import WsgiApp
13
13
  from paas_charm.app import App, WorkloadConfig
14
14
  from paas_charm.charm import PaasCharm
15
15
  from paas_charm.exceptions import CharmConfigInvalidError
16
- from paas_charm.paas_config import read_paas_config
17
16
 
18
17
  logger = logging.getLogger(__name__)
19
18
 
@@ -24,13 +23,12 @@ class GunicornBase(PaasCharm):
24
23
  @property
25
24
  def _workload_config(self) -> WorkloadConfig:
26
25
  """Return a WorkloadConfig instance."""
27
- paas_config = read_paas_config()
28
26
  return create_workload_config(
29
27
  framework_name=self._framework_name,
30
28
  unit_name=self.unit.name,
31
29
  state_dir=self._state_dir,
32
30
  tracing_enabled=bool(self._tracing and self._tracing.is_ready()),
33
- logging_format=paas_config.framework_logging_format,
31
+ paas_config=self._paas_config,
34
32
  )
35
33
 
36
34
  def create_webserver_config(self) -> WebserverConfig:
@@ -17,14 +17,11 @@ import ops
17
17
  from ops.pebble import ExecError, PathError
18
18
 
19
19
  from paas_charm._gunicorn.workload_config import (
20
- APPLICATION_ERROR_LOG_FILE_FMT,
21
- APPLICATION_LOG_FILE_FMT,
22
20
  STATSD_HOST,
23
21
  )
24
22
  from paas_charm.app import WorkloadConfig
25
23
  from paas_charm.exceptions import CharmConfigInvalidError
26
24
  from paas_charm.paas_config import LoggingFormat
27
- from paas_charm.utils import enable_pebble_log_forwarding
28
25
 
29
26
  logger = logging.getLogger(__name__)
30
27
 
@@ -153,25 +150,14 @@ class GunicornWebserver: # pylint: disable=too-few-public-methods
153
150
  if isinstance(setting_value, (int, str))
154
151
  else int(setting_value.total_seconds())
155
152
  )
156
- if enable_pebble_log_forwarding():
157
- access_log = "-"
158
- error_log = "-"
159
- else:
160
- access_log = str(
161
- APPLICATION_LOG_FILE_FMT.format(framework=self._workload_config.framework)
162
- )
163
- error_log = str(
164
- APPLICATION_ERROR_LOG_FILE_FMT.format(framework=self._workload_config.framework)
165
- )
166
-
167
153
  jinja_environment = jinja2.Environment(
168
154
  loader=jinja2.PackageLoader("paas_charm", "templates"), autoescape=True
169
155
  )
170
156
  config = jinja_environment.get_template("gunicorn.conf.py.j2").render(
171
157
  workload_port=self._workload_config.port,
172
158
  workload_app_dir=str(self._workload_config.app_dir),
173
- access_log=access_log,
174
- error_log=error_log,
159
+ access_log="-",
160
+ error_log="-",
175
161
  statsd_host=str(STATSD_HOST),
176
162
  enable_tracing=self._workload_config.tracing_enabled,
177
163
  enable_json_logging=self._workload_config.logging_format == LoggingFormat.JSON,
@@ -201,7 +187,6 @@ class GunicornWebserver: # pylint: disable=too-few-public-methods
201
187
  Raises:
202
188
  CharmConfigInvalidError: if the charm configuration is not valid.
203
189
  """
204
- self._prepare_log_dir()
205
190
  webserver_config_path = str(self._config_path)
206
191
  try:
207
192
  current_webserver_config = self._container.pull(webserver_config_path)
@@ -239,16 +224,3 @@ class GunicornWebserver: # pylint: disable=too-few-public-methods
239
224
  if is_webserver_running:
240
225
  logger.info("gunicorn config changed, reloading")
241
226
  self._container.send_signal(self._reload_signal, self._workload_config.service_name)
242
-
243
- def _prepare_log_dir(self) -> None:
244
- """Prepare access and error log directory for the application."""
245
- container = self._container
246
- for log in self._workload_config.log_files:
247
- log_dir = str(log.parent.absolute())
248
- if not container.exists(log_dir):
249
- container.make_dir(
250
- log_dir,
251
- make_parents=True,
252
- user=self._workload_config.user,
253
- group=self._workload_config.group,
254
- )
@@ -6,19 +6,18 @@
6
6
  import pathlib
7
7
 
8
8
  from paas_charm.app import WorkloadConfig
9
- from paas_charm.paas_config import LoggingFormat
9
+ from paas_charm.paas_config import PaasConfig
10
10
 
11
11
  STATSD_HOST = "localhost:9125"
12
- APPLICATION_LOG_FILE_FMT = "/var/log/{framework}/access.log"
13
- APPLICATION_ERROR_LOG_FILE_FMT = "/var/log/{framework}/error.log"
12
+ GUNICORN_CONFIG_DIR = "/var/lib/gunicorn"
14
13
 
15
14
 
16
15
  def create_workload_config(
17
16
  framework_name: str,
18
17
  unit_name: str,
19
18
  state_dir: pathlib.Path,
19
+ paas_config: PaasConfig | None = None,
20
20
  tracing_enabled: bool = False,
21
- logging_format: LoggingFormat = LoggingFormat.NONE,
22
21
  ) -> WorkloadConfig:
23
22
  """Create an WorkloadConfig for Gunicorn.
24
23
 
@@ -26,27 +25,32 @@ def create_workload_config(
26
25
  framework_name: framework name.
27
26
  unit_name: name of the app unit.
28
27
  state_dir: state folder directory.
28
+ paas_config: PaasConfig instance. Uses the schema defaults when omitted.
29
29
  tracing_enabled: if True, tracing is enabled.
30
- logging_format: structured logging format; defaults to LoggingFormat.NONE.
31
30
 
32
31
  Returns:
33
32
  new WorkloadConfig
34
33
  """
35
- base_dir = pathlib.Path(f"/{framework_name}")
34
+ paas_config = paas_config or PaasConfig()
35
+ application_port = paas_config.application_port(default_port=8000)
36
+ metrics_port, metrics_path = paas_config.metrics_endpoint(
37
+ default_port=9102, default_path="/metrics"
38
+ )
39
+ # For Gunicorn, base_dir holds the (mutable) gunicorn.conf.py, kept separate from app_dir
40
+ # (the application source directory) so the two can have different lifecycles/ownership.
41
+ base_dir = pathlib.Path(GUNICORN_CONFIG_DIR)
42
+ app_dir = pathlib.Path("/app")
36
43
  return WorkloadConfig(
37
44
  framework=framework_name,
38
45
  container_name="app",
39
- port=8000,
46
+ port=application_port,
40
47
  base_dir=base_dir,
41
- app_dir=base_dir / "app",
48
+ app_dir=app_dir,
42
49
  state_dir=state_dir,
43
50
  service_name=framework_name,
44
- log_files=[
45
- pathlib.Path(str.format(APPLICATION_LOG_FILE_FMT, framework=framework_name)),
46
- pathlib.Path(str.format(APPLICATION_ERROR_LOG_FILE_FMT, framework=framework_name)),
47
- ],
48
- metrics_target="*:9102",
51
+ metrics_path=metrics_path,
52
+ metrics_port=metrics_port,
49
53
  unit_name=unit_name,
50
54
  tracing_enabled=tracing_enabled,
51
- logging_format=logging_format,
55
+ logging_format=paas_config.framework_logging_format,
52
56
  )
@@ -9,7 +9,7 @@ import logging
9
9
  import pathlib
10
10
  import urllib.parse
11
11
  from dataclasses import dataclass
12
- from typing import TYPE_CHECKING, List
12
+ from typing import TYPE_CHECKING
13
13
 
14
14
  import ops
15
15
  from dpcharmlibs.interfaces import ValkeyResponseModel
@@ -21,13 +21,12 @@ from paas_charm.paas_config import LoggingFormat
21
21
  logger = logging.getLogger(__name__)
22
22
 
23
23
  if TYPE_CHECKING:
24
- from charms.openfga_k8s.v1.openfga import OpenfgaProviderAppData
24
+ from charmlibs.interfaces.openfga import OpenfgaProviderAppData
25
25
  from charms.smtp_integrator.v0.smtp import SmtpRelationData
26
26
 
27
27
  from paas_charm.databases import PaaSDatabaseRelationData
28
28
  from paas_charm.oauth import PaaSOAuthRelationData
29
29
  from paas_charm.rabbitmq import PaaSRabbitMQRelationData
30
- from paas_charm.redis import PaaSRedisRelationData
31
30
  from paas_charm.s3 import PaaSS3RelationData
32
31
  from paas_charm.saml import PaaSSAMLRelationData
33
32
  from paas_charm.tracing import PaaSTracingRelationData
@@ -53,9 +52,8 @@ class WorkloadConfig: # pylint: disable=too-many-instance-attributes
53
52
  app_dir: the application directory in the application container.
54
53
  state_dir: the directory in the application container to store states information.
55
54
  service_name: the WSGI application pebble service name.
56
- log_files: list of files to monitor.
57
- metrics_target: target to scrape for metrics.
58
55
  metrics_path: path to scrape for metrics.
56
+ metrics_port: port on which the application exposes metrics.
59
57
  unit_name: Name of the unit. Needed to know if schedulers should run here.
60
58
  tracing_enabled: True if tracing should be enabled.
61
59
  logging_format: Structured logging format to use; ``LoggingFormat.NONE`` for default.
@@ -70,9 +68,8 @@ class WorkloadConfig: # pylint: disable=too-many-instance-attributes
70
68
  app_dir: pathlib.Path
71
69
  state_dir: pathlib.Path
72
70
  service_name: str
73
- log_files: List[pathlib.Path]
74
- metrics_target: str | None = None
75
71
  metrics_path: str | None = "/metrics"
72
+ metrics_port: int = 8080
76
73
  unit_name: str
77
74
  tracing_enabled: bool = False
78
75
  logging_format: LoggingFormat = LoggingFormat.NONE
@@ -152,21 +149,6 @@ def generate_rabbitmq_env(
152
149
  return envvars
153
150
 
154
151
 
155
- def generate_redis_env(relation_data: "PaaSRedisRelationData | None" = None) -> dict[str, str]:
156
- """Generate environment variable from Redis relation data.
157
-
158
- Args:
159
- relation_data: The charm Redis integration relation data.
160
-
161
- Returns:
162
- Redis environment mappings if Redis relation data is available, empty
163
- dictionary otherwise.
164
- """
165
- if not relation_data:
166
- return {}
167
- return _db_url_to_env_variables("REDIS", str(relation_data.url))
168
-
169
-
170
152
  def generate_valkey_env(
171
153
  relation_data: ValkeyResponseModel | None = None,
172
154
  ) -> dict[str, str]:
@@ -392,7 +374,6 @@ class App: # pylint: disable=too-many-instance-attributes
392
374
  generate_db_env: Maps database connection information to environment variables.
393
375
  generate_openfga_env: Maps OpenFGA connection information to environment variables.
394
376
  generate_rabbitmq_env: Maps RabbitMQ connection information to environment variables.
395
- generate_redis_env: Maps Redis connection information to environment variables.
396
377
  generate_valkey_env: Maps Valkey connection information to environment variables.
397
378
  generate_s3_env: Maps S3 connection information to environment variables.
398
379
  generate_saml_env: Maps SAML connection information to environment variables.
@@ -405,7 +386,6 @@ class App: # pylint: disable=too-many-instance-attributes
405
386
  generate_db_env = staticmethod(generate_db_env)
406
387
  generate_openfga_env = staticmethod(generate_openfga_env)
407
388
  generate_rabbitmq_env = staticmethod(generate_rabbitmq_env)
408
- generate_redis_env = staticmethod(generate_redis_env)
409
389
  generate_valkey_env = staticmethod(generate_valkey_env)
410
390
  generate_s3_env = staticmethod(generate_s3_env)
411
391
  generate_saml_env = staticmethod(generate_saml_env)
@@ -421,7 +401,7 @@ class App: # pylint: disable=too-many-instance-attributes
421
401
  charm_state: CharmState,
422
402
  workload_config: WorkloadConfig,
423
403
  database_migration: DatabaseMigration,
424
- framework_config_prefix: str = "APP_",
404
+ framework_config_prefix: str = "",
425
405
  configuration_prefix: str = "APP_",
426
406
  integrations_prefix: str = "",
427
407
  ):
@@ -464,6 +444,7 @@ class App: # pylint: disable=too-many-instance-attributes
464
444
 
465
445
  # 2024/04/25 - we're refactoring this method which will get rid of map_integrations_to_env
466
446
  # wrapper function. Ignore too-complex error from flake8 for now.
447
+ # pylint: disable=too-many-branches
467
448
  def gen_environment(self) -> dict[str, str]: # noqa: too-complex
468
449
  """Generate a environment dictionary from the charm configurations.
469
450
 
@@ -499,6 +480,10 @@ class App: # pylint: disable=too-many-instance-attributes
499
480
  for k, v in framework_config.items()
500
481
  }
501
482
  )
483
+ env[f"{framework_config_prefix}METRICS_PORT"] = str(self._workload_config.metrics_port)
484
+ env[f"{framework_config_prefix}METRICS_PATH"] = (
485
+ self._workload_config.metrics_path or "/metrics"
486
+ )
502
487
 
503
488
  if self._charm_state.base_url:
504
489
  env[f"{prefix}BASE_URL"] = self._charm_state.base_url
@@ -515,6 +500,7 @@ class App: # pylint: disable=too-many-instance-attributes
515
500
  env[f"{prefix}PEER_FQDNS"] = self._charm_state.peer_fqdns
516
501
 
517
502
  env.update(self._generate_integration_environments(prefix=self.integrations_prefix))
503
+
518
504
  return env
519
505
 
520
506
  def _generate_integration_environments(self, prefix: str = "") -> dict[str, str]:
@@ -528,7 +514,6 @@ class App: # pylint: disable=too-many-instance-attributes
528
514
  env.update(
529
515
  self.generate_rabbitmq_env(relation_data=self._charm_state.integrations.rabbitmq)
530
516
  )
531
- env.update(self.generate_redis_env(relation_data=self._charm_state.integrations.redis))
532
517
  env.update(self.generate_valkey_env(relation_data=self._charm_state.integrations.valkey))
533
518
  env.update(self.generate_s3_env(relation_data=self._charm_state.integrations.s3))
534
519
  for (
@@ -546,6 +531,17 @@ class App: # pylint: disable=too-many-instance-attributes
546
531
  relation_data=self._charm_state.integrations.oauth,
547
532
  )
548
533
  )
534
+
535
+ for relation in self._charm_state.custom_relations:
536
+ for key, value in (relation.gen_environment() or {}).items():
537
+ if key in env:
538
+ logger.warning(
539
+ "Custom relation %s overwrites environment variable %s",
540
+ relation.relation_name,
541
+ key,
542
+ )
543
+ env[key] = value
544
+
549
545
  return {prefix + k: v for (k, v) in env.items()}
550
546
 
551
547
  @property