Skip to content

Commit da628df

Browse files
feat: add K8SCharmConfig for the K8s-only locales (#150)
* feat: add K8s locale superset to single kernel The K8s rock ships C.utf8 and POSIX (from the base image's libc-bin) that the VM snap's locales-all omits, so #137's single SNAP_LOCALES rejects those otherwise-valid values for the K8s response-lc-* config. Add K8S_LOCALES extending SNAP_LOCALES with the two rock-only entries so the K8s config model can accept them while VM keeps the snap-only set. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com> * feat: add K8SCharmConfig for the K8s-only locales #137's CharmConfig validates response-lc-* against SNAP_LOCALES, the VM snap's locale set. The K8s rock additionally ships C.utf8 and POSIX, so the K8s charm must accept them. Add K8SCharmConfig, a CharmConfig subclass that widens the three locale fields to K8S_LOCALES, so each substrate validates against the locales actually available to it. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com> * build: bump library version to 16.3.0 The aux migration adds new public API to the library (K8SCharmConfig, K8S_LOCALES, the shared constants and the architecture guard), so raise the minor version for the next release. Aligns with the 16.3.0 target of the cluster work in #145. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com> * build: make psycopg2 an optional extra psycopg2 is only needed by the library's PostgreSQL client code, but as a top-level dependency it forced every consumer — including the charms' integration tests, which import only the pure-Python locale constants — to build it from source. Move it into the `postgresql` extra so a bare install skips it; consumers using the DB code request `[postgresql]`. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com> --------- Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
1 parent a9c2480 commit da628df

4 files changed

Lines changed: 63 additions & 7 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[project]
55
name = "postgresql-charms-single-kernel"
66
description = "Shared and reusable code for PostgreSQL-related charms"
7-
version = "16.2.2"
7+
version = "16.3.0"
88
readme = "README.md"
99
license = {file = "LICENSE"}
1010
authors = [
@@ -18,12 +18,12 @@ classifiers = [
1818
requires-python = ">=3.8,<4.0"
1919
dependencies = [
2020
"ops>=2.0.0",
21-
"psycopg2>=2.9.10",
2221
"tenacity>=9.0.0",
2322
]
2423

2524
[project.optional-dependencies]
2625
postgresql = [
26+
"psycopg2>=2.9.10",
2727
"httpx; python_version >= '3.12'",
2828
"data-platform-helpers>=0.1.7; python_version >= '3.12'",
2929
"charmlibs-pathops>=1.0.1; python_version >= '3.12'",

single_kernel_postgresql/core/config.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from pydantic import Field, NonNegativeInt, PositiveInt
1111

12-
from single_kernel_postgresql.config.locales import SNAP_LOCALES
12+
from single_kernel_postgresql.config.locales import K8S_LOCALES, SNAP_LOCALES
1313
from single_kernel_postgresql.lib.charms.data_platform_libs.v1.data_models import BaseConfigModel
1414

1515
logger = logging.getLogger(__name__)
@@ -252,3 +252,15 @@ def keys(cls) -> list[str]:
252252
def plugin_keys(cls) -> filter:
253253
"""Return plugin config names in a iterable."""
254254
return filter(lambda x: x.startswith("plugin_"), cls.keys())
255+
256+
257+
class K8SCharmConfig(CharmConfig):
258+
"""Structured configuration for the K8s charm.
259+
260+
Identical to :class:`CharmConfig`, but the locale options also accept the two
261+
locales the K8s rock provides and the VM snap does not (``C.utf8``, ``POSIX``).
262+
"""
263+
264+
response_lc_monetary: K8S_LOCALES | None = Field(default=None)
265+
response_lc_numeric: K8S_LOCALES | None = Field(default=None)
266+
response_lc_time: K8S_LOCALES | None = Field(default=None)

tests/unit/test_config.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright 2026 Canonical Ltd.
2+
# See LICENSE file for licensing details.
3+
from typing import get_args
4+
5+
import pytest
6+
from pydantic import TypeAdapter, ValidationError
7+
from single_kernel_postgresql.core.config import CharmConfig, K8SCharmConfig
8+
9+
_LOCALE_FIELDS = ("response_lc_monetary", "response_lc_numeric", "response_lc_time")
10+
11+
12+
def _locale_values(model: type, field: str) -> set[str]:
13+
annotation = model.model_fields[field].annotation
14+
literal = next(arm for arm in get_args(annotation) if arm is not type(None))
15+
return set(get_args(literal))
16+
17+
18+
def test_charmconfig_imports_and_keys_resolve():
19+
keys = CharmConfig.keys()
20+
for field in _LOCALE_FIELDS:
21+
assert field in keys
22+
23+
24+
def test_k8s_config_adds_exactly_the_rock_only_locales():
25+
"""K8s locale fields are the shared set plus exactly C.utf8 and POSIX."""
26+
for field in _LOCALE_FIELDS:
27+
base = _locale_values(CharmConfig, field)
28+
k8s = _locale_values(K8SCharmConfig, field)
29+
assert "C" in base and "C.utf8" not in base and "POSIX" not in base
30+
assert k8s - base == {"C.utf8", "POSIX"}
31+
assert base - k8s == set()
32+
33+
34+
def test_locale_fields_validate_per_substrate():
35+
"""The VM model rejects C.utf8/POSIX; the K8s model accepts them."""
36+
for field in _LOCALE_FIELDS:
37+
base = TypeAdapter(CharmConfig.model_fields[field].annotation)
38+
k8s = TypeAdapter(K8SCharmConfig.model_fields[field].annotation)
39+
for locale in ("C.utf8", "POSIX"):
40+
with pytest.raises(ValidationError):
41+
base.validate_python(locale)
42+
k8s.validate_python(locale)
43+
base.validate_python("C")
44+
k8s.validate_python("C")

uv.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)