Skip to content

Preserve multiarch apt package names in pkg.installed#68933

Open
TobiPeterG wants to merge 1 commit intosaltstack:3006.xfrom
TobiPeterG:fix-68932
Open

Preserve multiarch apt package names in pkg.installed#68933
TobiPeterG wants to merge 1 commit intosaltstack:3006.xfrom
TobiPeterG:fix-68932

Conversation

@TobiPeterG
Copy link
Copy Markdown

@TobiPeterG TobiPeterG commented Apr 13, 2026

What does this PR do?

Fixes aptpkg.install() so explicit APT multiarch package names such as :amd64 are preserved when split_arch=False is passed from the generic pkg.installed state path.

This makes the APT behavior similar to the existing YUM handling and fixes a case where pkg.installed(update_holds=True) could fail to temporarily unhold packages that dpkg records as held with an architecture suffix.

This PR also adds documentation notes for the provider-specific naming requirement around held/unheld multiarch APT packages.

What issues does this PR fix or reference?

Fixes #68932

Previous Behavior

For APT, pkg.installed passes split_arch=False into pkg.install, but aptpkg.install() did not honor that when calling pkg_resource.parse_targets().

As a result, an explicit package target like:

- libnvidia-cfg1-570-server:amd64

could be normalized to:

libnvidia-cfg1-570-server

before APT hold handling ran.

If dpkg recorded the hold as:

libnvidia-cfg1-570-server:amd64

then update_holds: True could fail to match and unhold the package correctly.

New Behavior

aptpkg.install() now honors split_arch=False when parsing install targets, matching the existing YUM pattern.

This preserves explicit APT multiarch package names such as :amd64 in the install path, allowing update_holds: True to operate on the exact held package name reported by dpkg.

Merge requirements satisfied?

  • Docs
  • Changelog
  • Tests written/updated

Tests updated:

  • tests/pytests/unit/modules/test_aptpkg.py

Validation performed:

  • Built docs locally and verified the new doc text renders correctly
  • Added a unit test for the APT split_arch=False path
  • Ran local validation on the changed Python files

Commits signed with GPG?

No

Is signing the commits with GPG required?

I tried to test my changes locally on a Ubuntu system to confirm that the issue is properly resolved. However, I saw this message when I tried to install salt in editable mode:
ERROR: Project file:///home/user/Documents/salt uses a build backend that is missing the 'build_editable' hook, so it cannot be installed in editable mode. Consider using a build backend that supports PEP 660.
Did I do something wrong?

This means that I don't have a real world proof that this code actually fixes the issue. However, I hope that A) the issue is easily reproducable and B) this code can be easily checked by someone with a working test setup.
Until this PR is proven to solve the linked issue, I will keep it in draft mode.
EDIT: I will change it to ready and add WIP as there are very few PRs in draft mode, but quite a few with the WIP tag. I hope this change is correct.

I'm also not too experienced in python and not very familiar with the code base, so I hope this is an acceptable solution. :)
I'm of course open for feedback. :)

PS: I hope I didn't miss a point in the contribution guidelines :D

@welcome
Copy link
Copy Markdown

welcome bot commented Apr 13, 2026

Hi there! Welcome to the Salt Community! Thank you for making your first contribution. We have a lengthy process for issues and PRs. Someone from the Core Team will follow up as soon as possible. In the meantime, here's some information that may help as you continue your Salt journey.
Please be sure to review our Code of Conduct. Also, check out some of our community resources including:

There are lots of ways to get involved in our community. Every month, there are around a dozen opportunities to meet with other contributors and the Salt Core team and collaborate in real time. The best way to keep track is by subscribing to the Salt Community Events Calendar.
If you have additional questions, email us at saltproject.pdl@broadcom.com. We're glad you've joined our community and look forward to doing awesome things with you!

@TobiPeterG TobiPeterG changed the base branch from master to 3006.x April 13, 2026 14:19
@TobiPeterG TobiPeterG changed the title Honor split_arch in aptpkg install target parsing [WIP] Honor split_arch in aptpkg install target parsing Apr 13, 2026
@TobiPeterG TobiPeterG marked this pull request as ready for review April 13, 2026 14:25
@TobiPeterG TobiPeterG requested a review from a team as a code owner April 13, 2026 14:25
Comment thread salt/modules/aptpkg.py
@TobiPeterG
Copy link
Copy Markdown
Author

TobiPeterG commented Apr 15, 2026

Good news: I found a way to test this.
Bad news: This doesn't fix the issue, but now I can actually test this myself.

Update: Running
sudo $(pyenv which salt-call) -c local/etc/salt pkg.install pkgs='[{"zlib1g:amd64": "1:1.3.dfsg-3.1ubuntu2.1"}]' update_holds=True split_arch=False -l info
already works with the zlib1g package being held at a lower version. So it partially works already :)
Just the "complete" call via pkg.installed doesn't work yet

Update 2; Got it to upgrade and rehold the package as expected, but salt reports that the installation still fails. It looks like that's because apt reports the final name without the architecture suffix. I'm looking into this.

Update 3: I think I actually fixed this now:

local:
----------
          ID: test_bundle
    Function: pkg.installed
        Name: zlib1g:amd64
      Result: True
     Comment: 1 targeted package was installed/updated.
     Started: 13:13:54.954516
    Duration: 18481.389 ms
     Changes:   
              ----------
              zlib1g:
                  ----------
                  new:
                      1:1.3.dfsg-3.1ubuntu2.1
                  old:
                      1:1.3.dfsg-3.1ubuntu2
              zlib1g-dev:
                  ----------
                  new:
                      1:1.3.dfsg-3.1ubuntu2.1
                  old:
                      1:1.3.dfsg-3.1ubuntu2
----------
          ID: test_hold
    Function: pkg.held
      Result: True
     Comment: No changes made
     Started: 13:14:13.441287
    Duration: 619.098 ms
     Changes:   

Summary for local
------------
Succeeded: 2 (changed=1)
Failed:    0
------------
Total states run:     2
Total run time:  19.100 s

Honor split_arch in aptpkg target parsing, keep arch-qualified package
names through the pkg.installed preflight path, and normalize installed
package lookups during final verification so held multiarch packages can
be updated correctly with update_holds=True.
Add regression tests and document the provider-specific naming caveat
for held/unheld packages.
@TobiPeterG
Copy link
Copy Markdown
Author

Alright, I pushed my changes now. I tested it manually, I see:

local:
----------
          ID: test_bundle
    Function: pkg.installed
      Result: True
     Comment: 2 targeted packages were installed/updated.
     Started: 13:39:49.331478
    Duration: 10179.514 ms
     Changes:   
              ----------
              nano:
                  ----------
                  new:
                      7.2-2ubuntu0.1
                  old:
                      7.2-2build1
              zlib1g:
                  ----------
                  new:
                      1:1.3.dfsg-3.1ubuntu2.1
                  old:
                      1:1.3.dfsg-3.1ubuntu2
              zlib1g-dev:
                  ----------
                  new:
                      1:1.3.dfsg-3.1ubuntu2.1
                  old:
                      1:1.3.dfsg-3.1ubuntu2
----------
          ID: test_hold
    Function: pkg.held
      Result: True
     Comment: No changes made
     Started: 13:39:59.518718
    Duration: 586.037 ms
     Changes:   

Summary for local
------------
Succeeded: 2 (changed=1)
Failed:    0
------------
Total states run:     2
Total run time:  10.766 s

on Ubuntu 24.04 when applying this state:

test_bundle:
  pkg.installed:
    - pkgs:
      - zlib1g:amd64=1:1.3.dfsg-3.1ubuntu2.1
      - nano=7.2-2ubuntu0.1
    - update_holds: True

test_hold:
  pkg.held:
    - pkgs:
      - zlib1g:amd64
    - require:
      - pkg: test_bundle

With both packages held to lower versions.

Please note that this PR also changes the behaviour of YUM a bit and how its name matching works, but this should not affect how it is used in any way. I hope this is fine. :)

@TobiPeterG TobiPeterG changed the title [WIP] Honor split_arch in aptpkg install target parsing Preserve multiarch apt package names in pkg.installed Apr 15, 2026
@TobiPeterG TobiPeterG requested a review from bdrx312 April 15, 2026 11:52
Comment thread salt/states/pkg.py
Comment on lines -907 to -910
elif __grains__["os_family"] == "Debian":
cver = new_pkgs.get(pkgname.split("=")[0])
else:
cver = new_pkgs.get(pkgname)
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.

Why were these 2 conditions combined?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I combined them because both branches were doing the same installed-package lookup pattern, and the new normalization fallback needed to apply to both. I kept the Debian-specific split("=")[0] behavior via the lookup_name variable, but merged the rest to avoid duplicating the fallback logic.
I can also split them again, then it would look like this:

        elif __grains__["os_family"] == "Debian":
            lookup_name = pkgname.split("=")[0]
            cver = new_pkgs.get(lookup_name)
            if not cver and "pkg.normalize_name" in __salt__:
                normalized_name = __salt__["pkg.normalize_name"](lookup_name)
                if normalized_name != lookup_name:
                    cver = new_pkgs.get(normalized_name)
        else:
            cver = new_pkgs.get(pkgname)
            if not cver and "pkg.normalize_name" in __salt__:
                normalized_name = __salt__["pkg.normalize_name"](pkgname)
                if normalized_name != pkgname:
                    cver = new_pkgs.get(normalized_name)
            if not cver and pkgname in new_caps:
                cver = new_pkgs.get(new_caps.get(pkgname)[0])

So only the initial lookup key differs. Is this the prefered solution? :)

Copy link
Copy Markdown
Contributor

@bdrx312 bdrx312 Apr 16, 2026

Choose a reason for hiding this comment

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

Another option would be:

        else
            if __grains__["os_family"] == "Debian":
                lookup_name = pkgname.split("=")[0]
            else:
                lookup_name = pkgname
            cver = new_pkgs.get(lookup_name)
            if not cver and "pkg.normalize_name" in __salt__:
                normalized_name = __salt__["pkg.normalize_name"](lookup_name)
                if normalized_name != lookup_name:
                    cver = new_pkgs.get(normalized_name)
            if not cver and pkgname in new_caps:
                cver = new_pkgs.get(new_caps.get(pkgname)[0])

but I think it is fine to always split on the equal sign for both like your code is already doing since = is not a valid character in rpm names either. So I think what you have is ideal if both need to do this normalization.

I don't think yum/dnf supports multi architecture packages though, so it isn't clear from the issue/PR description why yum package logic is changing. Maybe you can update the descriptions to elaborate.

@TobiPeterG TobiPeterG requested a review from bdrx312 April 16, 2026 10:10
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.

[Bug]: v3006: updating held arch-dependent packages doesn't work with apt with update_holds: True flag

2 participants