diff --git a/cmd/chisel/cmd_cut.go b/cmd/chisel/cmd_cut.go index fd680b6f..7a20c8d9 100644 --- a/cmd/chisel/cmd_cut.go +++ b/cmd/chisel/cmd_cut.go @@ -91,6 +91,7 @@ func (cmd *cmdCut) Execute(args []string) error { Suites: archiveInfo.Suites, Components: archiveInfo.Components, CacheDir: cache.DefaultDir("chisel"), + Priority: archiveInfo.Priority, }) if err != nil { return err diff --git a/internal/archive/archive.go b/internal/archive/archive.go index 63badc8f..bd5f23c6 100644 --- a/internal/archive/archive.go +++ b/internal/archive/archive.go @@ -13,9 +13,17 @@ import ( "github.com/canonical/chisel/internal/deb" ) +type PackageInfo interface { + Name() string + Version() string + Arch() string + SHA256() string +} + type Archive interface { Options() *Options Fetch(pkg string) (io.ReadCloser, error) + Info(pkg string) PackageInfo Exists(pkg string) bool } @@ -26,6 +34,7 @@ type Options struct { Suites []string Components []string CacheDir string + Priority int32 } func Open(options *Options) (Archive, error) { @@ -86,6 +95,22 @@ func (a *ubuntuArchive) Exists(pkg string) bool { return err == nil } +type pkgInfo struct{ control.Section } + +var _ PackageInfo = pkgInfo{} + +func (info pkgInfo) Name() string { return info.Get("Package") } +func (info pkgInfo) Version() string { return info.Get("Version") } +func (info pkgInfo) Arch() string { return info.Get("Architecture") } +func (info pkgInfo) SHA256() string { return info.Get("SHA256") } + +func (a *ubuntuArchive) Info(pkg string) PackageInfo { + if section, _, _ := a.selectPackage(pkg); section != nil { + return &pkgInfo{section} + } + return nil +} + func (a *ubuntuArchive) selectPackage(pkg string) (control.Section, *ubuntuIndex, error) { var selectedVersion string var selectedSection control.Section diff --git a/internal/archive/archive_test.go b/internal/archive/archive_test.go index cb9f3278..b18fc60c 100644 --- a/internal/archive/archive_test.go +++ b/internal/archive/archive_test.go @@ -332,6 +332,39 @@ func (s *httpSuite) TestArchiveLabels(c *C) { c.Assert(err, ErrorMatches, `.*\bno Ubuntu section`) } +func (s *httpSuite) TestPackageInfo(c *C) { + s.prepareArchive("jammy", "22.04", "amd64", []string{"main", "universe"}) + + options := archive.Options{ + Label: "ubuntu", + Version: "22.04", + Arch: "amd64", + Suites: []string{"jammy"}, + Components: []string{"main", "universe"}, + CacheDir: c.MkDir(), + } + + archive, err := archive.Open(&options) + c.Assert(err, IsNil) + + info1 := archive.Info("mypkg1") + c.Assert(info1, NotNil) + c.Assert(info1.Name(), Equals, "mypkg1") + c.Assert(info1.Version(), Equals, "1.1") + c.Assert(info1.Arch(), Equals, "amd64") + c.Assert(info1.SHA256(), Equals, "1f08ef04cfe7a8087ee38a1ea35fa1810246648136c3c42d5a61ad6503d85e05") + + info3 := archive.Info("mypkg3") + c.Assert(info3, NotNil) + c.Assert(info3.Name(), Equals, "mypkg3") + c.Assert(info3.Version(), Equals, "1.3") + c.Assert(info3.Arch(), Equals, "amd64") + c.Assert(info3.SHA256(), Equals, "fe377bf13ba1a5cb287cb4e037e6e7321281c929405ae39a72358ef0f5d179aa") + + info99 := archive.Info("mypkg99") + c.Assert(info99, IsNil) +} + func read(r io.Reader) string { data, err := io.ReadAll(r) if err != nil { diff --git a/internal/setup/setup.go b/internal/setup/setup.go index 451bcc3c..e7c616e9 100644 --- a/internal/setup/setup.go +++ b/internal/setup/setup.go @@ -18,10 +18,9 @@ import ( // Release is a collection of package slices targeting a particular // distribution version. type Release struct { - Path string - Packages map[string]*Package - Archives map[string]*Archive - DefaultArchive string + Path string + Packages map[string]*Package + Archives map[string]*Archive } // Archive is the location from which binary packages are obtained. @@ -30,14 +29,14 @@ type Archive struct { Version string Suites []string Components []string + Priority int32 } // Package holds a collection of slices that represent parts of themselves. type Package struct { - Name string - Path string - Archive string - Slices map[string]*Slice + Name string + Path string + Slices map[string]*Slice } // Slice holds the details about a package slice. @@ -306,9 +305,6 @@ func readSlices(release *Release, baseDir, dirName string) error { if err != nil { return err } - if pkg.Archive == "" { - pkg.Archive = release.DefaultArchive - } release.Packages[pkg.Name] = pkg } @@ -326,7 +322,7 @@ type yamlArchive struct { Version string `yaml:"version"` Suites []string `yaml:"suites"` Components []string `yaml:"components"` - Default bool `yaml:"default"` + Priority int32 `yaml:"priority"` } type yamlPackage struct { @@ -428,19 +424,12 @@ func parseRelease(baseDir, filePath string, data []byte) (*Release, error) { if len(details.Components) == 0 { return nil, fmt.Errorf("%s: archive %q missing components field", fileName, archiveName) } - if len(yamlVar.Archives) == 1 { - details.Default = true - } else if details.Default && release.DefaultArchive != "" { - return nil, fmt.Errorf("%s: more than one default archive: %s, %s", fileName, release.DefaultArchive, archiveName) - } - if details.Default { - release.DefaultArchive = archiveName - } release.Archives[archiveName] = &Archive{ Name: archiveName, Version: details.Version, Suites: details.Suites, Components: details.Components, + Priority: details.Priority, } } @@ -464,7 +453,6 @@ func parsePackage(baseDir, pkgName, pkgPath string, data []byte) (*Package, erro if yamlPkg.Name != pkg.Name { return nil, fmt.Errorf("%s: filename and 'package' field (%q) disagree", pkgPath, yamlPkg.Name) } - pkg.Archive = yamlPkg.Archive zeroPath := yamlPath{} for sliceName, yamlSlice := range yamlPkg.Slices { diff --git a/internal/setup/setup_test.go b/internal/setup/setup_test.go index 7ffae74f..0a954db2 100644 --- a/internal/setup/setup_test.go +++ b/internal/setup/setup_test.go @@ -60,8 +60,6 @@ var setupTests = []setupTest{{ `, }, release: &setup.Release{ - DefaultArchive: "ubuntu", - Archives: map[string]*setup.Archive{ "ubuntu": { Name: "ubuntu", @@ -72,10 +70,9 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Archive: "ubuntu", - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, }, @@ -103,8 +100,6 @@ var setupTests = []setupTest{{ `, }, release: &setup.Release{ - DefaultArchive: "ubuntu", - Archives: map[string]*setup.Archive{ "ubuntu": { Name: "ubuntu", @@ -115,9 +110,8 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Archive: "ubuntu", - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice1": { Package: "mypkg", @@ -164,8 +158,6 @@ var setupTests = []setupTest{{ `, }, release: &setup.Release{ - DefaultArchive: "ubuntu", - Archives: map[string]*setup.Archive{ "ubuntu": { Name: "ubuntu", @@ -176,9 +168,8 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Archive: "ubuntu", - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice1": { Package: "mypkg", @@ -424,8 +415,6 @@ var setupTests = []setupTest{{ `, }, release: &setup.Release{ - DefaultArchive: "ubuntu", - Archives: map[string]*setup.Archive{ "ubuntu": { Name: "ubuntu", @@ -436,9 +425,8 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Archive: "ubuntu", - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice1": { Package: "mypkg", @@ -638,8 +626,6 @@ var setupTests = []setupTest{{ `, }, release: &setup.Release{ - DefaultArchive: "ubuntu", - Archives: map[string]*setup.Archive{ "ubuntu": { Name: "ubuntu", @@ -650,9 +636,8 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Archive: "ubuntu", - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice": { Package: "mypkg", @@ -677,8 +662,6 @@ var setupTests = []setupTest{{ `, }, release: &setup.Release{ - DefaultArchive: "ubuntu", - Archives: map[string]*setup.Archive{ "ubuntu": { Name: "ubuntu", @@ -689,9 +672,8 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Archive: "ubuntu", - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice": { Package: "mypkg", @@ -717,8 +699,6 @@ var setupTests = []setupTest{{ `, }, release: &setup.Release{ - DefaultArchive: "ubuntu", - Archives: map[string]*setup.Archive{ "ubuntu": { Name: "ubuntu", @@ -729,9 +709,8 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Archive: "ubuntu", - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice": { Package: "mypkg", @@ -755,7 +734,6 @@ var setupTests = []setupTest{{ version: 22.04 components: [main, universe] suites: [jammy] - default: true bar: version: 22.04 components: [universe] @@ -766,8 +744,6 @@ var setupTests = []setupTest{{ `, }, release: &setup.Release{ - DefaultArchive: "foo", - Archives: map[string]*setup.Archive{ "foo": { Name: "foo", @@ -784,13 +760,36 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Archive: "foo", - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, }, +}, { + summary: "Maximum priority", + input: map[string]string{ + "chisel.yaml": ` + format: chisel-v1 + archives: + upper-limit: + version: 1 + suites: [main] + components: [main] + priority: 2147483647 + lower-limit: + version: 1 + suites: [main] + components: [main] + priority: -2147483648 + over-limit: + version: 1 + suites: [main] + components: [main] + priority: 2147483648 + `, + }, + relerror: "(?s).*\\bcannot unmarshal !!int `2147483648` into int32\\b.*", }} const defaultChiselYaml = ` diff --git a/internal/slicer/slicer.go b/internal/slicer/slicer.go index 2b684b5d..999d48c0 100644 --- a/internal/slicer/slicer.go +++ b/internal/slicer/slicer.go @@ -5,8 +5,10 @@ import ( "bytes" "fmt" "io" + "math" "os" "path/filepath" + "sort" "strings" "syscall" @@ -59,7 +61,6 @@ func Run(options *RunOptions) error { syscall.Umask(oldUmask) }() - release := options.Selection.Release targetDir := filepath.Clean(options.TargetDir) targetDirAbs := targetDir if !filepath.IsAbs(targetDirAbs) { @@ -70,19 +71,68 @@ func Run(options *RunOptions) error { targetDirAbs = filepath.Join(dir, targetDir) } + // Archives are ordered in descending order by priority. The + // default priority is 0 and the range of allowed priorities + // is the same as the range of the int32 data type. + // + // If a package to be downloaded exists in archive A and + // archive B, and archive A has higher priority than archive + // B, the package is downloaded from archive A even if archive + // B has a newer version. + // + // If two or more archives have the same priority, the most + // recent version of the package from all of them is selected, + // if any. Otherwise, archives with lower priority are tried, + // if any. + // + // If two or more archives with the same priority contain the + // package with the same version, the archive with + // lexicographically greatest label is used. Since archive + // labels are used as keys in YAML map, they should be unique. + orderedArchives := make([]archive.Archive, 0, len(options.Archives)) + for _, archive := range options.Archives { + orderedArchives = append(orderedArchives, archive) + } + sort.Slice(orderedArchives, func(i, j int) bool { + a := orderedArchives[i].Options() + b := orderedArchives[j].Options() + if a.Priority == b.Priority { + if a.Label == b.Label { + panic("internal error: archive labels are not unique") + } + return a.Label > b.Label + } + return a.Priority > b.Priority + }) + // Build information to process the selection. for _, slice := range options.Selection.Slices { extractPackage := extract[slice.Package] if extractPackage == nil { - archiveName := release.Packages[slice.Package].Archive - archive := options.Archives[archiveName] - if archive == nil { - return fmt.Errorf("archive %q not defined", archiveName) + var selectedVersion string + var selectedArchive archive.Archive + var currentPrio int32 = math.MaxInt32 + for _, currentArchive := range orderedArchives { + if prio := currentArchive.Options().Priority; prio < currentPrio { + if selectedVersion != "" { + break // already have a package from a higher priority archive + } + currentPrio = prio + } + pkgInfo := currentArchive.Info(slice.Package) + if pkgInfo == nil { + continue + } + currentVersion := pkgInfo.Version() + if selectedVersion == "" || deb.CompareVersions(selectedVersion, currentVersion) < 0 { + selectedVersion = currentVersion + selectedArchive = currentArchive + } } - if !archive.Exists(slice.Package) { + if selectedVersion == "" { return fmt.Errorf("slice package %q missing from archive", slice.Package) } - archives[slice.Package] = archive + archives[slice.Package] = selectedArchive extractPackage = make(map[string][]deb.ExtractInfo) extract[slice.Package] = extractPackage } diff --git a/internal/slicer/slicer_test.go b/internal/slicer/slicer_test.go index aa9d0f51..a7122901 100644 --- a/internal/slicer/slicer_test.go +++ b/internal/slicer/slicer_test.go @@ -16,9 +16,21 @@ import ( "github.com/canonical/chisel/internal/testutil" ) +var ( + Reg = testutil.Reg + Dir = testutil.Dir + Lnk = testutil.Lnk +) + +type testPackage struct { + info map[string]string + content []byte +} + type slicerTest struct { summary string arch string + pkgs map[string]map[string]testPackage release map[string]string slices []setup.SliceKey hackopt func(c *C, opts *slicer.RunOptions) @@ -496,7 +508,6 @@ var slicerTests = []slicerTest{{ foo: version: 22.04 components: [main, universe] - default: true bar: version: 22.04 components: [main] @@ -515,6 +526,322 @@ var slicerTests = []slicerTest{{ "/usr/bin/": "dir 0755", "/usr/bin/hello": "file 0775 eaf29575", }, +}, { + summary: "Custom archives with custom packages", + pkgs: map[string]map[string]testPackage{ + "leptons": { + "electron": testPackage{ + content: testutil.MustMakeDeb([]testutil.TarEntry{ + Dir(0755, "./"), + Dir(0755, "./mass/"), + Reg(0644, "./mass/electron", "9.1093837015E−31 kg\n"), + Dir(0755, "./usr/"), + Dir(0755, "./usr/share/"), + Dir(0755, "./usr/share/doc/"), + Dir(0755, "./usr/share/doc/electron/"), + Reg(0644, "./usr/share/doc/electron/copyright", ""), + }), + }, + }, + "hadrons": { + "proton": testPackage{ + content: testutil.MustMakeDeb([]testutil.TarEntry{ + Dir(0755, "./"), + Dir(0755, "./mass/"), + Reg(0644, "./mass/proton", "1.67262192369E−27 kg\n"), + }), + }, + }, + }, + release: map[string]string{ + "chisel.yaml": ` + format: chisel-v1 + archives: + leptons: + version: 1 + suites: [main] + components: [main, universe] + hadrons: + version: 1 + suites: [main] + components: [main] + `, + "slices/mydir/electron.yaml": ` + package: electron + slices: + mass: + contents: + /mass/electron: + `, + "slices/mydir/proton.yaml": ` + package: proton + slices: + mass: + contents: + /mass/proton: + `, + }, + slices: []setup.SliceKey{ + {"electron", "mass"}, + {"proton", "mass"}, + }, + result: map[string]string{ + "/mass/": "dir 0755", + "/mass/electron": "file 0644 a1258e30", + "/mass/proton": "file 0644 a2390d10", + "/usr/": "dir 0755", + "/usr/share/": "dir 0755", + "/usr/share/doc/": "dir 0755", + "/usr/share/doc/electron/": "dir 0755", + "/usr/share/doc/electron/copyright": "file 0644 empty", + }, +}, { + summary: "Can pick latest packages from multiple archives", + pkgs: map[string]map[string]testPackage{ + "vertebrates": { + "cheetah": testPackage{ + info: map[string]string{ + "Version": "109.4", + }, + content: testutil.MustMakeDeb([]testutil.TarEntry{ + Dir(0755, "./"), + Dir(0755, "./speed/"), + Reg(0644, "./speed/cheetah", "109.4 km/h\n"), + }), + }, + "ostrich": testPackage{ + info: map[string]string{ + "Version": "100.0", + }, + content: testutil.MustMakeDeb([]testutil.TarEntry{ + Dir(0755, "./"), + Dir(0755, "./speed/"), + Reg(0644, "./speed/ostrich", "100.0 km/h\n"), + }), + }, + }, + "mammals": { + "cheetah": testPackage{ + info: map[string]string{ + "Version": "120.7", + }, + content: testutil.MustMakeDeb([]testutil.TarEntry{ + Dir(0755, "./"), + Dir(0755, "./speed/"), + Reg(0644, "./speed/cheetah", "120.7 km/h\n"), + }), + }, + }, + "birds": { + "ostrich": testPackage{ + info: map[string]string{ + "Version": "90.0", + }, + content: testutil.MustMakeDeb([]testutil.TarEntry{ + Dir(0755, "./"), + Dir(0755, "./speed/"), + Reg(0644, "./speed/ostrich", "90.0 km/h\n"), + }), + }, + }, + }, + slices: []setup.SliceKey{ + {"cheetah", "speed"}, + {"ostrich", "speed"}, + }, + release: map[string]string{ + "chisel.yaml": ` + format: chisel-v1 + archives: + vertebrates: + version: 1 + suites: [main] + components: [main, universe] + mammals: + version: 1 + suites: [main] + components: [main] + birds: + version: 1 + suites: [main] + components: [main] + `, + "slices/mydir/cheetah.yaml": ` + package: cheetah + slices: + speed: + contents: + /speed/cheetah: + `, + "slices/mydir/ostrich.yaml": ` + package: ostrich + slices: + speed: + contents: + /speed/ostrich: + `, + }, + result: map[string]string{ + "/speed/": "dir 0755", + "/speed/cheetah": "file 0644 e98b0879", + "/speed/ostrich": "file 0644 c8fa2806", + }, +}, { + summary: "Pick package from archive with highest priority", + pkgs: map[string]map[string]testPackage{ + "edge": { + "hello": testPackage{ + info: map[string]string{ + "Version": "2.0-beta", + }, + content: testutil.MustMakeDeb([]testutil.TarEntry{ + Dir(0755, "./"), + Reg(0644, "./hello", "Hello, The Edge\n"), + }), + }, + }, + "candidate": { + "hello": testPackage{ + info: map[string]string{ + "Version": "1.8", + }, + content: testutil.MustMakeDeb([]testutil.TarEntry{ + Dir(0755, "./"), + Reg(0644, "./hello", "Hello, The Candidate\n"), + }), + }, + }, + "stable": { + "hello": testPackage{ + info: map[string]string{ + "Version": "1.2", + }, + content: testutil.MustMakeDeb([]testutil.TarEntry{ + Dir(0755, "./"), + Reg(0644, "./hello", "Hello, The Stable\n"), + }), + }, + }, + "obsolete": { + "hello": testPackage{ + info: map[string]string{ + "Version": "1.0", + }, + content: testutil.MustMakeDeb([]testutil.TarEntry{ + Dir(0755, "./"), + Reg(0644, "./hello", "Hello, The Obsolete\n"), + }), + }, + }, + }, + release: map[string]string{ + "chisel.yaml": ` + format: chisel-v1 + archives: + edge: + version: 1 + suites: [main] + components: [main] + candidate: + version: 1 + suites: [main] + components: [main] + priority: 5 + stable: + version: 1 + suites: [main] + components: [main] + priority: 10 + obsolete: + version: 1 + suites: [main] + components: [main] + priority: 10 + `, + "slices/mydir/hello.yaml": ` + package: hello + slices: + all: + contents: + /hello: + `, + }, + slices: []setup.SliceKey{ + {"hello", "all"}, + }, + result: map[string]string{ + "/hello": "file 0644 b5621b65", + }, +}, { + summary: "Archive with lexicographically greatest label wins on stalemate", + pkgs: map[string]map[string]testPackage{ + "10-official": { + "hello": testPackage{ + info: map[string]string{ + "Version": "1.2", + }, + content: testutil.MustMakeDeb([]testutil.TarEntry{ + Dir(0755, "./"), + Reg(0644, "./hello", "Hello, The Official 1.2\n"), + }), + }, + }, + "50-thirdparty": { + "hello": testPackage{ + info: map[string]string{ + "Version": "1.2", + }, + content: testutil.MustMakeDeb([]testutil.TarEntry{ + Dir(0755, "./"), + Reg(0644, "./hello", "Hello, The Third-party 1.2\n"), + }), + }, + }, + "99-testing": { + "hello": testPackage{ + info: map[string]string{ + "Version": "1.3", + }, + content: testutil.MustMakeDeb([]testutil.TarEntry{ + Dir(0755, "./"), + Reg(0644, "./hello", "Hello, The Testing 1.3\n"), + }), + }, + }, + }, + release: map[string]string{ + "chisel.yaml": ` + format: chisel-v1 + archives: + 10-official: + version: 1 + suites: [main] + components: [main] + priority: 10 + 50-thirdparty: + version: 1 + suites: [main] + components: [main] + priority: 10 + 99-testing: + version: 1 + suites: [main] + components: [main] + `, + "slices/mydir/hello.yaml": ` + package: hello + slices: + all: + contents: + /hello: + `, + }, + slices: []setup.SliceKey{ + {"hello", "all"}, + }, + result: map[string]string{ + "/hello": "file 0644 d538d7b8", // "Hello, The Testing 1.3\n" + }, }} const defaultChiselYaml = ` @@ -525,9 +852,25 @@ const defaultChiselYaml = ` components: [main, universe] ` +type testPackageInfo map[string]string + +var _ archive.PackageInfo = (testPackageInfo)(nil) + +func (info testPackageInfo) Name() string { return info["Package"] } +func (info testPackageInfo) Version() string { return info["Version"] } +func (info testPackageInfo) Arch() string { return info["Architecture"] } +func (info testPackageInfo) SHA256() string { return info["SHA256"] } + +func (s testPackageInfo) Get(key string) (value string) { + if s != nil { + value = s[key] + } + return +} + type testArchive struct { options archive.Options - pkgs map[string][]byte + pkgs map[string]testPackage } func (a *testArchive) Options() *archive.Options { @@ -536,7 +879,7 @@ func (a *testArchive) Options() *archive.Options { func (a *testArchive) Fetch(pkg string) (io.ReadCloser, error) { if data, ok := a.pkgs[pkg]; ok { - return io.NopCloser(bytes.NewBuffer(data)), nil + return io.NopCloser(bytes.NewBuffer(data.content)), nil } return nil, fmt.Errorf("attempted to open %q package", pkg) } @@ -546,6 +889,18 @@ func (a *testArchive) Exists(pkg string) bool { return ok } +func (a *testArchive) Info(pkg string) archive.PackageInfo { + var info map[string]string + if pkgData, ok := a.pkgs[pkg]; ok { + if info = pkgData.info; info == nil { + info = map[string]string{ + "Version": "1.0", + } + } + } + return testPackageInfo(info) +} + func (s *S) TestRun(c *C) { for _, test := range slicerTests { c.Logf("Summary: %s", test.summary) @@ -569,25 +924,33 @@ func (s *S) TestRun(c *C) { selection, err := setup.Select(release, test.slices) c.Assert(err, IsNil) - pkgs := map[string][]byte{ - "base-files": testutil.PackageData["base-files"], + pkgs := map[string]testPackage{ + "base-files": testPackage{content: testutil.PackageData["base-files"]}, } for name, entries := range packageEntries { deb, err := testutil.MakeDeb(entries) c.Assert(err, IsNil) - pkgs[name] = deb + pkgs[name] = testPackage{content: deb} } archives := map[string]archive.Archive{} for name, setupArchive := range release.Archives { + var archivePkgs map[string]testPackage + if test.pkgs != nil { + archivePkgs = test.pkgs[name] + } + if archivePkgs == nil { + archivePkgs = pkgs + } archive := &testArchive{ options: archive.Options{ Label: setupArchive.Name, Version: setupArchive.Version, Suites: setupArchive.Suites, Components: setupArchive.Components, + Priority: setupArchive.Priority, Arch: test.arch, }, - pkgs: pkgs, + pkgs: archivePkgs, } archives[name] = archive } @@ -611,8 +974,15 @@ func (s *S) TestRun(c *C) { if test.result != nil { result := make(map[string]string, len(copyrightEntries)+len(test.result)) - for k, v := range copyrightEntries { - result[k] = v + if test.pkgs == nil { + // This was added in order to not specify copyright entries for each + // existing test. These tests use only the base-files embedded + // package. Custom packages may not include copyright entries + // though. So if a test defines any custom packages, it must include + // copyright entries explicitly in the results. + for k, v := range copyrightEntries { + result[k] = v + } } for k, v := range test.result { result[k] = v