Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Fix breaking changes in package install #5069

Merged
merged 5 commits into from
Mar 19, 2024

Conversation

TheRealFalcon
Copy link
Member

Proposed Commit Message

fix: Fix breaking changes in package install

Ensure:
* Apt can still install packages that contain `-`, `/`, or `=`
* If a specified package manager fails we don't retry as a generic
  package
* We do not attempt to invoke a package manager that doesn't exist
* Packages that are unable to be installed are tracked and
  reported appropriately

Fixes GH-5066

Additional Context

#5066

Test Steps

Added additional unit tests

Merge type

  • Squash merge using "Proposed Commit Message"
  • Rebase and merge unique commits. Requires commit messages per-commit each referencing the pull request number (#<PR_NUM>)

@@ -120,7 +120,9 @@ def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None:
try:
cloud.distro.install_packages(pkglist)
except Exception as e:
util.logexc(LOG, "Failed to install packages: %s", pkglist)
util.logexc(
LOG, "Failure when attempting to install packages: %s", pkglist
Copy link
Member Author

Choose a reason for hiding this comment

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

Not strictly needed, but I made this slightly more passive since the old phrasing sounds like ALL packages failed, whereas that may not always be the case.

Ensure:
* Apt can still install packages that contain `-`, `/`, or `=`
* If a specified package manager fails we don't retry as a generic
  package
* We do not attempt to invoke a package manager that doesn't exist
* Packages that are unable to be installed are tracked and
  reported appropriately

Fixes canonicalGH-5066
return [
pkg
for pkg in pkglist
if re.split("/|=|-", pkg)[0] not in self.get_all_packages()
Copy link
Collaborator

Choose a reason for hiding this comment

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

The issue you'll have with spitting on hyphen is that many packages contain hyphens, like our own cloud-init :) which will lead to us trying to match cloud instead of cloud-init. It'll have to specifically be an rstrip for the hyphen case... or making sure we only truncate that last character if it's a hyphen.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm +1 on splitting on the / or = as they can't be part of a valid debian package name. But again, the hyphen we need to treat differently once we have that split.
So maybe the following?

Suggested change
if re.split("/|=|-", pkg)[0] not in self.get_all_packages()
if re.split("/|=", pkg)[0].rstrip("-") not in self.get_all_packages()

failed = {
pkg for pkg in uninstalled if pkg not in generic_packages
failed = manager.install_packages(to_try)
uninstalled.update(failed)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't this be the following?

Suggested change
uninstalled.update(failed)
uninstalled = to_try.difference(failed)

Copy link
Member Author

@TheRealFalcon TheRealFalcon Mar 19, 2024

Choose a reason for hiding this comment

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

I changed the wording from uninstalled to total_failed to hopefully be a little more clear.

Shouldn't this be the following?

No, doing this won't keep track of failures from previous package managers.

Say we have

packages:
- pkg1
- apt:
  - pkg2
- snap:
  - pkg3

If pkg2 failed, it will stay failed regardless of happens during snap install, so overwriting total_failed each time in the loop won't work, because to_try won't contain pkg2 when doing the snap install. On the other hand, if pkg1 fails during apt install, we will still attempt it during snap install, so what happened during apt install should get replaced by what happened during snap. The logic is a bit convoluted, so I'm open to any ideas for simplification.

This is the reason for the new test_failed test in distros/test_init.py

Copy link
Collaborator

@blackboxsw blackboxsw left a comment

Choose a reason for hiding this comment

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

A couple of comments:

  • question on calculating remaining uninstalled after failure
  • avoiding splitting on all hyphens in pkg names

Copy link
Collaborator

@blackboxsw blackboxsw left a comment

Choose a reason for hiding this comment

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

minor nits and +1

cloudinit/distros/__init__.py Show resolved Hide resolved
}
if failed:
if unexpected_failed:
LOG.info(error_message, failed)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we be logging the unexpected_failed here instead of failed now?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmmm, originally we were logging (at info) all failed packages. I think I want to keep it that way (still requiring a change here) so it shows something didn't install here but may be installed in the future. Let me know if you disagree.

Copy link
Collaborator

Choose a reason for hiding this comment

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

WFM.

TheRealFalcon and others added 2 commits March 19, 2024 15:30
Co-authored-by: Chad Smith <chad.smith@canonical.com>
@TheRealFalcon TheRealFalcon merged commit cbe5f3a into canonical:main Mar 19, 2024
29 checks passed
@TheRealFalcon TheRealFalcon deleted the fix-apt branch March 19, 2024 20:56
TheRealFalcon added a commit to TheRealFalcon/cloud-init that referenced this pull request Mar 19, 2024
fix: Fix breaking changes in package install

Ensure:
* Apt can still install packages that contain `-`, `/`, or `=`
* If a specified package manager fails we don't retry as a generic
  package
* We do not attempt to invoke a package manager that doesn't exist
* Packages that are unable to be installed are tracked and
  reported appropriately

Fixes canonicalGH-5066
@ccievoiceoks
Copy link

Hi ,
I think that I'm hitting the same issue with the caret character as well .
As I need to install a very light version of Xfce . I'm trying to install the following packages :

  • xubuntu-coreˆ

but then the packages failed to install .

It was working previously with older version of cloud-init that I made in February or early March .

So I can easily reproduce it with the following versions :

I got as well the same kind of error

2024-03-21 22:26:23,033 - helpers.py[DEBUG]: Running update-sources using lock (<FileLock using file '/var/lib/cloud/instances/564dec94-2f8f-4ae4-d0b5-cfa9d9099e98/
sem/update_sources'>)
2024-03-21 22:26:23,033 - apt.py[DEBUG]: Waiting for APT lock
2024-03-21 22:26:23,034 - apt.py[DEBUG]: APT lock available
2024-03-21 22:26:23,034 - subp.py[DEBUG]: Running command ['eatmydata', 'apt-get', '--option=Dpkg::Options::=--force-confold', '--option=Dpkg::options::=--force-uns
afe-io', '--assume-yes', '--quiet', 'update'] with allowed return codes [0] (shell=False, capture=False)
2024-03-21 22:26:28,097 - util.py[DEBUG]: apt-update [eatmydata apt-get --option=Dpkg::Options::=--force-confold --option=Dpkg::options::=--force-unsafe-io --assume
-yes --quiet update] took 5.063 seconds
2024-03-21 22:26:28,097 - helpers.py[DEBUG]: update-sources already ran (freq=once-per-instance)
2024-03-21 22:26:28,097 - subp.py[DEBUG]: Running command ['apt-cache', 'pkgnames'] with allowed return codes [0] (shell=False, capture=True)
2024-03-21 22:26:28,176 - apt.py[DEBUG]: The following packages were not found by APT so APT will not attempt to install them: ['xubuntu-coreˆ']
2024-03-21 22:26:28,176 - apt.py[DEBUG]: Waiting for APT lock
2024-03-21 22:26:28,176 - apt.py[DEBUG]: APT lock available
2024-03-21 22:26:28,176 - subp.py[DEBUG]: Running command ['eatmydata', 'apt-get', '--option=Dpkg::Options::=--force-confold', '--option=Dpkg::options::=--force-uns
afe-io', '--assume-yes', '--quiet', 'install', 'ssl-cert', 'preload', 'debconf-utils', 'open-vm-tools-desktop', 'open-vm-tools'] with allowed return codes [0] (shel
l=False, capture=False)
2024-03-21 22:26:37,827 - util.py[DEBUG]: apt-install [eatmydata apt-get --option=Dpkg::Options::=--force-confold --option=Dpkg::options::=--force-unsafe-io --assum
e-yes --quiet install ssl-cert preload debconf-utils open-vm-tools-desktop open-vm-tools] took 9.650 seconds
2024-03-21 22:26:37,827 - subp.py[DEBUG]: Running command ['snap', 'install', 'xubuntu-coreˆ'] with allowed return codes [0] (shell=False, capture=True)
2024-03-21 22:26:37,838 - snap.py[INFO]: Failed to 'snap install xubuntu-coreˆ'!
2024-03-21 22:26:37,838 - util.py[WARNING]: Failed to install packages: ['debconf-utils', 'preload', 'xubuntu-coreˆ', 'open-vm-tools', 'open-vm-tools-desktop', 'ssl
-cert']
2024-03-21 22:26:37,839 - util.py[DEBUG]: Failed to install packages: ['debconf-utils', 'preload', 'xubuntu-coreˆ', 'open-vm-tools', 'open-vm-tools-desktop', 'ssl-c
ert']
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/cloudinit/config/cc_package_update_upgrade_install.py", line 121, in handle
    cloud.distro.install_packages(pkglist)
  File "/usr/lib/python3/dist-packages/cloudinit/distros/__init__.py", line 260, in install_packages
    raise PackageInstallerError(error_message % uninstalled)
cloudinit.distros.PackageInstallerError: Failed to install the following packages: ['xubuntu-coreˆ']. See associated package manager logs for more details.
2024-03-21 22:26:37,840 - cc_package_update_upgrade_install.py[WARNING]: 1 failed with exceptions, re-raising the last one
2024-03-21 22:26:37,840 - handlers.py[DEBUG]: finish: modules-final/config-package_update_upgrade_install: FAIL: running config-package_update_upgrade_install with
frequency once-per-instance
2024-03-21 22:26:37,840 - util.py[WARNING]: Running module package_update_upgrade_install (<module 'cloudinit.config.cc_package_update_upgrade_install' from '/usr/l
ib/python3/dist-packages/cloudinit/config/cc_package_update_upgrade_install.py'>) failed
2024-03-21 22:26:37,840 - util.py[DEBUG]: Running module package_update_upgrade_install (<module 'cloudinit.config.cc_package_update_upgrade_install' from '/usr/lib
/python3/dist-packages/cloudinit/config/cc_package_update_upgrade_install.py'>) failed
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/cloudinit/config/modules.py", line 255, in _run_modules
    ran, _r = cc.run(
  File "/usr/lib/python3/dist-packages/cloudinit/cloud.py", line 60, in run
    return self._runners.run(name, functor, args, freq, clear_on_fail)
  File "/usr/lib/python3/dist-packages/cloudinit/helpers.py", line 172, in run
    results = functor(**args)
  File "/usr/lib/python3/dist-packages/cloudinit/config/cc_package_update_upgrade_install.py", line 147, in handle
    raise errors[-1]
  File "/usr/lib/python3/dist-packages/cloudinit/config/cc_package_update_upgrade_install.py", line 121, in handle
    cloud.distro.install_packages(pkglist)
  File "/usr/lib/python3/dist-packages/cloudinit/distros/__init__.py", line 260, in install_packages
    raise PackageInstallerError(error_message % uninstalled)
cloudinit.distros.PackageInstallerError: Failed to install the following packages: ['xubuntu-coreˆ']. See associated package manager logs for more details.
2024-03-21 22:26:37,843 - modules.py[DEBUG]: Running module write_files_deferred (<module 'cloudinit.config.cc_write_files_deferred' from '/usr/lib/python3/dist-pac
kages/cloudinit/config/cc_write_files_deferred.py'>) with frequency once-per-instance
2024-03-21 22:26:37,844 - handlers.py[DEBUG]: start: modules-final/config-write_files_deferred: running config-write_files_deferred with frequency once-per-instance
2024-03-21 22:26:37,844 - util.py[DEBUG]: Writing to /var/lib/cloud/instances/564dec94-2f8f-4ae4-d0b5-cfa9d9099e98/sem/config_write_files_deferred - wb: [644] 25 by
tes
``` 

What can we do ?

Many thanks

Olivier

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.

3 participants