Skip to content

Fix Linux4 Auto OS issue observed in Canary(/etc/dnf/automatic.conf absent by design in Linux4) - #368

Open
yashnap wants to merge 5 commits into
masterfrom
autoOSFix_dnf5
Open

Fix Linux4 Auto OS issue observed in Canary(/etc/dnf/automatic.conf absent by design in Linux4) #368
yashnap wants to merge 5 commits into
masterfrom
autoOSFix_dnf5

Conversation

@yashnap

@yashnap yashnap commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

REPRO STEPS:
Provision an Azure Linux 4.0 (AzL4) VM with DNF5
Install or verify DNF5 automatic plugin: rpm -qa | grep dnf5-plugin-automatic
Enable machine default auto updates using timer:
sudo systemctl enable --now dnf5-automatic.timer
Verify timer is enabled:
systemctl is-enabled dnf5-automatic.timer -> ENABLED

No configuration file exist yet :
/etc/dnf/automatic.conf
Run Assess/Install Patch from Azure Portal.

Expected: LPE should read current state which is enabled, disable it, perform the task and leave it in disabled state

Actual: LPE correctly Detects the current state of the system but when trying to disable, it runs into File not found error since /etc/dnf/automatic.conf is not automatically created when service is enabled in Azl4 (DNF5).

SOLUTION

According to their DNF5 doc (Automatic Command — dnf5 documentation), it uses 2 configuration files.

/usr/share/dnf5/dnf5-plugins/automatic.conf contains the default values and should be available once dnf5 is installed
/etc/dnf/automatic.conf contains Host-specific overrides and may not be available/created during dnf5 installation. Has to be explicitly created
They advise using /etc/dnf/automatic.conf to customize configurations for the automatic service.

Sequence of steps for disabling machine default OS updates should be:

  1. Read and backup both /usr/share/dnf5/dnf5-plugins/automatic.conf and /etc/dnf/automatic.conf .
  2. Backup file contains a json structure : have 2 json structures, one named 'default-dnf5-automatic' and the other 'override-dnf5-automatic'. A non existent override file can be represented with empty apply_updates and download-updates values in backup.
    3.Check if service is installed
    If not, do nothing
    If installed, pre-emptively disable it
    For this, if the override file does not exist, create a copy from default file and set apply_updates and download_updates to false in the override. DO NOT modify the default file

Now to revert auto OS update to machine default OS update on when AutoPatching is disabled:

  1. Get the current auto OS config on the machine, which will include its installation, enable state on reboot, apply and download updates values in both default and override file.
  2. If service is not installed, do nothing
    If installed, log current state
    Read backup file and revert to the state it contains. i.e. if override file did not exist before, remove it. If values in default config were different to what we had logged in backup, modify default config to backup values.

Since override config does not always exist, code does not throw an exception for something that is by design

TESTING

ARM ID : /subscriptions/6acc8a91-e2b0-4041-a069-c2932ab42fd9/resourceGroups/azl4-rg-canary/providers/Microsoft.Compute/virtualMachines/linux4-auto-os

  1. No service Installed
    no_service_installed.log

  2. Service instal
    service_installed_no_timer.log
    led but timer not enabled

  3. Service inst
    service_installed_yes_timer.log
    alled and timer enabled

  4. Idempotent call checking if backup
    revert_idempotent.log
    is not overriden/recreated

  5. Revert to Default( Offboarding from GuestPatching) and service installed
    revert_service_installed.log

  6. Idempotent call after revert
    revert_idempotent.log

  7. Revert back ( Onboard again to Guest Patching)
    revert_to_Azure.log

 

ID Scenario Initial State Findings Result
S1 Service not installed dnf5-plugin-automatic not installed No unexpected failures observed. Auto OS state DISABLED ✅ PASS
S2 Service installed, timer not enabled dnf5-plugin-automatic installed, timer disabled, no override file Auto OS state correctly detected as Disabled. ✅ PASS
S3 Service installed, timer enabled dnf5-plugin-automatic installed, timer enabled, no override file Auto OS state correctly detected as Enabled. During AutoByPlatform onboarding, override file was created, timer was disabled, backup was captured, and Auto OS state transitioned from Enabled → Disabled. ✅ PASS
R1 Change patch mode from AutomaticByPlatform to ImageDefault AutoByPlatform state, timer disabled, backup present Original configuration restored, timer re-enabled, override file removed, and machine returned to ImageDefault state. ✅ PASS
R2 ImageDefault idempotent validation ImageDefault state already restored Assessment was run again. No additional configuration changes were made and existing ImageDefault state was preserved. ✅ PASS
R3 Change patch mode back to AutomaticByPlatform after restore Restored ImageDefault state, timer enabled, override file absent Override file recreated, timer disabled, and AutoByPlatform ownership successfully re-established. ✅ PASS

Copilot AI review requested due to automatic review settings July 24, 2026 18:21
@yashnap yashnap changed the title Linux4 Auto OS iisue observed in Canary fix Linux4 Auto OS issue observed in Canary fix (/etc/dnf/automatic.conf absent by design in Linux4) Jul 24, 2026
@yashnap yashnap changed the title Linux4 Auto OS issue observed in Canary fix (/etc/dnf/automatic.conf absent by design in Linux4) Fix Linux4 Auto OS issue observed in Canary(/etc/dnf/automatic.conf absent by design in Linux4) Jul 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the DNF5 auto OS update handling to support Azure Linux 4’s behavior where /etc/dnf/automatic.conf may be absent by design, by separating “default” vs “override” configuration sources and making disable/revert flows resilient to a missing override file.

Changes:

  • Treat DNF5 automatic configuration as two layers: default (/usr/share/.../automatic.conf) + override (/etc/dnf/automatic.conf), and compute effective values accordingly.
  • Create an override config from the default config when disabling auto OS updates if the override file doesn’t exist; on revert, remove the override file when backup indicates it was absent.
  • Update unit tests and legacy env mocks to reflect the new default/override split and dependency simulation changes.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/core/src/package_managers/Dnf5PackageManager.py Implements default+override config handling, new backup keys, and override-file creation/removal during disable/revert.
src/core/tests/Test_Dnf5PackageManager.py Updates tests for new backup schema and config paths; adds coverage for override removal failure.
src/core/tests/library/LegacyEnvLayerExtensions.py Extends command-output mocking for dnf5 install --assumeno --skip-broken openssl used by tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

# restore sys.stdout output
sys.stdout = original_stdout
self.__assert_std_io(captured_output=captured_output,expected_output=testcase["stdio"]["expected_output"])
print("packagemanager" ,dir(package_manager))
Comment on lines 638 to +642
def is_backup_valid_for_dnf5_automatic(self, image_default_patch_configuration_backup):
if self.dnf5_auto_os_update_service in image_default_patch_configuration_backup \
and self.dnf5_automatic_download_updates_identifier_text in image_default_patch_configuration_backup[self.dnf5_auto_os_update_service] \
and self.dnf5_automatic_apply_updates_identifier_text in image_default_patch_configuration_backup[self.dnf5_auto_os_update_service] \
and self.dnf5_automatic_enable_on_reboot_identifier_text in image_default_patch_configuration_backup[self.dnf5_auto_os_update_service] \
and self.dnf5_automatic_installation_state_identifier_text in image_default_patch_configuration_backup[self.dnf5_auto_os_update_service]:
self.composite_logger.log_debug("[DNF5] Extension has a valid backup for default dnf5-automatic configuration settings")
return True
else:
self.composite_logger.log_debug("[DNF5] Extension does not have a valid backup for default dnf5-automatic configuration settings")
default_backup_valid = self.__is_backup_valid(image_default_patch_configuration_backup, self.os_patch_default_configuration_backup_key)
override_backup_valid = self.__is_backup_valid(image_default_patch_configuration_backup, self.os_patch_override_configuration_backup_key)

if default_backup_valid and override_backup_valid:
Comment on lines +734 to +743
self.composite_logger.log_debug("[DNF5] Removing override configuration file to restore machine default.[Path={0}]".format(self.os_patch_override_configuration_settings_file_path))
code, out = self.env_layer.run_command_output(self.dnf5_automatic_remove_override_configuration_file_cmd, False, False)

if code != 0:
error_msg = "[DNF5] Error removing override configuration file. [Command={0}][Code={1}][Output={2}]".format(self.dnf5_automatic_remove_override_configuration_file_cmd, str(code), out)
self.composite_logger.log_error(error_msg)
self.status_handler.add_error_to_status(error_msg, Constants.PatchOperationErrorCodes.OPERATION_FAILED)
raise Exception(error_msg, "[{0}]".format(Constants.ERROR_ADDED_TO_STATUS))

self.composite_logger.log_debug("[DNF5] Removed override configuration file. [Command={0}][Code={1}][Output={2}]".format(self.dnf5_automatic_remove_override_configuration_file_cmd, str(code), out))
Copilot AI review requested due to automatic review settings July 24, 2026 18:27
@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.53695% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.17%. Comparing base (fc7f336) to head (19fb740).

Files with missing lines Patch % Lines
...rc/core/src/package_managers/Dnf5PackageManager.py 95.57% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #368      +/-   ##
==========================================
+ Coverage   94.10%   94.17%   +0.07%     
==========================================
  Files         109      109              
  Lines       20642    20770     +128     
==========================================
+ Hits        19425    19560     +135     
+ Misses       1217     1210       -7     
Flag Coverage Δ
python27 94.17% <97.53%> (+0.07%) ⬆️
python312 94.17% <97.53%> (+0.07%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (2)

src/core/src/package_managers/Dnf5PackageManager.py:642

  • The body of is_backup_valid_for_dnf5_automatic is over-indented, which will raise an IndentationError (or change the block structure) at runtime and break the package manager module import.
    def is_backup_valid_for_dnf5_automatic(self, image_default_patch_configuration_backup):
            default_backup_valid = self.__is_backup_valid(image_default_patch_configuration_backup, self.os_patch_default_configuration_backup_key)
            override_backup_valid = self.__is_backup_valid(image_default_patch_configuration_backup, self.os_patch_override_configuration_backup_key)

            if default_backup_valid and override_backup_valid:

src/core/tests/Test_Dnf5PackageManager.py:487

  • Leftover debug print in the test will add noise to test output and can cause brittle assertions when stdout is captured.
            print("packagemanager" ,dir(package_manager))


def __setup_current_auto_os_update_config(self, package_manager, config_value='',
config_file_name="automatic.conf"):
def __setup_current_auto_os_update_config(self, package_manager, config_value='', config_file_name=""):
Comment on lines +725 to +727
def __remove_override_configuration_if_exists(self):
"""Removes dnf5-automatic override configuration file if it exists.Missing override file is valid by design, so this method must not throw
when the file is absent."""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants