Skip to content

Commit

Permalink
feat: Add deprecation boundary to logger (#5411)
Browse files Browse the repository at this point in the history
This will all stable distros to define whether a key is deprecated
based on the version of cloud-init which deprecated the key.
  • Loading branch information
holmanb committed Jun 27, 2024
1 parent bcc5920 commit e80514b
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 23 deletions.
21 changes: 8 additions & 13 deletions cloudinit/cmd/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,17 +236,12 @@ def attempt_cmdline_url(path, network=True, cmdline=None) -> Tuple[int, str]:
is_cloud_cfg = False
if is_cloud_cfg:
if cmdline_name == "url":
return (
log.DEPRECATED,
str(
util.deprecate(
deprecated="The kernel command line key `url`",
deprecated_version="22.3",
extra_message=" Please use `cloud-config-url` "
"kernel command line parameter instead",
return_log=True,
),
),
return util.deprecate(
deprecated="The kernel command line key `url`",
deprecated_version="22.3",
extra_message=" Please use `cloud-config-url` "
"kernel command line parameter instead",
skip_log=True,
)
else:
if cmdline_name == "cloud-config-url":
Expand Down Expand Up @@ -972,8 +967,8 @@ def main(sysv_args=None):
deprecated="`init`",
deprecated_version="24.1",
extra_message="Use `cloud-init init` instead.",
return_log=True,
)
skip_log=True,
).message
parser_mod.add_argument(
"--mode",
"-m",
Expand Down
29 changes: 29 additions & 0 deletions cloudinit/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,35 @@
to write /etc/apt/sources.list directly.
"""

DEPRECATION_INFO_BOUNDARY = "devel"
"""
DEPRECATION_INFO_BOUNDARY is used by distros to configure at which upstream
version to start logging deprecations at a level higher than INFO.
The default value "devel" tells cloud-init to log all deprecations higher
than INFO. This value may be overriden by downstreams in order to maintain
stable behavior across releases.
Jsonschema key deprecations and inline logger deprecations include a
deprecated_version key. When the variable below is set to a version,
cloud-init will use that version as a demarcation point. Deprecations which
are added after this version will be logged as at an INFO level. Deprecations
which predate this version will be logged at the higher DEPRECATED level.
Downstreams that want stable log behavior may set the variable below to the
first version released in their stable distro. By doing this, they can expect
that newly added deprecations will be logged at INFO level. The implication of
the different log levels is that logs at DEPRECATED level result in a return
code of 2 from `cloud-init status`.
format:
<value> :: = <default> | <version>
<default> ::= "devel"
<version> ::= <major> "." <minor> ["." <patch>]
where <major>, <minor>, and <patch> are positive integers
"""


def get_features() -> Dict[str, bool]:
"""Return a dict of applicable features/overrides and their values."""
Expand Down
35 changes: 25 additions & 10 deletions cloudinit/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
Generator,
List,
Mapping,
NamedTuple,
Optional,
Sequence,
TypeVar,
Expand All @@ -61,6 +62,7 @@
from cloudinit import (
features,
importer,
log,
mergers,
net,
settings,
Expand Down Expand Up @@ -89,6 +91,11 @@
FALSE_STRINGS = ("off", "0", "no", "false")


class DeprecationLog(NamedTuple):
log_level: int
message: str


def kernel_version():
return tuple(map(int, os.uname().release.split(".")[:2]))

Expand Down Expand Up @@ -3209,8 +3216,8 @@ def deprecate(
deprecated_version: str,
extra_message: Optional[str] = None,
schedule: int = 5,
return_log: bool = False,
):
skip_log: bool = False,
) -> DeprecationLog:
"""Mark a "thing" as deprecated. Deduplicated deprecations are
logged.
Expand All @@ -3226,8 +3233,10 @@ def deprecate(
@param schedule: Manually set the deprecation schedule. Defaults to
5 years. Leave a comment explaining your reason for deviation if
setting this value.
@param return_log: Return log text rather than logging it. Useful for
@param skip_log: Return log text rather than logging it. Useful for
running prior to logging setup.
@return: NamedTuple containing log level and log message
DeprecationLog(level: int, message: str)
Note: uses keyword-only arguments to improve legibility
"""
Expand All @@ -3242,14 +3251,20 @@ def deprecate(
f"{deprecated_version} and scheduled to be removed in "
f"{version_removed}. {message}"
).rstrip()
if return_log:
return deprecate_msg
if dedup not in deprecate._log: # type: ignore
if (
"devel" != features.DEPRECATION_INFO_BOUNDARY
and Version.from_str(features.DEPRECATION_INFO_BOUNDARY) < version
):
LOG.info(deprecate_msg)
level = logging.INFO
elif hasattr(LOG, "deprecated"):
level = log.DEPRECATED
else:
level = logging.WARN
if not skip_log and dedup not in deprecate._log: # type: ignore
deprecate._log.add(dedup) # type: ignore
if hasattr(LOG, "deprecated"):
LOG.deprecated(deprecate_msg) # type: ignore
else:
LOG.warning(deprecate_msg)
LOG.log(level, deprecate_msg)
return DeprecationLog(level, deprecate_msg)


def deprecate_call(
Expand Down

0 comments on commit e80514b

Please sign in to comment.