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.
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/PKG-INFO +8 -5
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/pyproject.toml +27 -24
- paas_charm-2.0.dev3/src/paas_charm/__init__.py +13 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/_gunicorn/charm.py +1 -3
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/_gunicorn/webserver.py +2 -30
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/_gunicorn/workload_config.py +18 -14
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/app.py +22 -26
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/charm.py +242 -366
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/charm_state.py +28 -31
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/django/charm.py +1 -1
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/exceptions.py +9 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/expressjs/charm.py +9 -8
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/fastapi/charm.py +11 -11
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/flask/charm.py +1 -1
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/framework.py +8 -14
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/go/charm.py +12 -11
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/oauth.py +15 -4
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/observability.py +26 -65
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/paas_config.py +49 -1
- paas_charm-2.0.dev3/src/paas_charm/peers.py +60 -0
- paas_charm-2.0.dev3/src/paas_charm/relations.py +213 -0
- paas_charm-2.0.dev3/src/paas_charm/secret_key.py +86 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/springboot/charm.py +46 -38
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/tracing.py +4 -2
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/utils.py +0 -19
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm.egg-info/PKG-INFO +8 -5
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm.egg-info/SOURCES.txt +3 -12
- paas_charm-2.0.dev3/src/paas_charm.egg-info/requires.txt +9 -0
- paas_charm-2.0.dev3/src/paas_charm.egg-info/top_level.txt +1 -0
- paas_charm-2.0.dev0/src/paas_app_charmer/__init__.py +0 -13
- paas_charm-2.0.dev0/src/paas_app_charmer/django/__init__.py +0 -13
- paas_charm-2.0.dev0/src/paas_app_charmer/django/charm.py +0 -15
- paas_charm-2.0.dev0/src/paas_app_charmer/fastapi/__init__.py +0 -13
- paas_charm-2.0.dev0/src/paas_app_charmer/fastapi/charm.py +0 -16
- paas_charm-2.0.dev0/src/paas_app_charmer/flask/__init__.py +0 -13
- paas_charm-2.0.dev0/src/paas_app_charmer/flask/charm.py +0 -15
- paas_charm-2.0.dev0/src/paas_app_charmer/go/__init__.py +0 -13
- paas_charm-2.0.dev0/src/paas_app_charmer/go/charm.py +0 -15
- paas_charm-2.0.dev0/src/paas_charm/__init__.py +0 -60
- paas_charm-2.0.dev0/src/paas_charm/openfga.py +0 -9
- paas_charm-2.0.dev0/src/paas_charm/redis.py +0 -63
- paas_charm-2.0.dev0/src/paas_charm/secret_storage.py +0 -169
- paas_charm-2.0.dev0/src/paas_charm.egg-info/requires.txt +0 -6
- paas_charm-2.0.dev0/src/paas_charm.egg-info/top_level.txt +0 -2
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/README.md +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/setup.cfg +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/_gunicorn/__init__.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/_gunicorn/wsgi_app.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/charm_utils.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/database_migration.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/databases.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/django/__init__.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/django/cos/grafana_dashboards/django.json +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/django/cos/loki_alert_rules/.gitkeep +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/django/cos/prometheus_alert_rules/django.rule +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/expressjs/__init__.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/expressjs/cos/grafana_dashboards/expressjs.json +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/expressjs/cos/loki_alert_rules/.gitkeep +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/expressjs/cos/prometheus_alert_rules/.gitkeep +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/fastapi/__init__.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/fastapi/app.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/fastapi/cos/grafana_dashboards/fastapi.json +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/fastapi/cos/loki_alert_rules/.gitkeep +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/fastapi/cos/prometheus_alert_rules/.gitkeep +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/flask/__init__.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/flask/cos/grafana_dashboards/flask.json +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/flask/cos/loki_alert_rules/.gitkeep +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/flask/cos/prometheus_alert_rules/flask.rule +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/go/__init__.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/go/cos/grafana_dashboards/go.json +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/go/cos/loki_alert_rules/.gitkeep +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/go/cos/prometheus_alert_rules/.gitkeep +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/http_proxy.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/rabbitmq.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/s3.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/saml.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/springboot/__init__.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/springboot/cos/grafana_dashboards/spring_boot.json +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/springboot/cos/loki_alert_rules/.gitkeep +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/springboot/cos/prometheus_alert_rules/.gitkeep +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/templates/_gunicorn_json_logging.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/templates/fastapi/uvicorn-log-config.json +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/templates/fastapi/uvicorn_log_handler.py +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/templates/gunicorn.conf.py.j2 +0 -0
- {paas_charm-2.0.dev0 → paas_charm-2.0.dev3}/src/paas_charm/valkey.py +0 -0
- {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.
|
|
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:
|
|
15
|
-
Requires-Dist:
|
|
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.
|
|
19
|
-
Requires-Dist: pydantic==2.13.
|
|
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.
|
|
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
|
-
"
|
|
21
|
-
"
|
|
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.
|
|
25
|
-
"pydantic==2.13.
|
|
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",
|
|
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.
|
|
138
|
-
"coverage[toml]==7.
|
|
139
|
-
"gunicorn==26.
|
|
140
|
-
"opentelemetry-api==1.
|
|
141
|
-
"opentelemetry-sdk==1.
|
|
142
|
-
"ops[testing]==3.
|
|
143
|
-
"pydantic[email]==2.13.
|
|
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
|
-
"
|
|
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.
|
|
172
|
-
"cosl==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.
|
|
175
|
-
"juju==3.6.1.
|
|
176
|
-
"minio==7.2.
|
|
177
|
-
"ops==3.
|
|
178
|
-
"tenacity==9.
|
|
179
|
-
"playwright==1.
|
|
180
|
-
"lightkube==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
|
-
|
|
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=
|
|
174
|
-
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
|
|
9
|
+
from paas_charm.paas_config import PaasConfig
|
|
10
10
|
|
|
11
11
|
STATSD_HOST = "localhost:9125"
|
|
12
|
-
|
|
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
|
-
|
|
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=
|
|
46
|
+
port=application_port,
|
|
40
47
|
base_dir=base_dir,
|
|
41
|
-
app_dir=
|
|
48
|
+
app_dir=app_dir,
|
|
42
49
|
state_dir=state_dir,
|
|
43
50
|
service_name=framework_name,
|
|
44
|
-
|
|
45
|
-
|
|
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=
|
|
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
|
|
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
|
|
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 = "
|
|
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
|