Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,132 @@
26.2
- refactor(azure): Extract SSH key cert handling to new certs module
(#6802) [Peyton Robertson]
- fix(alpine): Use Alpine CDN as default APK mirror (#6947) [Natanael Copa]
- fix(oracle): detect iSCSI root via iBFT for dracut images (#6921)
[Gisaldjo Purbollari] (GH: 6915)
- Harden Jinja template rendering with sandboxing (#6888)
[Tyler Coatsworth]
- fix(mounts): escape special characters in fstab mount paths (#6911)
[Eugene Kalinin] (GH: 3603)
- net: add route-metric support in NetworkManager renderer (#6886)
[Harshavardhan Lankipalli]
- test: fix expected empty stderr on cloud-init analyze boot (#6925)
- feat: Fix import error (#6926) [Chloé E.M. Smith] (GH: 6924)
- fix(analyze): warn before GNU date fallback (#6879)
[nanookclaw] (GH: 4357)
- fix(debian): update debian backports suite for bullseye (#6885)
[Challvy Tee]
- test(opennebula): validate gen_conf() output against network-config-v2
schema (#6855) [Mickaël Canévet]
- ci: add scheduled integration runners for stonking and drop questing
- ci: update PR workflows from questing to latest released ubuntu series
resolute
- ci: bump labeler ver add pull-request write permission for labeling
(#6919)
- fix(tests): make user_groups integration tests distro-aware (#6889)
[Medha Mummigatti]
- chore: bump pylint version, dropping deprecated argparse.FileType
lints (#6917)
- fix(analyze): return integer exit code from analyze_boot (#6863)
[nanookclaw]
- chore: add type annotations to cloudinit.distros.parsers.hosts (#6916)
[ph1n3y]
- test: apt resolute gpg remove accounts for gpg-from-sq (#6902)
- feat(azure): add apply_network_config_set_name option to disable
renames (#6807) [Chris Patterson]
- test: pytest fix strict_parametrization_ids providing required id (#6908)
- docs: fix chpasswd user entries in cloud-config example (#6905)
[Wai Hlyan Min Thein] (GH: 6765)
- fix(distros): clarify misleading ssh_redirect_user warning (#6906)
[Sandhya-d] (GH: 3924)
- chore(ci): address review comments, single operation PRIVATE_KEY creation
- ci: standardize input/dispatch defaults, errors for missing secrets
- ci: rename PYCLOUDLIB_TOML_B64 to indicate base64-encoded
- ci: update image_type defaults, validate required secrets set perms
- ci: switch to pytest-json-ctrf and refactor job env/defaults
- ci: add lxd_vm and EC2 integration test workflows
- ci: require PYCLOUDLIB_TOML and SSH key secrets, write config to
runner temp
- chore(ci): rename and renumber workflow files to 1xx scheme
- feat(opennebula): support global SEARCH_DOMAIN for all interfaces
(#6813) [Mickaël Canévet]
- feat(azure): report failure if missing customdata (#6779) [Cade Jacobson]
- docs: make password lock explicit in users-groups example 3 (#6869)
[Clinton Phillips]
- docs: clarify default user entry in users examples (#6894)
[alan747271363-art] (GH: 6702)
- fix(cmd): enable mypy strict checking for cloudinit.cmd (#6861)
[Borja Velasco Santamaría]
- ai: copilot review comment resolution
- tests: simplify get_syslog_or_console to get_journal_syslog for
systemd-v255
- test: prefer reading journalctl transport=syslog over /var/log/syslog
- docs: fix invalid badge URLs (#6900) [Richard Coker] (GH: 6899)
- fix(azurelinux): support Azure Linux 4.0 (#6874) [Dan Streetman]
- tests: expect intermittent warning accessing Azure IMDS. Avoid
client.destroy
- fix(cloudstack): get vr information from network manager leases (#6829)
[Ani Sinha]
- fix(cloudstack): get domain name information from network manager
leases (#6829) [Ani Sinha]
- test: fix test_get_domainname_isc_dhclient mock leak (#6829) [Ani Sinha]
- feat(dhcp): Add network manager lease parsing capability (#6829)
[Ani Sinha]
- test: logger specifically configure localhost:514 destination (#6897)
- fix(ug_util): prioritize user-data.users over the default user config
(#6860) [Mostafa Abdelwahab] (GH: 6703)
- ci: fix alpine edge python3.14 tox missing dep py3-python-discovery
(#6864)
- fix(docs): correct users and groups examples 3, 4 and 7 (#6858)
[Bastien Traverse] (GH: 6857)
- feat(opennebula): support ETHx_ROUTES static routes in network config
(#6810) [Mickaël Canévet]
- fix(opennebula): coerce MTU to int in gen_conf() (#6856)
[Mickaël Canévet]
- Enable Amazon Linux to yum_add_repo and ca_certs. (#6767) [joe-usa]
- fix: add single retry of ssh-import-id on exit 1 (#6805)
- fix:(disk_setup):handle empty disk in check_partition_gpt_layout_sfdisk
(#6728) [Amy Chen] (GH: 6682)
- chore(opennebula): enable mypy type checking (#6827) [Mickaël Canévet]
- fix(net): do not resolve IPs when netloc contains a port (#6841)
[Orukaria Ndukiye]
- test: Add new `fake_fs` fixture backed by pyfakefs (#6785)
- feat(bsd): add OpenBSD support to Meson build (#6789)
[Hyacinthe Cartiaux]
- ci: move ssh-import-id tests to github instead of launchpad keys (#6830)
- fix: increase raspberry pi usb timeout (#6823) [Nick Brown]
- fix(debian): Fix locale generation (#6472) [Paul] (GH: 6471)
- fix(ubuntu): Configure arm64 to use archive.ubuntu.com (#6826)
[Dave Jones] (LP: 6825, #2147101)
- test: fix integration test_combined for rhel (#6791) [Amy Chen]
- test: fix test_schema when the schema validation takes longer (#6822)
[Ani Sinha]
- tools: fix brpm script to use correct builddir in temp directory (#6809)
- chore: drop unnecessary pylint ignore settings, use pytest --strict-markers
(#6817)
- ci: disable daily jobs on forks (#6821)
- fix(openbsd): specify the correct usr_lib_exec path (#6793)
[Hyacinthe Cartiaux]
- test: fix missing mocker of rmtree for redhat unittests (#6808)
- chore: drop outdated code (#6798)
- ci: fix integration test CLOUD_INIT_LOCAL_LOG_PATH. drop hashFiles
(#6804)
- chore: auto-format shell (#6780)
- chore(net): log when klibc is parsed (#6799)
- ci: add cloud-init integration test logs upon failure (#6803)
- fix(bsd): correct the _ROOT_TMPDIR path for *BSD system (#6794)
[Hyacinthe Cartiaux] (GH: 5789)
- fix(azure): catch import error as reportable (#6714)
[Cade Jacobson] (GH: 6770)
- WSL: Subprocess cmd.exe with /U to output UTF-16LE (#6717)
[Carlos Nihelton] (GH: 6716)
- feat(util): fail early when hostname is not resolvable in is_resolvable
(#6772) [Alejandro Perez]
- feat(azure): introduce experimental skip_ready_report for Azure (#6771)
[Chris Patterson]
- test: apt tests need to wait_for_cloud_init on potentially slow VMs
- test: use rmadison form devscripts to determine hello pkg version

26.1
- test: update hello package tested using rmadison (#6774)
- test: add details about failing package status to aid in debug (#6775)
Expand Down
13 changes: 13 additions & 0 deletions cloudinit/analyze/dump.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# This file is part of cloud-init. See LICENSE file for license information.

import calendar
import logging
import sys
from datetime import datetime, timezone
from typing import IO, Any, Dict, List, Optional, TextIO, Tuple

from cloudinit import atomic_helper, subp, util

LOG = logging.getLogger(__name__)

stage_to_description: Dict[str, str] = {
"finished": "finished running cloud-init",
"init-local": "starting search for local datasources",
Expand Down Expand Up @@ -76,6 +79,16 @@ def parse_timestamp_from_date(timestampstr: str) -> float:
raise ValueError(
f"Unable to parse timestamp without GNU date: [{timestampstr}]"
)
# Transitional: shelling out to GNU date is slated for removal in favor of
# native Python parsing. Log the unrecognized timestamp so maintainers can
# collect formats that need direct support (see GH-4357).
LOG.warning(
"analyze: falling back to GNU %s(1) to parse unrecognized "
"timestamp %r; please report this format upstream so it can be "
"handled natively in Python.",
date,
timestampstr,
)
return float(
subp.subp([date, "-u", "+%s.%3N", "-d", timestampstr]).stdout.strip()
)
Expand Down
2 changes: 1 addition & 1 deletion cloudinit/config/cc_apk_configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@


# If no mirror is specified then use this one
DEFAULT_MIRROR = "https://alpine.global.ssl.fastly.net/alpine"
DEFAULT_MIRROR = "https://dl-cdn.alpinelinux.org/alpine"


REPOSITORIES_TEMPLATE = """\
Expand Down
15 changes: 13 additions & 2 deletions cloudinit/config/cc_mounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ def parse_fstab() -> Tuple[List[str], Dict[str, str], List[str]]:
continue
toks = line.split()
if toks:
fstab_devs[toks[0]] = line
fstab_devs[util.unescape_fstab_field(toks[0])] = line
fstab_lines.append(line)
return fstab_lines, fstab_devs, fstab_removed

Expand Down Expand Up @@ -584,7 +584,18 @@ def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None:
LOG.debug("No modifications to fstab needed")
return

cfg_lines = ["\t".join(entry) for entry in updated_cfg]
# Only fs_spec (device) and fs_file (mount point) can contain values that
# need escaping; the remaining fstab fields never do, so we skip them.
cfg_lines = [
"\t".join(
[
util.escape_fstab_field(entry[0]),
util.escape_fstab_field(entry[1]),
*entry[2:],
]
)
for entry in updated_cfg
]

dirs = [d[1] for d in updated_cfg if d[1].startswith("/")]

Expand Down
4 changes: 2 additions & 2 deletions cloudinit/config/schemas/schema-cloud-config-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -1052,8 +1052,8 @@
"properties": {
"base_url": {
"type": "string",
"default": "https://alpine.global.ssl.fastly.net/alpine",
"description": "The base URL of an Alpine repository, or mirror, to download official packages from. If not specified then it defaults to ``https://alpine.global.ssl.fastly.net/alpine``."
"default": "https://dl-cdn.alpinelinux.org/alpine",
"description": "The base URL of an Alpine repository, or mirror, to download official packages from. If not specified then it defaults to ``https://dl-cdn.alpinelinux.org/alpine``."
},
"community_enabled": {
"type": "boolean",
Expand Down
6 changes: 5 additions & 1 deletion cloudinit/handlers/jinja_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,11 @@ def render_jinja_payload(payload, payload_fn, instance_data, debug=False):
)
try:
rendered_payload = render_string(payload, instance_jinja_vars)
except (TypeError, exceptions.UndefinedError) as e:
except (
TypeError,
exceptions.UndefinedError,
exceptions.SecurityError,
) as e:
LOG.warning("Ignoring jinja template for %s: %s", payload_fn, str(e))
return None
warnings = [
Expand Down
12 changes: 3 additions & 9 deletions cloudinit/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#
# This file is part of cloud-init. See LICENSE file for license information.

import importlib
import importlib.util
from types import ModuleType
from typing import Optional, Sequence

Expand Down Expand Up @@ -42,10 +42,7 @@ def match_case_insensitive_module_name(mod_name: str) -> str:
if not mod_name.startswith("DataSource"):
mod_name = f"DataSource{mod_name}"
modules = {}
# mypy is right to warn here. This isn't a documented method.
# However it has worked for years without issue, so fixing it is not
# a high priority. We'll fix it when it breaks.
spec = importlib.util.find_spec("cloudinit.sources") # type: ignore
spec = importlib.util.find_spec("cloudinit.sources")
if spec and spec.submodule_search_locations:
for dir in spec.submodule_search_locations:
modules.update(util.get_modules_from_dir(dir))
Expand All @@ -70,10 +67,7 @@ def find_module(
# Add base name to search paths. Filter out empty paths.
full_path = ".".join(filter(None, [path, base_name]))
lookup_paths.append(full_path)
# mypy is right to warn here. This isn't a documented method.
# However it has worked for years without issue, so fixing it is not
# a high priority. We'll fix it when it breaks.
if not importlib.util.find_spec(full_path): # type: ignore
if not importlib.util.find_spec(full_path):
continue
# Check that required_attrs are all present within the module.
if _count_attrs(full_path, required_attrs) == len(required_attrs):
Expand Down
3 changes: 3 additions & 0 deletions cloudinit/net/network_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,9 @@ def render_interface(self, iface, network_state, renderer):
self.config[family]["gateway"] = subnet["gateway"]
for route in subnet["routes"]:
self._add_route(route)
# metric may apply to both dhcp and static config
if "metric" in subnet:
self.config[family]["route-metric"] = str(subnet["metric"])
# Add subnet-level DNS
if "dns_nameservers" in subnet:
found_nameservers.extend(subnet["dns_nameservers"])
Expand Down
25 changes: 5 additions & 20 deletions cloudinit/sources/DataSourceAzure.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import requests

from cloudinit import net, performance, sources, ssh_util, subp, util
from cloudinit import net, performance, sources, subp, util
from cloudinit.config import cc_mounts
from cloudinit.event import EventScope, EventType
from cloudinit.net import device_driver
Expand All @@ -30,7 +30,7 @@
)
from cloudinit.net.ephemeral import EphemeralDHCPv4, EphemeralIPv4Network
from cloudinit.reporting import events
from cloudinit.sources.azure import errors, identity, imds, kvp
from cloudinit.sources.azure import certs, errors, identity, imds, kvp
from cloudinit.sources.helpers import netlink
from cloudinit.sources.helpers.azure import (
DEFAULT_WIRESERVER_ENDPOINT,
Expand Down Expand Up @@ -1077,7 +1077,9 @@ def _get_public_keys_from_imds(self, imds_md: dict) -> List[str]:
report_diagnostic_event(log_msg, logger_func=LOG.debug)
raise

if any(not _key_is_openssh_formatted(key=key) for key in ssh_keys):
ssh_keys = [certs.sanitize_openssh_key(key) for key in ssh_keys]

if any(not certs.is_openssh_formatted(key) for key in ssh_keys):
log_msg = "Key(s) not in OpenSSH format"
report_diagnostic_event(log_msg, logger_func=LOG.debug)
raise ValueError(log_msg)
Expand Down Expand Up @@ -1784,23 +1786,6 @@ def _disable_password_from_imds(imds_data):
return None


def _key_is_openssh_formatted(key):
"""
Validate whether or not the key is OpenSSH-formatted.
"""
# See https://bugs.launchpad.net/cloud-init/+bug/1910835
if "\r\n" in key.strip():
return False

parser = ssh_util.AuthKeyLineParser()
try:
akl = parser.parse(key)
except TypeError:
return False

return akl.keytype is not None


def _partitions_on_device(devpath, maxnum=16):
# return a list of tuples (ptnum, path) for each part on devpath
for suff in ("-part", "p", ""):
Expand Down
Loading