Skip to content
This repository was archived by the owner on Aug 7, 2025. It is now read-only.

Commit 6c4d085

Browse files
author
William Douglas
committed
Add support for package bundles
Add package level bundles "pundles" first class support in mixer. This involves tagging pundles with "pundle" in the bundle definition file's "MAINTAINER" field. This support requires changes to bundle constraints, primarily that cycles are now allowed in bundle includes. As part of this, directory subtraction will no longer be done for includes as the true directory owner isn't something that can be decided on in a cycle. Validation also needed a slight adjustment for pundles to reflect their minimal bundle definition files. Signed-off-by: William Douglas <william.douglas@intel.com>
1 parent 40aa54c commit 6c4d085

File tree

5 files changed

+133
-23
lines changed

5 files changed

+133
-23
lines changed

builder/bundles.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,24 @@ func resolvePackagesWithOptions(numWorkers int, set bundleSet, packagerCmd []str
404404
return
405405
}
406406

407+
if bundle.Header.Maintainer == "pundle" {
408+
singlePkgMap := make(repoPkgMap)
409+
for _, pkgs := range rpm {
410+
for _, pkg := range pkgs {
411+
if pkg.name == bundle.Name {
412+
singlePkgMap[pkg.repo] = append(singlePkgMap[pkg.repo], pkg)
413+
break
414+
}
415+
}
416+
if len(singlePkgMap) > 0 {
417+
break
418+
}
419+
}
420+
if len(singlePkgMap) > 0 {
421+
rpm = singlePkgMap
422+
}
423+
}
424+
407425
for _, pkgs := range rpm {
408426
// Add packages to bundle's AllPackages
409427
for _, pkg := range pkgs {

builder/bundleset.go

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,10 @@ type bundleSet map[string]*bundle
4444
// the following constraints:
4545
// 1. Completeness. For each bundle in the set, every bundle included by that
4646
// bundle is also in the set.
47-
// 2. Cycle-Free. The set contains no bundle include cycles.
4847
func validateAndFillBundleSet(bundles bundleSet) error {
4948
// Sort the bundles so that all includes and optional (also-add) includes appear
5049
// before a bundle, then calculate AllPackages for each bundle.
51-
// Cycles and missing bundles are identified as part of sorting the bundles.
50+
// Missing bundles are identified as part of sorting the bundles.
5251
sortedBundles, err := sortBundles(bundles)
5352
if err != nil {
5453
return err
@@ -58,9 +57,13 @@ func validateAndFillBundleSet(bundles bundleSet) error {
5857
for k, v := range b.DirectPackages {
5958
b.AllPackages[k] = v
6059
}
61-
for _, include := range b.DirectIncludes {
62-
for k, v := range bundles[include].AllPackages {
63-
b.AllPackages[k] = v
60+
}
61+
for _, b := range sortedBundles {
62+
if b.Header.Maintainer != "pundle" {
63+
for _, include := range b.DirectIncludes {
64+
for k, v := range bundles[include].AllPackages {
65+
b.AllPackages[k] = v
66+
}
6467
}
6568
}
6669
}
@@ -90,7 +93,8 @@ func sortBundles(bundles bundleSet) ([]*bundle, error) {
9093
visit = func(b *bundle) error {
9194
switch mark[b] {
9295
case Visiting:
93-
return fmt.Errorf("cycle found in bundles: %s -> %s", strings.Join(visiting, " -> "), b.Name)
96+
// In a cycle, short circuit
97+
return nil
9498
case NotVisited:
9599
mark[b] = Visiting
96100
visiting = append(visiting, b.Name)
@@ -161,14 +165,21 @@ func validateBundleFile(filename string, lvl ValidationLevel) error {
161165
}
162166

163167
// Strict Validation
164-
err = validateBundle(b)
165-
if err != nil {
166-
errText += err.Error() + "\n"
167-
}
168+
if b.Header.Maintainer == "pundle" {
169+
err = validatePundle(b)
170+
if err != nil {
171+
errText += err.Error() + "\n"
172+
}
173+
} else {
174+
err = validateBundle(b)
175+
if err != nil {
176+
errText += err.Error() + "\n"
177+
}
168178

169-
name := filepath.Base(filename)
170-
if name != b.Header.Title {
171-
errText += fmt.Sprintf("Bundle name %q and bundle header Title %q do not match\n", name, b.Header.Title)
179+
name := filepath.Base(filename)
180+
if name != b.Header.Title {
181+
errText += fmt.Sprintf("Bundle name %q and bundle header Title %q do not match\n", name, b.Header.Title)
182+
}
172183
}
173184

174185
if errText != "" {
@@ -195,6 +206,31 @@ func validateBundleFilename(filename string) error {
195206
return nil
196207
}
197208

209+
func validatePundle(b *bundle) error {
210+
var errText string
211+
212+
if b.Header.Title != "" {
213+
errText = fmt.Sprintf("pundle name %q detected in pundle header Title (should be empty)\n", b.Header.Title)
214+
}
215+
if b.Header.Description != "" {
216+
errText += "Non-empty Description in pundle header\n"
217+
}
218+
if b.Header.Maintainer != "pundle" {
219+
errText += "Non-pundle Maintainer in bundle header\n"
220+
}
221+
if b.Header.Status == "" {
222+
errText += "Non-empty Status in bundle header\n"
223+
}
224+
if b.Header.Capabilities == "" {
225+
errText += "Non-empty Capabilities in bundle header\n"
226+
}
227+
if errText != "" {
228+
return errors.New(strings.TrimSuffix(errText, "\n"))
229+
}
230+
231+
return nil
232+
}
233+
198234
func validateBundle(b *bundle) error {
199235
var errText string
200236

builder/bundleset_test.go

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -534,11 +534,57 @@ func TestParseBundleSet(t *testing.T) {
534534
},
535535
},
536536

537-
{"cyclic error two bundles",
538-
FilesMap{"a": "include(b)", "b": "include(a)"}, Error},
537+
{
538+
"cycles are fine, two bundles",
539+
FilesMap{
540+
"a": Lines("include(b) A"),
541+
"b": Lines("include(a) B"),
542+
},
543+
CountsMap{
544+
"a": 2,
545+
"b": 2,
546+
},
547+
},
548+
549+
{
550+
"cycles are fine, two pundles",
551+
FilesMap{
552+
"a": "# [MAINTAINER]: pundle\ninclude(b)\nA",
553+
"b": "# [MAINTAINER]: pundle\ninclude(a)\nB",
554+
},
555+
CountsMap{
556+
"a": 1,
557+
"b": 1,
558+
},
559+
},
539560

540-
{"cyclic error three bundles",
541-
FilesMap{"a": "include(b)", "b": "include(c)", "c": "include(a)"}, Error},
561+
{
562+
"cycles are fine, three bundles",
563+
FilesMap{
564+
"a": Lines("include(c) include(b) A"),
565+
"b": Lines("include(c) include(a) B"),
566+
"c": Lines("include(b) include(a) C"),
567+
},
568+
CountsMap{
569+
"a": 3,
570+
"b": 3,
571+
"c": 3,
572+
},
573+
},
574+
575+
{
576+
"cycles are fine, three pundles",
577+
FilesMap{
578+
"a": "# [MAINTAINER]: pundle\ninclude(c)\ninclude(b)\nA",
579+
"b": "# [MAINTAINER]: pundle\ninclude(c)\ninclude(a)\nB",
580+
"c": "# [MAINTAINER]: pundle\ninclude(b)\ninclude(a)\nC",
581+
},
582+
CountsMap{
583+
"a": 1,
584+
"b": 1,
585+
"c": 1,
586+
},
587+
},
542588

543589
{"bundle not available",
544590
FilesMap{"a": "include(c)"}, Error},

swupd/manifest.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -759,9 +759,19 @@ func (m *Manifest) addManifestFiles(ui UpdateInfo, c config) error {
759759
for f := range m.BundleInfo.Files {
760760
isIncluded := false
761761
for _, inc := range includes {
762-
if _, ok := inc.BundleInfo.Files[f]; ok {
763-
isIncluded = true
764-
break
762+
// Handle cycles
763+
if inc.Name == m.Name {
764+
continue
765+
}
766+
chrootDir := filepath.Join(c.imageBase, fmt.Sprint(ui.version), "full")
767+
fullPath := filepath.Join(chrootDir, f)
768+
if fi, err := os.Lstat(fullPath); err == nil {
769+
if !fi.IsDir() {
770+
if _, ok := inc.BundleInfo.Files[f]; ok {
771+
isIncluded = true
772+
break
773+
}
774+
}
765775
}
766776
}
767777
if !isIncluded {

swupd/swupd_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func TestFullRun(t *testing.T) {
9999
mustValidateZeroPack(t, ts.path("www/10/Manifest.test-bundle"), ts.path("www/10/pack-test-bundle-from-0.tar"))
100100
mustHaveDeltaCount(t, infoTestBundle, 0)
101101
// Empty file (bundle file), "foo".
102-
mustHaveFullfileCount(t, infoTestBundle, 2)
102+
mustHaveFullfileCount(t, infoTestBundle, 3)
103103

104104
testBundle := ts.parseManifest(10, "test-bundle")
105105
checkIncludes(t, testBundle, "os-core")
@@ -137,7 +137,7 @@ func TestFullRunDelta(t *testing.T) {
137137

138138
ts.createPack("os-core", 0, 10, ts.path("image"))
139139
info := ts.createPack("test-bundle", 0, 10, ts.path("image"))
140-
mustHaveFullfileCount(t, info, 4) // largefile, foo and foobarbaz and the test-bundle file.
140+
mustHaveFullfileCount(t, info, 5) // largefile, foo and foobarbaz and the test-bundle file.
141141

142142
mustValidateZeroPack(t, ts.path("www/10/Manifest.os-core"), ts.path("www/10/pack-os-core-from-0.tar"))
143143
mustValidateZeroPack(t, ts.path("www/10/Manifest.test-bundle"), ts.path("www/10/pack-test-bundle-from-0.tar"))
@@ -164,7 +164,7 @@ func TestFullRunDelta(t *testing.T) {
164164

165165
ts.createPack("os-core", 0, 20, ts.path("image"))
166166
info = ts.createPack("test-bundle", 0, 20, ts.path("image"))
167-
mustHaveFullfileCount(t, info, 2) // largefile and the test-bundle file.
167+
mustHaveFullfileCount(t, info, 3) // largefile and the test-bundle file.
168168

169169
mustValidateZeroPack(t, ts.path("www/20/Manifest.os-core"), ts.path("www/20/pack-os-core-from-0.tar"))
170170
mustValidateZeroPack(t, ts.path("www/20/Manifest.test-bundle"), ts.path("www/20/pack-test-bundle-from-0.tar"))

0 commit comments

Comments
 (0)