Skip to content

Commit

Permalink
feat(locale): locales install on minimal images when cfg requests (#5799
Browse files Browse the repository at this point in the history
)
  • Loading branch information
blackboxsw committed Oct 9, 2024
1 parent 90d905b commit dba2dd3
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 28 deletions.
24 changes: 20 additions & 4 deletions cloudinit/distros/debian.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,12 @@ def apply_locale(self, locale, out_fn=None, keyname="LANG"):
need_conf = not conf_fn_exists or need_regen or sys_locale_unset

if need_regen:
regenerate_locale(locale, out_fn, keyname=keyname)
regenerate_locale(
locale,
out_fn,
keyname=keyname,
install_function=self.install_packages,
)
else:
LOG.debug(
"System has '%s=%s' requested '%s', skipping regeneration.",
Expand All @@ -119,7 +124,12 @@ def apply_locale(self, locale, out_fn=None, keyname="LANG"):
)

if need_conf:
update_locale_conf(locale, out_fn, keyname=keyname)
update_locale_conf(
locale,
out_fn,
keyname=keyname,
install_function=self.install_packages,
)
# once we've updated the system config, invalidate cache
self.system_locale = None

Expand Down Expand Up @@ -267,11 +277,15 @@ def read_system_locale(sys_path=LOCALE_CONF_FN, keyname="LANG"):
return sys_val


def update_locale_conf(locale, sys_path, keyname="LANG"):
def update_locale_conf(
locale, sys_path, keyname="LANG", install_function=None
):
"""Update system locale config"""
LOG.debug(
"Updating %s with locale setting %s=%s", sys_path, keyname, locale
)
if not subp.which("update-locale"):
install_function(["locales"])
subp.subp(
[
"update-locale",
Expand All @@ -282,7 +296,7 @@ def update_locale_conf(locale, sys_path, keyname="LANG"):
)


def regenerate_locale(locale, sys_path, keyname="LANG"):
def regenerate_locale(locale, sys_path, keyname="LANG", install_function=None):
"""
Run locale-gen for the provided locale and set the default
system variable `keyname` appropriately in the provided `sys_path`.
Expand All @@ -298,5 +312,7 @@ def regenerate_locale(locale, sys_path, keyname="LANG"):
return

# finally, trigger regeneration
if not subp.which("locale-gen"):
install_function(["locales"])
LOG.debug("Generating locales for %s", locale)
subp.subp(["locale-gen", locale], capture=False)
23 changes: 14 additions & 9 deletions tests/integration_tests/modules/test_combined.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
from cloudinit.util import is_true
from tests.integration_tests.decorators import retry
from tests.integration_tests.instances import IntegrationInstance
from tests.integration_tests.integration_settings import PLATFORM
from tests.integration_tests.integration_settings import (
OS_IMAGE_TYPE,
PLATFORM,
)
from tests.integration_tests.releases import CURRENT_RELEASE, IS_UBUNTU, JAMMY
from tests.integration_tests.util import (
get_feature_flag_value,
Expand Down Expand Up @@ -183,14 +186,16 @@ def test_configured_locale(self, class_client: IntegrationInstance):
assert "LANG=en_GB.UTF-8" in default_locale

locale_a = client.execute("locale -a")
verify_ordered_items_in_text(["en_GB.utf8", "en_US.utf8"], locale_a)

locale_gen = client.execute(
"cat /etc/locale.gen | grep -v '^#' | uniq"
)
verify_ordered_items_in_text(
["en_GB.UTF-8", "en_US.UTF-8"], locale_gen
)
locale_gen = client.execute("grep -v '^#' /etc/locale.gen | uniq")
if OS_IMAGE_TYPE == "minimal":
# Minimal images don't have a en_US.utf8 locale
expected_locales = ["C.utf8", "en_GB.utf8"]
expected_locale_gen = ["en_GB.UTF-8", "UTF-8"]
else:
expected_locales = ["en_GB.utf8", "en_US.utf8"]
expected_locale_gen = ["en_GB.UTF-8", "en_US.UTF-8"]
verify_ordered_items_in_text(expected_locales, locale_a)
verify_ordered_items_in_text(expected_locale_gen, locale_gen)

def test_random_seed_data(self, class_client: IntegrationInstance):
"""Integration test for the random seed module.
Expand Down
23 changes: 14 additions & 9 deletions tests/unittests/config/test_cc_locale.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,20 @@ def test_locale_update_config_if_different_than_default(self, tmpdir):
with mock.patch(
"cloudinit.distros.debian.LOCALE_CONF_FN", locale_conf.strpath
):
cc_locale.handle("cc_locale", cfg, cc, [])
m_subp.assert_called_with(
[
"update-locale",
"--locale-file=%s" % locale_conf.strpath,
"LANG=C.UTF-8",
],
capture=False,
)
with mock.patch(
"cloudinit.distros.debian.subp.which",
return_value="/usr/sbin/update-locale",
) as m_which:
cc_locale.handle("cc_locale", cfg, cc, [])
m_subp.assert_called_with(
[
"update-locale",
"--locale-file=%s" % locale_conf.strpath,
"LANG=C.UTF-8",
],
capture=False,
)
m_which.assert_called_once_with("update-locale")


class TestLocaleSchema:
Expand Down
59 changes: 53 additions & 6 deletions tests/unittests/distros/test_debian.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# This file is part of cloud-init. See LICENSE file for license information.
from unittest import mock

import pytest

from cloudinit import util
Expand All @@ -11,6 +13,10 @@
class TestDebianApplyLocale:
@pytest.fixture
def m_subp(self, mocker):
mocker.patch(
"cloudinit.distros.debian.subp.which",
return_value="/usr/bin/locale-gen",
)
yield mocker.patch(
"cloudinit.distros.debian.subp.subp", return_value=(None, None)
)
Expand All @@ -26,15 +32,34 @@ def test_no_rerun(self, distro, m_subp):
distro.apply_locale(locale, out_fn=LOCALE_PATH)
m_subp.assert_not_called()

def test_no_regen_on_c_utf8(self, distro, m_subp):
"""If locale is set to C.UTF8, do not attempt to call locale-gen"""
@pytest.mark.parametrize(
"which_response,install_pkgs",
(("", ["locales"]), ("/usr/bin/update-locale", [])),
)
def test_no_regen_on_c_utf8(
self, which_response, install_pkgs, distro, mocker, m_subp
):
"""If locale is set to C.UTF8, do not attempt to call locale-gen.
Install locales deb package if not present and update-locale is called.
"""
m_which = mocker.patch(
"cloudinit.distros.debian.subp.which",
return_value=which_response,
)
m_subp.return_value = (None, None)
locale = "C.UTF-8"
util.write_file(LOCALE_PATH, "LANG=%s\n" % "en_US.UTF-8", omode="w")
distro.apply_locale(locale, out_fn=LOCALE_PATH)
with mock.patch.object(distro, "install_packages") as m_install:
distro.apply_locale(locale, out_fn=LOCALE_PATH)
assert [
["update-locale", f"--locale-file={LOCALE_PATH}", f"LANG={locale}"]
] == [p[0][0] for p in m_subp.call_args_list]
m_which.assert_called_with("update-locale")
if install_pkgs:
m_install.assert_called_once_with(install_pkgs)
else:
m_install.assert_not_called()

def test_rerun_if_different(self, distro, m_subp, caplog):
"""If system has different locale, locale-gen should be called."""
Expand All @@ -59,10 +84,24 @@ def test_rerun_if_different(self, distro, m_subp, caplog):
in caplog.text
)

def test_rerun_if_no_file(self, distro, m_subp):
"""If system has no locale file, locale-gen should be called."""
@pytest.mark.parametrize(
"which_response,install_pkgs",
(("", ["locales"]), ("/usr/bin/locale-gen", [])),
)
def test_rerun_if_no_file(
self, which_response, install_pkgs, mocker, distro, m_subp
):
"""If system has no locale file, locale-gen should be called.
Install locales package if absent and locale-gen called.
"""
m_which = mocker.patch(
"cloudinit.distros.debian.subp.which",
return_value=which_response,
)
locale = "en_US.UTF-8"
distro.apply_locale(locale, out_fn=LOCALE_PATH)
with mock.patch.object(distro, "install_packages") as m_install:
distro.apply_locale(locale, out_fn=LOCALE_PATH)
assert [
["locale-gen", locale],
[
Expand All @@ -71,6 +110,14 @@ def test_rerun_if_no_file(self, distro, m_subp):
f"LANG={locale}",
],
] == [p[0][0] for p in m_subp.call_args_list]
assert [
mock.call("locale-gen"),
mock.call("update-locale"),
] == m_which.call_args_list
if install_pkgs:
m_install.assert_called_with(install_pkgs)
else:
m_install.assert_not_called()

def test_rerun_on_unset_system_locale(self, distro, m_subp, caplog):
"""If system has unset locale, locale-gen should be called."""
Expand Down

0 comments on commit dba2dd3

Please sign in to comment.