Skip to content

Commit 26085f4

Browse files
committed
fix(dnfcache): switch module filter from allowlist to denylist
Rocky/RHEL 8.10 ships the perl:5.26 default-stream modulemd document with an empty artifacts list, while the actual default-stream .module+ RPMs (perl-interpreter, perl-libs, ...) are still present in primary.xml. The allowlist approach over-filtered and dropped them entirely — dnf install perl-interpreter returned unresolved. Switch the filter semantics: instead of allowing only NVRAs in the default stream's artifacts list, block NVRAs that appear in *non-default* streams' artifacts. Default-stream RPMs (whether listed or not) and unknown modular RPMs pass through, matching dnf install behavior without explicit dnf module enable.
1 parent 3cb4d90 commit 26085f4

3 files changed

Lines changed: 43 additions & 24 deletions

File tree

pkg/dnfcache/dnfcache.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -277,13 +277,9 @@ func (c *Cache) isBlockedByModuleFilter(p *PackageInfo) bool {
277277
return false
278278
}
279279

280-
if _, hasDefault := c.modules.defaultStream[p.Name]; !hasDefault {
281-
return false
282-
}
283-
284280
nvra := packageNVRA(p.Name, p.Epoch, p.Version, p.Release, p.Arch)
285281

286-
return !c.modules.allowedNVRA[nvra]
282+
return c.modules.blockedNVRA[nvra]
287283
}
288284

289285
// pickProvider chooses the best concrete provider from a list, preferring

pkg/dnfcache/modules.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,21 @@ import (
3636
// moduleIndex holds the parsed module-defaults and the allowed NVRAs.
3737
// Empty index = no filtering (non-modular repos).
3838
type moduleIndex struct {
39-
defaultStream map[string]string // module -> stream
40-
allowedNVRA map[string]bool // NVRA strings from default streams
39+
defaultStream map[string]string // module -> default stream
40+
// blockedNVRA: NVRAs that belong to a *non-default* stream of a module
41+
// that has a default. These packages are filtered out (matching `dnf
42+
// install` without explicit `dnf module enable`). NVRAs absent from
43+
// this set are allowed, including default-stream NVRAs and modular
44+
// packages whose default stream lists no artifacts (e.g. Rocky 8.10
45+
// perl:5.26 where the default-stream doc has an empty artifacts list
46+
// but the .module+el8 RPMs still ship in AppStream repodata).
47+
blockedNVRA map[string]bool
4148
}
4249

4350
func newModuleIndex() *moduleIndex {
4451
return &moduleIndex{
4552
defaultStream: make(map[string]string),
46-
allowedNVRA: make(map[string]bool),
53+
blockedNVRA: make(map[string]bool),
4754
}
4855
}
4956

@@ -115,12 +122,28 @@ func parseModulesYAML(r io.Reader, idx *moduleIndex) {
115122
}
116123
}
117124

118-
// Pass 2: keep only rpms from the default stream of each module.
119-
for module, stream := range idx.defaultStream {
120-
key := module + ":" + stream
125+
buildBlockedNVRA(idx, streamRPMs)
126+
}
121127

122-
for _, nvra := range streamRPMs[key] {
123-
idx.allowedNVRA[nvra] = true
128+
// buildBlockedNVRA fills idx.blockedNVRA with NVRAs from NON-default streams
129+
// of each module. Denylist (not allowlist) because some Rocky/RHEL 8 minor
130+
// releases (e.g. 8.10) ship the default-stream modulemd document with an
131+
// empty artifacts list while the default-stream `.module+` RPMs still live
132+
// in primary.xml — an allowlist would over-filter and drop them entirely
133+
// (e.g. perl-interpreter on Rocky 8.10).
134+
func buildBlockedNVRA(idx *moduleIndex, streamRPMs map[string][]string) {
135+
for module, defaultStream := range idx.defaultStream {
136+
prefix := module + ":"
137+
defaultKey := prefix + defaultStream
138+
139+
for key, rpms := range streamRPMs {
140+
if !strings.HasPrefix(key, prefix) || key == defaultKey {
141+
continue
142+
}
143+
144+
for _, nvra := range rpms {
145+
idx.blockedNVRA[nvra] = true
146+
}
124147
}
125148
}
126149
}
@@ -245,7 +268,7 @@ func loadModuleIndex() *moduleIndex {
245268
logger.Info("dnfcache: module index loaded",
246269
"files", len(files),
247270
"defaults", len(idx.defaultStream),
248-
"allowed_nvra", len(idx.allowedNVRA))
271+
"blocked_nvra", len(idx.blockedNVRA))
249272
}
250273

251274
return idx

pkg/dnfcache/modules_test.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,12 @@ data:
9595
t.Errorf("default stream perl=%q want 5.26", got)
9696
}
9797

98-
if !idx.allowedNVRA["perl-0:5.26.3-419.module+el8.5.0+728+2c8a1bd2.x86_64"] {
99-
t.Errorf("expected perl-5.26 NVRA in allowed set")
98+
if idx.blockedNVRA["perl-0:5.26.3-419.module+el8.5.0+728+2c8a1bd2.x86_64"] {
99+
t.Errorf("perl-5.26 (default stream) NVRA must NOT be blocked")
100100
}
101101

102-
if idx.allowedNVRA["perl-0:5.24.4-404.module+el8.6.0+882+2fa1e48f.x86_64"] {
103-
t.Errorf("perl-5.24 NVRA must NOT be in allowed set")
102+
if !idx.blockedNVRA["perl-0:5.24.4-404.module+el8.6.0+882+2fa1e48f.x86_64"] {
103+
t.Errorf("expected perl-5.24 (non-default stream) NVRA to be blocked")
104104
}
105105
}
106106

@@ -126,9 +126,9 @@ func TestParseModulesFileRocky8(t *testing.T) {
126126
}
127127

128128
// The 5.24 perl NVRA from the failing build must NOT be allowed.
129-
bad := "perl-0:5.24.4-404.module+el8.6.0+882+2fa1e48f.x86_64"
130-
if idx.allowedNVRA[bad] {
131-
t.Errorf("perl-5.24 NVRA must NOT be allowed: %s", bad)
129+
bad := "perl-4:5.24.4-404.module+el8.6.0+882+2fa1e48f.x86_64"
130+
if !idx.blockedNVRA[bad] {
131+
t.Errorf("perl-5.24 NVRA must be blocked: %s", bad)
132132
}
133133

134134
// All non-default perl-libs NVRAs (5.24, 5.30, 5.32) must be blocked.
@@ -140,10 +140,10 @@ func TestParseModulesFileRocky8(t *testing.T) {
140140
"perl-libs-4:5.30.1-452.module+el8.6.0+878+f93dfff7.x86_64",
141141
"perl-libs-4:5.32.1-473.module+el8.10.0+1616+0d20cc68.x86_64",
142142
} {
143-
if idx.allowedNVRA[bad] {
144-
t.Errorf("non-default perl-libs NVRA must NOT be allowed: %s", bad)
143+
if !idx.blockedNVRA[bad] {
144+
t.Errorf("non-default perl-libs NVRA must be blocked: %s", bad)
145145
}
146146
}
147147

148-
t.Logf("defaults=%d allowed_nvra=%d", len(idx.defaultStream), len(idx.allowedNVRA))
148+
t.Logf("defaults=%d blocked_nvra=%d", len(idx.defaultStream), len(idx.blockedNVRA))
149149
}

0 commit comments

Comments
 (0)