diff --git a/internal/setup/setup.go b/internal/setup/setup.go index 6ed24abe..dbc80ae4 100644 --- a/internal/setup/setup.go +++ b/internal/setup/setup.go @@ -16,6 +16,14 @@ import ( "github.com/canonical/chisel/internal/strdist" ) +// Store is the location from which binary packages are obtained via a store API. +type Store struct { + Name string + Kind string + Version string + DefaultPrefix string +} + // Release is a collection of package slices targeting a particular // distribution version. type Release struct { @@ -23,6 +31,7 @@ type Release struct { Path string Packages map[string]*Package Archives map[string]*Archive + Stores map[string]*Store Maintenance *Maintenance } @@ -51,20 +60,24 @@ type Archive struct { // Package holds a collection of slices that represent parts of themselves. type Package struct { - Name string - Path string - Archive string - Slices map[string]*Slice + RealName string + Name string + Path string + Archive string + Store string + DefaultTrack string + Slices map[string]*Slice } // Slice holds the details about a package slice. type Slice struct { - Package string - Name string - Hint string - Essential map[SliceKey]EssentialInfo - Contents map[string]PathInfo - Scripts SliceScripts + Package string + DefaultPrefix string + Name string + Hint string + Essential map[SliceKey]EssentialInfo + Contents map[string]PathInfo + Scripts SliceScripts } type EssentialInfo struct { @@ -86,7 +99,7 @@ const ( GeneratePath PathKind = "generate" // TODO Maybe in the future, for binary support. - //Base64Path PathKind = "base64" + // Base64Path PathKind = "base64" ) type PathUntil string @@ -133,7 +146,13 @@ func ParseSliceKey(sliceKey string) (SliceKey, error) { return apacheutil.ParseSliceKey(sliceKey) } -func (s *Slice) String() string { return s.Package + "_" + s.Name } +func (s *Slice) String() string { + return SliceKey{Package: s.RealPkgName(), Slice: s.Name}.String() +} + +func (s *Slice) RealPkgName() string { + return s.DefaultPrefix + s.Package +} // Selection holds the required configuration to create a Build for a selection // of slices from a Release. It's still an abstract proposal in the sense that @@ -160,16 +179,18 @@ func (s *Selection) Prefers() (map[string]*Package, error) { if !hasPrefers { continue } + sRealName := slice.RealPkgName() old, ok := pathPreferredPkg[path] if !ok { - pathPreferredPkg[path] = s.Release.Packages[slice.Package] + pathPreferredPkg[path] = s.Release.Packages[sRealName] continue } - if old.Name == slice.Package { + oldRealName := old.RealName + if oldRealName == sRealName { // Skip if the package was already recorded. continue } - preferred, err := preferredPathPackage(path, old.Name, slice.Package, prefers) + preferred, err := preferredPathPackage(path, oldRealName, sRealName, prefers) if err != nil { // Note: we have checked above that the path has prefers and // they are different packages so the error cannot be @@ -223,12 +244,14 @@ func (r *Release) validate() error { paths := make(map[string][]*Slice) for _, pkg := range r.Packages { for _, new := range pkg.Slices { - keys = append(keys, SliceKey{pkg.Name, new.Name}) + keys = append(keys, SliceKey{Package: pkg.RealName, Slice: new.Name}) + newRealName := new.RealPkgName() for newPath, newInfo := range new.Contents { if oldSlices, ok := paths[newPath]; ok { for _, old := range oldSlices { - if new.Package != old.Package { - _, err := preferredPathPackage(newPath, new.Package, old.Package, prefers) + oldRealName := old.RealPkgName() + if newRealName != oldRealName { + _, err := preferredPathPackage(newPath, newRealName, oldRealName, prefers) if err == nil { continue } else if err != preferNone { @@ -237,8 +260,8 @@ func (r *Release) validate() error { } oldInfo := old.Contents[newPath] - if !newInfo.SameContent(&oldInfo) || (newInfo.Kind == CopyPath || newInfo.Kind == GlobPath) && new.Package != old.Package { - if old.Package > new.Package || old.Package == new.Package && old.Name > new.Name { + if !newInfo.SameContent(&oldInfo) || (newInfo.Kind == CopyPath || newInfo.Kind == GlobPath) && newRealName != oldRealName { + if oldRealName > newRealName || oldRealName == newRealName && old.Name > new.Name { old, new = new, old } return fmt.Errorf("slices %s and %s conflict on %s", old, new, newPath) @@ -261,7 +284,7 @@ func (r *Release) validate() error { found := false for _, slice := range paths[skey.path] { - if slice.Package == skey.pkg { + if slice.RealPkgName() == skey.pkg { found = true break } @@ -286,14 +309,16 @@ func (r *Release) validate() error { } for _, new := range newSlices { newInfo := new.Contents[newPath] + newRealName := new.RealPkgName() + oldRealName := old.RealPkgName() if oldInfo.Kind == GlobPath && (newInfo.Kind == GlobPath || newInfo.Kind == CopyPath) { - if new.Package == old.Package { + if newRealName == oldRealName { continue } } if strdist.GlobPath(newPath, oldPath) { - if (old.Package > new.Package) || (old.Package == new.Package && old.Name > new.Name) || - (old.Package == new.Package && old.Name == new.Name && oldPath > newPath) { + if (oldRealName > newRealName) || (oldRealName == newRealName && old.Name > new.Name) || + (oldRealName == newRealName && old.Name == new.Name && oldPath > newPath) { old, new = new, old oldPath, newPath = newPath, oldPath } @@ -337,6 +362,16 @@ func (r *Release) validate() error { } } + // Check that stores referenced in packages are defined. + for _, pkg := range r.Packages { + if pkg.Store == "" { + continue + } + if _, ok := r.Stores[pkg.Store]; !ok { + return fmt.Errorf("%s: package refers to undefined store %q", pkg.Path, pkg.Store) + } + } + return nil } @@ -346,7 +381,6 @@ func (r *Release) validate() error { // If arch is supplied, essential(s) not specific to that arch are not // considered. func order(pkgs map[string]*Package, keys []SliceKey, arch string) ([]SliceKey, error) { - // Preprocess the list to improve error messages. for _, key := range keys { if pkg, ok := pkgs[key.Package]; !ok { @@ -391,9 +425,11 @@ func order(pkgs map[string]*Package, keys []SliceKey, arch string) ([]SliceKey, if len(names) > 1 { return nil, fmt.Errorf("essential loop detected: %s", strings.Join(names, ", ")) } - name := names[0] - dot := strings.IndexByte(name, '_') - order = append(order, SliceKey{name[:dot], name[dot+1:]}) + key, err := ParseSliceKey(names[0]) + if err != nil { + return nil, fmt.Errorf("internal error: cannot parse slice key %q", names[0]) + } + order = append(order, key) } return order, nil @@ -441,21 +477,21 @@ func readSlices(release *Release, baseDir, dirName string) error { pkgName := match[1] pkgPath := filepath.Join(dirName, entry.Name()) - if pkg, ok := release.Packages[pkgName]; ok { - return fmt.Errorf("package %q slices defined more than once: %s and %s", pkgName, pkg.Path, stripBase(baseDir, pkgPath)) - } data, err := os.ReadFile(pkgPath) if err != nil { // Errors from package os generally include the path. return fmt.Errorf("cannot read slice definition file: %v", err) } - pkg, err := parsePackage(release.Format, pkgName, stripBase(baseDir, pkgPath), data) + pkg, err := parsePackage(release, pkgName, stripBase(baseDir, pkgPath), data) if err != nil { return err } - release.Packages[pkg.Name] = pkg + if existing, ok := release.Packages[pkg.RealName]; ok { + return fmt.Errorf("package %q slices defined more than once: %s and %s", pkg.RealName, existing.Path, stripBase(baseDir, pkgPath)) + } + release.Packages[pkg.RealName] = pkg } return nil } @@ -520,32 +556,32 @@ type preferKey struct { func (r *Release) prefers() (map[preferKey]string, error) { prefers := make(map[preferKey]string) - for _, pkg := range r.Packages { + for realName, pkg := range r.Packages { for _, slice := range pkg.Slices { for path, info := range slice.Contents { if info.Prefer != "" { if _, ok := r.Packages[info.Prefer]; !ok { return nil, fmt.Errorf("slice %s path %s 'prefer' refers to undefined package %q", slice, path, info.Prefer) } - tkey := preferKey{preferTarget, path, pkg.Name} + tkey := preferKey{preferTarget, path, realName} skey := preferKey{preferSource, path, info.Prefer} if target, ok := prefers[tkey]; ok { if target != info.Prefer { pkg1, pkg2 := sortPair(target, info.Prefer) return nil, fmt.Errorf("package %q has conflicting prefers for %s: %s != %s", - pkg.Name, path, pkg1, pkg2) + realName, path, pkg1, pkg2) } } else if source, ok := prefers[skey]; ok { - if source != pkg.Name { - pkg1, pkg2 := sortPair(source, pkg.Name) + if source != realName { + pkg1, pkg2 := sortPair(source, realName) return nil, fmt.Errorf("packages %q and %q cannot both prefer %q for %s", pkg1, pkg2, info.Prefer, path) } } else { prefers[tkey] = info.Prefer - prefers[skey] = pkg.Name + prefers[skey] = realName // Sample package that requires this path to be in a prefer relationship. - prefers[preferKey{preferSource, path, ""}] = pkg.Name + prefers[preferKey{preferSource, path, ""}] = realName } } } diff --git a/internal/setup/setup_test.go b/internal/setup/setup_test.go index d669943f..17586b2b 100644 --- a/internal/setup/setup_test.go +++ b/internal/setup/setup_test.go @@ -92,9 +92,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -139,8 +140,9 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice1": { Package: "mypkg", @@ -150,7 +152,7 @@ var setupTests = []setupTest{{ "/file/path2": {Kind: "copy", Info: "/other/path"}, "/file/path3": {Kind: "symlink", Info: "/other/path"}, "/file/path4": {Kind: "text", Info: "content", Until: "mutate"}, - "/file/path5": {Kind: "copy", Mode: 0755, Mutable: true}, + "/file/path5": {Kind: "copy", Mode: 0o755, Mutable: true}, "/file/path6/": {Kind: "dir"}, }, }, @@ -158,7 +160,7 @@ var setupTests = []setupTest{{ Package: "mypkg", Name: "myslice2", Essential: map[setup.SliceKey]setup.EssentialInfo{ - {"mypkg", "myslice1"}: {}, + {Package: "mypkg", Slice: "myslice1"}: {}, }, Contents: map[string]setup.PathInfo{ "/another/path": {Kind: "copy"}, @@ -204,8 +206,9 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice1": { Package: "mypkg", @@ -270,9 +273,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -327,9 +331,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -421,7 +426,7 @@ var setupTests = []setupTest{{ myslice2: {essential: [mypkg1_myslice1]} `, }, - selslices: []setup.SliceKey{{"mypkg1", "myslice1"}}, + selslices: []setup.SliceKey{{Package: "mypkg1", Slice: "myslice1"}}, selection: &setup.Selection{ Slices: []*setup.Slice{{ Package: "mypkg1", @@ -444,7 +449,7 @@ var setupTests = []setupTest{{ myslice2: {essential: [mypkg1_myslice1]} `, }, - selslices: []setup.SliceKey{{"mypkg2", "myslice2"}}, + selslices: []setup.SliceKey{{Package: "mypkg2", Slice: "myslice2"}}, selection: &setup.Selection{ Slices: []*setup.Slice{{ Package: "mypkg1", @@ -453,7 +458,7 @@ var setupTests = []setupTest{{ Package: "mypkg2", Name: "myslice2", Essential: map[setup.SliceKey]setup.EssentialInfo{ - {"mypkg1", "myslice1"}: {}, + {Package: "mypkg1", Slice: "myslice1"}: {}, }, }}, }, @@ -483,7 +488,7 @@ var setupTests = []setupTest{{ /path3: {symlink: /link} `, }, - selslices: []setup.SliceKey{{"mypkg1", "myslice1"}, {"mypkg1", "myslice2"}, {"mypkg2", "myslice1"}}, + selslices: []setup.SliceKey{{Package: "mypkg1", Slice: "myslice1"}, {Package: "mypkg1", Slice: "myslice2"}, {Package: "mypkg2", Slice: "myslice1"}}, }, { summary: "Conflicting paths across slices", input: map[string]string{ @@ -582,8 +587,9 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice1": { Package: "mypkg", @@ -800,8 +806,9 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice": { Package: "mypkg", @@ -843,8 +850,9 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice": { Package: "mypkg", @@ -887,8 +895,9 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice": { Package: "mypkg", @@ -960,9 +969,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -1166,8 +1176,9 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice": { Package: "mypkg", @@ -1241,9 +1252,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -1354,8 +1366,9 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "jq": { - Name: "jq", - Path: "slices/mydir/jq.yaml", + RealName: "jq", + Name: "jq", + Path: "slices/mydir/jq.yaml", Slices: map[string]*setup.Slice{ "bins": { Package: "jq", @@ -1465,14 +1478,15 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "slice1": { Package: "mypkg", Name: "slice1", Essential: map[setup.SliceKey]setup.EssentialInfo{ - {"mypkg", "slice2"}: {}, + {Package: "mypkg", Slice: "slice2"}: {}, }, }, "slice2": { @@ -1483,16 +1497,16 @@ var setupTests = []setupTest{{ Package: "mypkg", Name: "slice3", Essential: map[setup.SliceKey]setup.EssentialInfo{ - {"mypkg", "slice2"}: {}, - {"mypkg", "slice1"}: {}, - {"mypkg", "slice4"}: {}, + {Package: "mypkg", Slice: "slice2"}: {}, + {Package: "mypkg", Slice: "slice1"}: {}, + {Package: "mypkg", Slice: "slice4"}: {}, }, }, "slice4": { Package: "mypkg", Name: "slice4", Essential: map[setup.SliceKey]setup.EssentialInfo{ - {"mypkg", "slice2"}: {}, + {Package: "mypkg", Slice: "slice2"}: {}, }, }, }, @@ -1538,30 +1552,32 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "slice1": { Package: "mypkg", Name: "slice1", Essential: map[setup.SliceKey]setup.EssentialInfo{ - {"myotherpkg", "slice2"}: {}, - {"mypkg", "slice2"}: {}, - {"myotherpkg", "slice1"}: {}, + {Package: "myotherpkg", Slice: "slice2"}: {}, + {Package: "mypkg", Slice: "slice2"}: {}, + {Package: "myotherpkg", Slice: "slice1"}: {}, }, }, "slice2": { Package: "mypkg", Name: "slice2", Essential: map[setup.SliceKey]setup.EssentialInfo{ - {"myotherpkg", "slice2"}: {}, + {Package: "myotherpkg", Slice: "slice2"}: {}, }, }, }, }, "myotherpkg": { - Name: "myotherpkg", - Path: "slices/mydir/myotherpkg.yaml", + RealName: "myotherpkg", + Name: "myotherpkg", + Path: "slices/mydir/myotherpkg.yaml", Slices: map[string]*setup.Slice{ "slice1": { Package: "myotherpkg", @@ -1721,8 +1737,9 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice": { Package: "mypkg", @@ -1739,7 +1756,7 @@ var setupTests = []setupTest{{ EndOfLife: time.Date(2100, time.January, 1, 0, 0, 0, 0, time.UTC), }, }, - selslices: []setup.SliceKey{{"mypkg", "myslice"}}, + selslices: []setup.SliceKey{{Package: "mypkg", Slice: "myslice"}}, selection: &setup.Selection{ Slices: []*setup.Slice{{ Package: "mypkg", @@ -1774,8 +1791,9 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice": { Package: "mypkg", @@ -1792,7 +1810,7 @@ var setupTests = []setupTest{{ EndOfLife: time.Date(2100, time.January, 1, 0, 0, 0, 0, time.UTC), }, }, - selslices: []setup.SliceKey{{"mypkg", "myslice"}}, + selslices: []setup.SliceKey{{Package: "mypkg", Slice: "myslice"}}, selerror: `slice mypkg_myslice has invalid 'generate' for path /dir/\*\*: "foo"`, }, { summary: "Paths with generate: manifest must have trailing /**", @@ -1869,8 +1887,9 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice": { Package: "mypkg", @@ -1882,8 +1901,9 @@ var setupTests = []setupTest{{ }, }, "mypkg2": { - Name: "mypkg2", - Path: "slices/mydir/mypkg2.yaml", + RealName: "mypkg2", + Name: "mypkg2", + Path: "slices/mydir/mypkg2.yaml", Slices: map[string]*setup.Slice{ "myslice": { Package: "mypkg2", @@ -2040,9 +2060,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -2166,9 +2187,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -2233,9 +2255,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -2328,9 +2351,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -2407,10 +2431,10 @@ var setupTests = []setupTest{{ }, { summary: "Path conflicts with 'prefer'", selslices: []setup.SliceKey{ - {"mypkg1", "myslice1"}, - {"mypkg1", "myslice2"}, - {"mypkg2", "myslice1"}, - {"mypkg3", "myslice1"}, + {Package: "mypkg1", Slice: "myslice1"}, + {Package: "mypkg1", Slice: "myslice2"}, + {Package: "mypkg2", Slice: "myslice1"}, + {Package: "mypkg3", Slice: "myslice1"}, }, input: map[string]string{ "slices/mydir/mypkg1.yaml": ` @@ -2454,8 +2478,9 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg1": { - Name: "mypkg1", - Path: "slices/mydir/mypkg1.yaml", + RealName: "mypkg1", + Name: "mypkg1", + Path: "slices/mydir/mypkg1.yaml", Slices: map[string]*setup.Slice{ "myslice1": { Package: "mypkg1", @@ -2475,8 +2500,9 @@ var setupTests = []setupTest{{ }, }, "mypkg2": { - Name: "mypkg2", - Path: "slices/mydir/mypkg2.yaml", + RealName: "mypkg2", + Name: "mypkg2", + Path: "slices/mydir/mypkg2.yaml", Slices: map[string]*setup.Slice{ "myslice1": { Package: "mypkg2", @@ -2489,8 +2515,9 @@ var setupTests = []setupTest{{ }, }, "mypkg3": { - Name: "mypkg3", - Path: "slices/mydir/mypkg3.yaml", + RealName: "mypkg3", + Name: "mypkg3", + Path: "slices/mydir/mypkg3.yaml", Slices: map[string]*setup.Slice{ "myslice1": { Package: "mypkg3", @@ -2514,9 +2541,9 @@ var setupTests = []setupTest{{ }, { summary: "Path conflicts with 'prefer' depends on selection", selslices: []setup.SliceKey{ - {"mypkg1", "myslice1"}, - {"mypkg1", "myslice2"}, - {"mypkg2", "myslice1"}, + {Package: "mypkg1", Slice: "myslice1"}, + {Package: "mypkg1", Slice: "myslice2"}, + {Package: "mypkg2", Slice: "myslice1"}, }, input: map[string]string{ "slices/mydir/mypkg1.yaml": ` @@ -2889,9 +2916,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -2939,9 +2967,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -3128,9 +3157,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -3250,9 +3280,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -3372,9 +3403,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -3494,9 +3526,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -3614,9 +3647,10 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", - Slices: map[string]*setup.Slice{}, + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", + Slices: map[string]*setup.Slice{}, }, }, Maintenance: &setup.Maintenance{ @@ -3657,31 +3691,32 @@ var setupTests = []setupTest{{ }, Packages: map[string]*setup.Package{ "mypkg": { - Name: "mypkg", - Path: "slices/mydir/mypkg.yaml", + RealName: "mypkg", + Name: "mypkg", + Path: "slices/mydir/mypkg.yaml", Slices: map[string]*setup.Slice{ "myslice1": { Package: "mypkg", Name: "myslice1", Essential: map[setup.SliceKey]setup.EssentialInfo{ - {"mypkg", "myslice2"}: {Arch: []string{"amd64"}}, - {"mypkg", "myslice3"}: {Arch: []string{"amd64", "arm64"}}, - {"mypkg", "myslice4"}: {Arch: []string{"amd64", "i386"}}, - {"mypkg", "myslice5"}: {Arch: nil}, + {Package: "mypkg", Slice: "myslice2"}: {Arch: []string{"amd64"}}, + {Package: "mypkg", Slice: "myslice3"}: {Arch: []string{"amd64", "arm64"}}, + {Package: "mypkg", Slice: "myslice4"}: {Arch: []string{"amd64", "i386"}}, + {Package: "mypkg", Slice: "myslice5"}: {Arch: nil}, }, }, "myslice2": { Package: "mypkg", Name: "myslice2", Essential: map[setup.SliceKey]setup.EssentialInfo{ - {"mypkg", "myslice4"}: {Arch: []string{"amd64", "i386"}}, + {Package: "mypkg", Slice: "myslice4"}: {Arch: []string{"amd64", "i386"}}, }, }, "myslice3": { Package: "mypkg", Name: "myslice3", Essential: map[setup.SliceKey]setup.EssentialInfo{ - {"mypkg", "myslice4"}: {Arch: []string{"amd64", "i386"}}, + {Package: "mypkg", Slice: "myslice4"}: {Arch: []string{"amd64", "i386"}}, }, }, "myslice4": { @@ -3693,7 +3728,7 @@ var setupTests = []setupTest{{ Package: "mypkg", Name: "myslice5", Essential: map[setup.SliceKey]setup.EssentialInfo{ - {"mypkg", "myslice4"}: {Arch: []string{"amd64", "i386"}}, + {Package: "mypkg", Slice: "myslice4"}: {Arch: []string{"amd64", "i386"}}, }, }, }, @@ -3896,6 +3931,386 @@ var setupTests = []setupTest{{ `, }, relerror: `package "mypkg" slices defined more than once: slices/dir1/mypkg.yaml and slices/dir2/mypkg.yaml`, +}, { + summary: "Store package is parsed correctly", + input: map[string]string{ + "chisel.yaml": ` + format: v3 + maintenance: + standard: 2025-01-01 + end-of-life: 2100-01-01 + archives: + ubuntu: + version: 26.10 + components: [main, universe] + suites: [stonking] + public-keys: [test-key] + public-keys: + test-key: + id: ` + testKey.ID + ` + armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t") + ` + stores: + bin: + kind: bin + version: 26.10 + default-prefix: "bin-" + `, + "slices/bin/mypkg.yaml": ` + package: mypkg + store: bin + default-track: "3.0" + `, + }, + release: &setup.Release{ + Format: "v3", + Archives: map[string]*setup.Archive{ + "ubuntu": { + Name: "ubuntu", + Version: "26.10", + Suites: []string{"stonking"}, + Components: []string{"main", "universe"}, + PubKeys: []*packet.PublicKey{testKey.PubKey}, + Maintained: true, + }, + }, + Stores: map[string]*setup.Store{ + "bin": { + Name: "bin", + Kind: "bin", + Version: "26.10", + DefaultPrefix: "bin-", + }, + }, + Packages: map[string]*setup.Package{ + "bin-mypkg": { + RealName: "bin-mypkg", + Name: "mypkg", + Path: "slices/bin/mypkg.yaml", + Store: "bin", + DefaultTrack: "3.0", + Slices: map[string]*setup.Slice{}, + }, + }, + Maintenance: &setup.Maintenance{ + Standard: time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC), + EndOfLife: time.Date(2100, time.January, 1, 0, 0, 0, 0, time.UTC), + }, + }, +}, { + summary: "Store and archive are mutually exclusive", + input: map[string]string{ + "chisel.yaml": ` + format: v3 + maintenance: + standard: 2025-01-01 + end-of-life: 2100-01-01 + archives: + ubuntu: + version: 26.10 + components: [main, universe] + suites: [stonking] + public-keys: [test-key] + public-keys: + test-key: + id: ` + testKey.ID + ` + armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t") + ` + stores: + bin: + kind: bin + version: 26.10 + default-prefix: "bin-" + `, + "slices/bin/mypkg.yaml": ` + package: mypkg + archive: ubuntu + store: bin + default-track: "3.0" + `, + }, + relerror: `cannot parse package "mypkg": both 'store' and 'archive' fields are set`, +}, { + summary: "Store package missing default-track (v3)", + input: map[string]string{ + "chisel.yaml": ` + format: v3 + maintenance: + standard: 2025-01-01 + end-of-life: 2100-01-01 + archives: + ubuntu: + version: 26.10 + components: [main, universe] + suites: [stonking] + public-keys: [test-key] + public-keys: + test-key: + id: ` + testKey.ID + ` + armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t") + ` + stores: + bin: + kind: bin + version: 26.10 + default-prefix: "bin-" + `, + "slices/bin/mypkg.yaml": ` + package: mypkg + store: bin + `, + }, + relerror: `cannot parse package "mypkg": 'default-track' is required when 'store' is set`, +}, { + summary: "default-track without store (v3)", + input: map[string]string{ + "chisel.yaml": ` + format: v3 + maintenance: + standard: 2025-01-01 + end-of-life: 2100-01-01 + archives: + ubuntu: + version: 26.10 + components: [main, universe] + suites: [stonking] + public-keys: [test-key] + public-keys: + test-key: + id: ` + testKey.ID + ` + armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t") + ` + stores: + bin: + kind: bin + version: 26.10 + default-prefix: "bin-" + `, + "slices/bin/mypkg.yaml": ` + package: mypkg + default-track: "3.0" + `, + }, + relerror: `cannot parse package "mypkg": 'store' is required when 'default-track' is set`, +}, { + summary: "default-track must not contain / (v3)", + input: map[string]string{ + "chisel.yaml": ` + format: v3 + maintenance: + standard: 2025-01-01 + end-of-life: 2100-01-01 + archives: + ubuntu: + version: 26.10 + components: [main, universe] + suites: [stonking] + public-keys: [test-key] + public-keys: + test-key: + id: ` + testKey.ID + ` + armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t") + ` + stores: + bin: + kind: bin + version: 26.10 + default-prefix: "bin-" + `, + "slices/bin/mypkg.yaml": ` + package: mypkg + store: bin + default-track: "3.0/stable" + `, + }, + relerror: `cannot parse package "mypkg": 'default-track' must be a track name without /`, +}, { + summary: "Package store references undefined store (v3)", + input: map[string]string{ + "chisel.yaml": ` + format: v3 + maintenance: + standard: 2025-01-01 + end-of-life: 2100-01-01 + archives: + ubuntu: + version: 26.10 + components: [main, universe] + suites: [stonking] + public-keys: [test-key] + public-keys: + test-key: + id: ` + testKey.ID + ` + armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t") + ` + stores: + bin: + kind: bin + version: 26.10 + default-prefix: "bin-" + `, + "slices/bin/mypkg.yaml": ` + package: mypkg + store: no-such-store + default-track: "3.0" + `, + }, + relerror: `slices/bin/mypkg.yaml: package refers to undefined store "no-such-store"`, +}, { + summary: "Store missing version", + input: map[string]string{ + "chisel.yaml": ` + format: v3 + maintenance: + standard: 2025-01-01 + end-of-life: 2100-01-01 + archives: + ubuntu: + version: 26.10 + components: [main, universe] + suites: [stonking] + public-keys: [test-key] + public-keys: + test-key: + id: ` + testKey.ID + ` + armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t") + ` + stores: + bin: + kind: bin + default-prefix: "bin-" + `, + }, + relerror: `chisel.yaml: store "bin" missing version field`, +}, { + summary: "Store missing default-prefix", + input: map[string]string{ + "chisel.yaml": ` + format: v3 + maintenance: + standard: 2025-01-01 + end-of-life: 2100-01-01 + archives: + ubuntu: + version: 26.10 + components: [main, universe] + suites: [stonking] + public-keys: [test-key] + public-keys: + test-key: + id: ` + testKey.ID + ` + armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t") + ` + stores: + bin: + kind: bin + version: 26.10 + `, + }, + relerror: `chisel.yaml: store "bin" missing default-prefix field`, +}, { + summary: "Same-named package in archive and store", + input: map[string]string{ + "chisel.yaml": ` + format: v3 + maintenance: + standard: 2025-01-01 + end-of-life: 2100-01-01 + archives: + ubuntu: + version: 26.10 + components: [main, universe] + suites: [stonking] + public-keys: [test-key] + public-keys: + test-key: + id: ` + testKey.ID + ` + armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t") + ` + stores: + bin: + kind: bin + version: 26.10 + default-prefix: "bin-" + `, + "slices/curl.yaml": ` + package: curl + slices: + libs: + contents: + /usr/lib/libcurl.so: + bins: + contents: + /usr/bin/curl: + `, + "slices/bin/curl.yaml": ` + package: curl + store: bin + default-track: "3.0" + slices: + bins: + contents: + /usr/bin/curl-bin: + `, + }, + release: &setup.Release{ + Format: "v3", + Archives: map[string]*setup.Archive{ + "ubuntu": { + Name: "ubuntu", + Version: "26.10", + Suites: []string{"stonking"}, + Components: []string{"main", "universe"}, + PubKeys: []*packet.PublicKey{testKey.PubKey}, + Maintained: true, + }, + }, + Stores: map[string]*setup.Store{ + "bin": { + Name: "bin", + Kind: "bin", + Version: "26.10", + DefaultPrefix: "bin-", + }, + }, + Packages: map[string]*setup.Package{ + "curl": { + RealName: "curl", + Name: "curl", + Path: "slices/curl.yaml", + Slices: map[string]*setup.Slice{ + "libs": { + Package: "curl", + DefaultPrefix: "", + Name: "libs", + Contents: map[string]setup.PathInfo{ + "/usr/lib/libcurl.so": {Kind: setup.CopyPath}, + }, + }, + "bins": { + Package: "curl", + DefaultPrefix: "", + Name: "bins", + Contents: map[string]setup.PathInfo{ + "/usr/bin/curl": {Kind: setup.CopyPath}, + }, + }, + }, + }, + "bin-curl": { + RealName: "bin-curl", + Name: "curl", + Path: "slices/bin/curl.yaml", + Store: "bin", + DefaultTrack: "3.0", + Slices: map[string]*setup.Slice{ + "bins": { + Package: "curl", + DefaultPrefix: "bin-", + Name: "bins", + Contents: map[string]setup.PathInfo{ + "/usr/bin/curl-bin": {Kind: setup.CopyPath}, + }, + }, + }, + }, + }, + Maintenance: &setup.Maintenance{ + Standard: time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC), + EndOfLife: time.Date(2100, time.January, 1, 0, 0, 0, 0, time.UTC), + }, + }, }} func (s *S) TestParseRelease(c *C) { @@ -3998,9 +4413,9 @@ func runParseReleaseTests(c *C, tests []setupTest) { dir := c.MkDir() for path, data := range test.input { fpath := filepath.Join(dir, path) - err := os.MkdirAll(filepath.Dir(fpath), 0755) + err := os.MkdirAll(filepath.Dir(fpath), 0o755) c.Assert(err, IsNil) - err = os.WriteFile(fpath, testutil.Reindent(data), 0644) + err = os.WriteFile(fpath, testutil.Reindent(data), 0o644) c.Assert(err, IsNil) } @@ -4045,6 +4460,40 @@ func runParseReleaseTests(c *C, tests []setupTest) { } } +func (s *S) TestStoresNotSupportedInOldFormats(c *C) { + for _, format := range []string{"v1", "v2"} { + c.Logf("Format: %s", format) + chiselYaml := ` + format: ` + format + ` + maintenance: + standard: 2025-01-01 + end-of-life: 2100-01-01 + archives: + ubuntu: + version: 22.04 + components: [main, universe] + suites: [jammy] + public-keys: [test-key] + public-keys: + test-key: + id: ` + testKey.ID + ` + armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t") + ` + stores: + bin: + kind: bin + version: 22.04 + default-prefix: "bin-" + ` + dir := c.MkDir() + err := os.WriteFile(filepath.Join(dir, "chisel.yaml"), testutil.Reindent(chiselYaml), 0o644) + c.Assert(err, IsNil) + err = os.MkdirAll(filepath.Join(dir, "slices"), 0o755) + c.Assert(err, IsNil) + _, err = setup.ReadRelease(dir) + c.Assert(err, ErrorMatches, `chisel.yaml: stores is not supported in format "`+format+`"`) + } +} + func (s *S) TestPackageMarshalYAML(c *C) { for _, test := range setupTests { c.Logf("Summary: %s", test.summary) @@ -4061,16 +4510,16 @@ func (s *S) TestPackageMarshalYAML(c *C) { dir := c.MkDir() // Write chisel.yaml. fpath := filepath.Join(dir, "chisel.yaml") - err := os.WriteFile(fpath, testutil.Reindent(data), 0644) + err := os.WriteFile(fpath, testutil.Reindent(data), 0o644) c.Assert(err, IsNil) // Write the packages YAML. for _, pkg := range test.release.Packages { fpath = filepath.Join(dir, pkg.Path) - err = os.MkdirAll(filepath.Dir(fpath), 0755) + err = os.MkdirAll(filepath.Dir(fpath), 0o755) c.Assert(err, IsNil) pkgData, err := yaml.Marshal(pkg) c.Assert(err, IsNil) - err = os.WriteFile(fpath, testutil.Reindent(string(pkgData)), 0644) + err = os.WriteFile(fpath, testutil.Reindent(string(pkgData)), 0o644) c.Assert(err, IsNil) } @@ -4083,7 +4532,7 @@ func (s *S) TestPackageMarshalYAML(c *C) { } func (s *S) TestPackageYAMLFormat(c *C) { - var tests = []struct { + tests := []struct { summary string input map[string]string expected map[string]string @@ -4232,6 +4681,40 @@ func (s *S) TestPackageYAMLFormat(c *C) { mypkg_three: {arch: i386} `, }, + }, { + summary: "Store package fields", + input: map[string]string{ + "chisel.yaml": ` + format: v3 + maintenance: + standard: 2025-01-01 + end-of-life: 2100-01-01 + archives: + ubuntu: + version: 26.10 + components: [main, universe] + suites: [stonking] + public-keys: [test-key] + stores: + bin: + kind: bin + version: 26.10 + default-prefix: "bin-" + public-keys: + test-key: + id: ` + testKey.ID + ` + armor: |` + "\n" + testutil.PrefixEachLine(testKey.PubKeyArmor, "\t\t\t\t\t\t\t") + ` + `, + "slices/bin/mypkg.yaml": ` + package: mypkg + store: bin + default-track: "3.0" + slices: + myslice: + contents: + /usr/bin/mypkg: {} + `, + }, }} for _, test := range tests { @@ -4244,9 +4727,9 @@ func (s *S) TestPackageYAMLFormat(c *C) { dir := c.MkDir() for path, data := range test.input { fpath := filepath.Join(dir, path) - err := os.MkdirAll(filepath.Dir(fpath), 0755) + err := os.MkdirAll(filepath.Dir(fpath), 0o755) c.Assert(err, IsNil) - err = os.WriteFile(fpath, testutil.Reindent(data), 0644) + err = os.WriteFile(fpath, testutil.Reindent(data), 0o644) c.Assert(err, IsNil) } @@ -4332,7 +4815,7 @@ func (s *S) TestSelectEmptyArch(c *C) { release, err := setup.ReadRelease(dir) c.Assert(err, IsNil) - selslice := []setup.SliceKey{{"mypkg", "myslice"}} + selslice := []setup.SliceKey{{Package: "mypkg", Slice: "myslice"}} selection, err := setup.Select(release, selslice, "") c.Assert(err, IsNil) diff --git a/internal/setup/yaml.go b/internal/setup/yaml.go index cefe86cb..f13232ee 100644 --- a/internal/setup/yaml.go +++ b/internal/setup/yaml.go @@ -35,6 +35,7 @@ type yamlRelease struct { // fields that break said compatibility (e.g. "pro" archives) and merged // together with "archives". V2Archives map[string]yamlArchive `yaml:"v2-archives"` + Stores map[string]yamlStore `yaml:"stores"` } const ( @@ -52,9 +53,17 @@ type yamlArchive struct { PubKeys []string `yaml:"public-keys"` } +type yamlStore struct { + Kind string `yaml:"kind"` + Version string `yaml:"version"` + DefaultPrefix string `yaml:"default-prefix"` +} + type yamlPackage struct { - Name string `yaml:"package"` - Archive string `yaml:"archive,omitempty"` + Name string `yaml:"package"` + Archive string `yaml:"archive,omitempty"` + Store string `yaml:"store,omitempty"` + DefaultTrack string `yaml:"default-track,omitempty"` // For backwards-compatibility reasons with v1 and v2, essential needs // custom logic to be parsed. See [yamlEssentialListMap]. Essential yamlEssentialListMap `yaml:"essential,omitempty"` @@ -118,8 +127,10 @@ func (es yamlEssentialListMap) MarshalYAML() (any, error) { return es.Values, nil } -var _ yaml.Marshaler = yamlEssentialListMap{} -var _ yaml.Unmarshaler = (*yamlEssentialListMap)(nil) +var ( + _ yaml.Marshaler = yamlEssentialListMap{} + _ yaml.Unmarshaler = (*yamlEssentialListMap)(nil) +) type yamlPath struct { Dir bool `yaml:"make,omitempty"` @@ -433,10 +444,35 @@ func parseRelease(baseDir, filePath string, data []byte) (*Release, error) { release.Archives[archiveName] = details } + // Parse stores. + if len(yamlVar.Stores) > 0 && (release.Format == "v1" || release.Format == "v2") { + return nil, fmt.Errorf("%s: stores is not supported in format %q", fileName, release.Format) + } + if len(yamlVar.Stores) > 0 { + release.Stores = make(map[string]*Store, len(yamlVar.Stores)) + } + for storeName, details := range yamlVar.Stores { + if details.Kind == "" { + return nil, fmt.Errorf("%s: store %q missing kind field", fileName, storeName) + } + if details.Version == "" { + return nil, fmt.Errorf("%s: store %q missing version field", fileName, storeName) + } + if details.DefaultPrefix == "" { + return nil, fmt.Errorf("%s: store %q missing default-prefix field", fileName, storeName) + } + release.Stores[storeName] = &Store{ + Name: storeName, + Kind: details.Kind, + Version: details.Version, + DefaultPrefix: details.DefaultPrefix, + } + } + return release, err } -func parsePackage(format, pkgName, pkgPath string, data []byte) (*Package, error) { +func parsePackage(release *Release, pkgName, pkgPath string, data []byte) (*Package, error) { pkg := Package{ Name: pkgName, Path: pkgPath, @@ -454,28 +490,58 @@ func parsePackage(format, pkgName, pkgPath string, data []byte) (*Package, error return nil, fmt.Errorf("%s: filename and 'package' field (%q) disagree", pkgPath, yamlPkg.Name) } - if format == "v1" || format == "v2" { + if (yamlPkg.Store != "" || yamlPkg.DefaultTrack != "") && (release.Format == "v1" || release.Format == "v2") { + return nil, fmt.Errorf("cannot parse package %q: 'store' and 'default-track' are not supported in format %q", pkgName, release.Format) + } + if yamlPkg.Store != "" && yamlPkg.Archive != "" { + return nil, fmt.Errorf("cannot parse package %q: both 'store' and 'archive' fields are set", pkgName) + } + if yamlPkg.Store != "" { + if yamlPkg.DefaultTrack == "" { + return nil, fmt.Errorf("cannot parse package %q: 'default-track' is required when 'store' is set", pkgName) + } + if strings.Contains(yamlPkg.DefaultTrack, "/") { + return nil, fmt.Errorf("cannot parse package %q: 'default-track' must be a track name without /", pkgName) + } + pkg.Store = yamlPkg.Store + pkg.DefaultTrack = yamlPkg.DefaultTrack + } else { + if yamlPkg.DefaultTrack != "" { + return nil, fmt.Errorf("cannot parse package %q: 'store' is required when 'default-track' is set", pkgName) + } + } + + // Derive the package DefaultPrefix from its store reference. + var prefix string + if pkg.Store != "" { + if store := release.Stores[pkg.Store]; store != nil { + prefix = store.DefaultPrefix + } + } + pkg.RealName = prefix + pkgName + + if release.Format == "v1" || release.Format == "v2" { if yamlPkg.Essential.style != unsetEssential && yamlPkg.Essential.style != listEssential { - return nil, fmt.Errorf("cannot parse package %q: essential expects a list", pkgName) + return nil, fmt.Errorf("cannot parse package %q: essential expects a list", pkg.RealName) } for sliceName, yamlSlice := range yamlPkg.Slices { if yamlSlice.Essential.style != unsetEssential && yamlSlice.Essential.style != listEssential { - return nil, fmt.Errorf("cannot parse slice %s: essential expects a list", SliceKey{pkgName, sliceName}) + return nil, fmt.Errorf("cannot parse slice %s: essential expects a list", SliceKey{Package: pkg.RealName, Slice: sliceName}) } } } else { if yamlPkg.V3Essential != nil { - return nil, fmt.Errorf("cannot parse package %q: v3-essential is obsolete since format v3", pkgName) + return nil, fmt.Errorf("cannot parse package %q: v3-essential is obsolete since format v3", pkg.RealName) } if yamlPkg.Essential.style != unsetEssential && yamlPkg.Essential.style != mapEssential { - return nil, fmt.Errorf("cannot parse package %q: essential expects a map", pkgName) + return nil, fmt.Errorf("cannot parse package %q: essential expects a map", pkg.RealName) } for sliceName, yamlSlice := range yamlPkg.Slices { if yamlSlice.V3Essential != nil { - return nil, fmt.Errorf("cannot parse slice %s: v3-essential is obsolete since format v3", SliceKey{pkgName, sliceName}) + return nil, fmt.Errorf("cannot parse slice %s: v3-essential is obsolete since format v3", SliceKey{Package: pkg.RealName, Slice: sliceName}) } if yamlSlice.Essential.style != unsetEssential && yamlSlice.Essential.style != mapEssential { - return nil, fmt.Errorf("cannot parse slice %s: essential expects a map", SliceKey{pkgName, sliceName}) + return nil, fmt.Errorf("cannot parse slice %s: essential expects a map", SliceKey{Package: pkg.RealName, Slice: sliceName}) } } } @@ -491,12 +557,13 @@ func parsePackage(format, pkgName, pkgPath string, data []byte) (*Package, error return !unicode.IsPrint(r) }) if len(yamlSlice.Hint) > 40 || hintNotPrintable { - return nil, fmt.Errorf("slice %s has invalid hint %q (must be len <= 40, only contain letters, numbers, symbols and \" \")", SliceKey{pkgName, sliceName}, yamlSlice.Hint) + return nil, fmt.Errorf("slice %s has invalid hint %q (must be len <= 40, only contain letters, numbers, symbols and \" \")", SliceKey{Package: pkg.RealName, Slice: sliceName}, yamlSlice.Hint) } slice := &Slice{ - Package: pkgName, - Name: sliceName, - Hint: yamlSlice.Hint, + Package: pkgName, + DefaultPrefix: prefix, + Name: sliceName, + Hint: yamlSlice.Hint, Scripts: SliceScripts{ Mutate: yamlSlice.Mutate, }, @@ -518,7 +585,7 @@ func parsePackage(format, pkgName, pkgPath string, data []byte) (*Package, error if !path.IsAbs(contPath) || path.Clean(contPath) != comparePath { return nil, fmt.Errorf("slice %s_%s has invalid content path: %s", pkgName, sliceName, contPath) } - var kinds = make([]PathKind, 0, 3) + kinds := make([]PathKind, 0, 3) var info string var mode uint var mutable bool @@ -531,17 +598,17 @@ func parsePackage(format, pkgName, pkgPath string, data []byte) (*Package, error zeroPathGenerate.Generate = yamlPath.Generate if !yamlPath.SameContent(&zeroPathGenerate) || yamlPath.Prefer != "" || yamlPath.Until != UntilNone { return nil, fmt.Errorf("slice %s_%s path %s has invalid generate options", - pkgName, sliceName, contPath) + pkg.RealName, sliceName, contPath) } if _, err := validateGeneratePath(contPath); err != nil { - return nil, fmt.Errorf("slice %s_%s has invalid generate path: %s", pkgName, sliceName, err) + return nil, fmt.Errorf("slice %s_%s has invalid generate path: %s", pkg.RealName, sliceName, err) } kinds = append(kinds, GeneratePath) } else if strings.ContainsAny(contPath, "*?") { if yamlPath != nil { if !yamlPath.SameContent(&zeroPath) || yamlPath.Prefer != "" { return nil, fmt.Errorf("slice %s_%s path %s has invalid wildcard options", - pkgName, sliceName, contPath) + pkg.RealName, sliceName, contPath) } } kinds = append(kinds, GlobPath) @@ -554,7 +621,7 @@ func parsePackage(format, pkgName, pkgPath string, data []byte) (*Package, error if yamlPath.Dir { if !strings.HasSuffix(contPath, "/") { return nil, fmt.Errorf("slice %s_%s path %s must end in / for 'make' to be valid", - pkgName, sliceName, contPath) + pkg.RealName, sliceName, contPath) } kinds = append(kinds, DirPath) } @@ -577,17 +644,17 @@ func parsePackage(format, pkgName, pkgPath string, data []byte) (*Package, error switch until { case UntilNone, UntilMutate: default: - return nil, fmt.Errorf("slice %s_%s has invalid 'until' for path %s: %q", pkgName, sliceName, contPath, until) + return nil, fmt.Errorf("slice %s_%s has invalid 'until' for path %s: %q", pkg.RealName, sliceName, contPath, until) } arch = yamlPath.Arch.List for _, s := range arch { if deb.ValidateArch(s) != nil { - return nil, fmt.Errorf("slice %s_%s has invalid 'arch' for path %s: %q", pkgName, sliceName, contPath, s) + return nil, fmt.Errorf("slice %s_%s has invalid 'arch' for path %s: %q", pkg.RealName, sliceName, contPath, s) } } } - if prefer == pkgName { - return nil, fmt.Errorf("slice %s_%s cannot 'prefer' its own package for path %s", pkgName, sliceName, contPath) + if prefer == pkg.RealName { + return nil, fmt.Errorf("slice %s_%s cannot 'prefer' its own package for path %s", pkg.RealName, sliceName, contPath) } if len(kinds) == 0 { kinds = append(kinds, CopyPath) @@ -597,10 +664,10 @@ func parsePackage(format, pkgName, pkgPath string, data []byte) (*Package, error for i, s := range kinds { list[i] = string(s) } - return nil, fmt.Errorf("conflict in slice %s_%s definition for path %s: %s", pkgName, sliceName, contPath, strings.Join(list, ", ")) + return nil, fmt.Errorf("conflict in slice %s_%s definition for path %s: %s", pkg.RealName, sliceName, contPath, strings.Join(list, ", ")) } if mutable && kinds[0] != TextPath && (kinds[0] != CopyPath || isDir) { - return nil, fmt.Errorf("slice %s_%s mutable is not a regular file: %s", pkgName, sliceName, contPath) + return nil, fmt.Errorf("slice %s_%s mutable is not a regular file: %s", pkg.RealName, sliceName, contPath) } slice.Contents[contPath] = PathInfo{ Kind: kinds[0], @@ -690,9 +757,11 @@ func sliceToYAML(s *Slice) (*yamlSlice, error) { // packageToYAML converts a Package object to a yamlPackage object. func packageToYAML(p *Package) (*yamlPackage, error) { pkg := &yamlPackage{ - Name: p.Name, - Archive: p.Archive, - Slices: make(map[string]yamlSlice, len(p.Slices)), + Name: p.Name, + Archive: p.Archive, + Store: p.Store, + DefaultTrack: p.DefaultTrack, + Slices: make(map[string]yamlSlice, len(p.Slices)), } for name, slice := range p.Slices { yamlSlice, err := sliceToYAML(slice) @@ -801,7 +870,7 @@ func parseEssentials(yamlPkg *yamlPackage, yamlSlice *yamlSlice, pkgPath string, if err != nil { return fmt.Errorf("package %q has invalid essential slice reference: %q", yamlPkg.Name, refName) } - if sliceKey.Package == slice.Package && sliceKey.Slice == slice.Name { + if sliceKey.Package == slice.RealPkgName() && sliceKey.Slice == slice.Name { // Do not add the slice to its own essentials list. return nil } @@ -823,7 +892,7 @@ func parseEssentials(yamlPkg *yamlPackage, yamlSlice *yamlSlice, pkgPath string, if err != nil { return fmt.Errorf("package %q has invalid essential slice reference: %q", yamlPkg.Name, refName) } - if sliceKey.Package == slice.Package && sliceKey.Slice == slice.Name { + if sliceKey.Package == slice.RealPkgName() && sliceKey.Slice == slice.Name { return fmt.Errorf("cannot add slice to itself as essential %s in %s", refName, pkgPath) } if _, ok := slice.Essential[sliceKey]; ok { diff --git a/internal/slicer/slicer.go b/internal/slicer/slicer.go index 9d3447fb..dc372862 100644 --- a/internal/slicer/slicer.go +++ b/internal/slicer/slicer.go @@ -23,7 +23,7 @@ import ( "github.com/canonical/chisel/internal/setup" ) -const manifestMode fs.FileMode = 0644 +const manifestMode fs.FileMode = 0o644 type RunOptions struct { Selection *setup.Selection @@ -95,6 +95,13 @@ func Run(options *RunOptions) error { return err } + // Build a map from package real name to architecture. + pkgArch := make(map[string]string) + for realName, a := range pkgArchive { + pkgArch[realName] = a.Options().Arch + } + // TODO Handle packages coming from a store as well when we support them. + prefers, err := options.Selection.Prefers() if err != nil { return err @@ -103,12 +110,13 @@ func Run(options *RunOptions) error { // Build information to process the selection. extract := make(map[string]map[string][]deb.ExtractInfo) for _, slice := range options.Selection.Slices { - extractPackage := extract[slice.Package] + realName := slice.RealPkgName() + extractPackage := extract[realName] if extractPackage == nil { extractPackage = make(map[string][]deb.ExtractInfo) - extract[slice.Package] = extractPackage + extract[realName] = extractPackage } - arch := pkgArchive[slice.Package].Options().Arch + arch := pkgArch[realName] for targetPath, pathInfo := range slice.Contents { if targetPath == "" { continue @@ -149,15 +157,16 @@ func Run(options *RunOptions) error { packages := make(map[string]io.ReadSeekCloser) var pkgInfos []*archive.PackageInfo for _, slice := range options.Selection.Slices { - if packages[slice.Package] != nil { + realName := slice.RealPkgName() + if packages[realName] != nil { continue } - reader, info, err := pkgArchive[slice.Package].Fetch(slice.Package) + reader, info, err := pkgArchive[realName].Fetch(slice.Package) if err != nil { return err } defer reader.Close() - packages[slice.Package] = reader + packages[realName] = reader pkgInfos = append(pkgInfos, info) } @@ -234,18 +243,19 @@ func Run(options *RunOptions) error { // Extract all packages, also using the selection order. for _, slice := range options.Selection.Slices { - reader := packages[slice.Package] + realPkgName := slice.RealPkgName() + reader := packages[realPkgName] if reader == nil { continue } err := deb.Extract(reader, &deb.ExtractOptions{ Package: slice.Package, - Extract: extract[slice.Package], + Extract: extract[realPkgName], TargetDir: targetDir, Create: create, }) reader.Close() - packages[slice.Package] = nil + packages[realPkgName] = nil if err != nil { return err } @@ -269,7 +279,7 @@ func Run(options *RunOptions) error { // them to the appropriate slices. relPaths := map[string][]*setup.Slice{} for _, slice := range options.Selection.Slices { - arch := pkgArchive[slice.Package].Options().Arch + arch := pkgArch[slice.RealPkgName()] for relPath, pathInfo := range slice.Contents { if len(pathInfo.Arch) > 0 && !slices.Contains(pathInfo.Arch, arch) { continue @@ -351,7 +361,8 @@ func Run(options *RunOptions) error { } func generateManifests(targetDir string, selection *setup.Selection, - report *manifestutil.Report, pkgInfos []*archive.PackageInfo) error { + report *manifestutil.Report, pkgInfos []*archive.PackageInfo, +) error { manifestSlices := manifestutil.FindPaths(selection.Slices) if len(manifestSlices) == 0 { // Nothing to do. @@ -456,9 +467,9 @@ func createFile(targetDir, relPath string, pathInfo setup.PathInfo) (*fsutil.Ent targetMode := pathInfo.Mode if targetMode == 0 { if pathInfo.Kind == setup.DirPath { - targetMode = 0755 + targetMode = 0o755 } else { - targetMode = 0644 + targetMode = 0o644 } } @@ -508,10 +519,16 @@ func selectPkgArchives(archives map[string]archive.Archive, selection *setup.Sel pkgArchive := make(map[string]archive.Archive) for _, s := range selection.Slices { - if _, ok := pkgArchive[s.Package]; ok { + realPkgName := s.RealPkgName() + if _, ok := pkgArchive[realPkgName]; ok { + continue + } + pkg := selection.Release.Packages[realPkgName] + if pkg.Store != "" { + // Packages coming from a store are not fetched from an archive, + // so we skip them here. continue } - pkg := selection.Release.Packages[s.Package] var candidates []*setup.Archive if pkg.Archive == "" { @@ -533,7 +550,7 @@ func selectPkgArchives(archives map[string]archive.Archive, selection *setup.Sel if chosen == nil { return nil, fmt.Errorf("cannot find package %q in archive(s)", pkg.Name) } - pkgArchive[pkg.Name] = chosen + pkgArchive[realPkgName] = chosen } return pkgArchive, nil } diff --git a/internal/slicer/slicer_test.go b/internal/slicer/slicer_test.go index 8d3cf775..d11d8ca0 100644 --- a/internal/slicer/slicer_test.go +++ b/internal/slicer/slicer_test.go @@ -22,9 +22,7 @@ import ( "github.com/canonical/chisel/public/manifest" ) -var ( - testKey = testutil.PGPKeys["key1"] -) +var testKey = testutil.PGPKeys["key1"] type slicerTest struct { summary string @@ -46,7 +44,7 @@ var packageEntries = map[string][]testutil.TarEntry{ {Header: tar.Header{Name: "./usr/"}}, {Header: tar.Header{Name: "./usr/lib/"}}, {Header: tar.Header{Name: "./usr/lib/x86_64-linux-gnu/"}}, - {Header: tar.Header{Name: "./usr/lib/x86_64-linux-gnu/libssl.so.3", Mode: 00755}}, + {Header: tar.Header{Name: "./usr/lib/x86_64-linux-gnu/libssl.so.3", Mode: 0o0755}}, {Header: tar.Header{Name: "./usr/share/"}}, {Header: tar.Header{Name: "./usr/share/doc/"}}, {Header: tar.Header{Name: "./usr/share/doc/copyright-symlink-libssl3/"}}, @@ -59,7 +57,7 @@ var packageEntries = map[string][]testutil.TarEntry{ {Header: tar.Header{Name: "./etc/ssl/openssl.cnf"}}, {Header: tar.Header{Name: "./usr/"}}, {Header: tar.Header{Name: "./usr/bin/"}}, - {Header: tar.Header{Name: "./usr/bin/openssl", Mode: 00755}}, + {Header: tar.Header{Name: "./usr/bin/openssl", Mode: 0o0755}}, {Header: tar.Header{Name: "./usr/share/"}}, {Header: tar.Header{Name: "./usr/share/doc/"}}, {Header: tar.Header{Name: "./usr/share/doc/copyright-symlink-openssl/"}}, @@ -69,16 +67,16 @@ var packageEntries = map[string][]testutil.TarEntry{ var testPackageCopyrightEntries = []testutil.TarEntry{ // Hardcoded copyright paths. - testutil.Dir(0755, "./usr/"), - testutil.Dir(0755, "./usr/share/"), - testutil.Dir(0755, "./usr/share/doc/"), - testutil.Dir(0755, "./usr/share/doc/test-package/"), - testutil.Reg(0644, "./usr/share/doc/test-package/copyright", "copyright"), + testutil.Dir(0o755, "./usr/"), + testutil.Dir(0o755, "./usr/share/"), + testutil.Dir(0o755, "./usr/share/doc/"), + testutil.Dir(0o755, "./usr/share/doc/test-package/"), + testutil.Reg(0o644, "./usr/share/doc/test-package/copyright", "copyright"), } var slicerTests = []slicerTest{{ summary: "Basic slicing", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -111,7 +109,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Glob extraction", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -133,7 +131,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Create new file under extracted directory and preserve parent directory permissions", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -153,7 +151,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Create new nested file under extracted directory and preserve parent directory permissions", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -174,7 +172,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Create new directory under extracted directory and preserve parent directory permissions", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -194,7 +192,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Create new file using glob and preserve parent directory permissions", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -218,7 +216,7 @@ var slicerTests = []slicerTest{{ }, { summary: "Conditional architecture", arch: "amd64", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -249,7 +247,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Copyright is not installed implicitly", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, pkgs: []*testutil.TestPackage{{ Name: "test-package", // Add the copyright entries to the package. @@ -274,8 +272,9 @@ var slicerTests = []slicerTest{{ }, { summary: "Install two packages", slices: []setup.SliceKey{ - {"test-package", "myslice"}, - {"other-package", "myslice"}}, + {Package: "test-package", Slice: "myslice"}, + {Package: "other-package", Slice: "myslice"}, + }, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.PackageData["test-package"], @@ -317,25 +316,26 @@ var slicerTests = []slicerTest{{ }, { summary: "Install two packages, explicit path has preference over implicit parent", slices: []setup.SliceKey{ - {"a-implicit-parent", "myslice"}, - {"b-explicit-dir", "myslice"}, - {"c-implicit-parent", "myslice"}}, + {Package: "a-implicit-parent", Slice: "myslice"}, + {Package: "b-explicit-dir", Slice: "myslice"}, + {Package: "c-implicit-parent", Slice: "myslice"}, + }, pkgs: []*testutil.TestPackage{{ Name: "a-implicit-parent", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./dir/"), - testutil.Reg(0644, "./dir/file-1", "random"), + testutil.Dir(0o755, "./dir/"), + testutil.Reg(0o644, "./dir/file-1", "random"), }), }, { Name: "b-explicit-dir", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(01777, "./dir/"), + testutil.Dir(0o1777, "./dir/"), }), }, { Name: "c-implicit-parent", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0766, "./dir/"), - testutil.Reg(0644, "./dir/file-2", "random"), + testutil.Dir(0o766, "./dir/"), + testutil.Reg(0o644, "./dir/file-2", "random"), }), }}, release: map[string]string{ @@ -376,8 +376,9 @@ var slicerTests = []slicerTest{{ }, { summary: "Valid same file in two slices in different packages", slices: []setup.SliceKey{ - {"test-package", "myslice"}, - {"other-package", "myslice"}}, + {Package: "test-package", Slice: "myslice"}, + {Package: "other-package", Slice: "myslice"}, + }, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.PackageData["test-package"], @@ -409,7 +410,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Script: write a file", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -430,7 +431,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Script: read a file", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -456,7 +457,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Script: use 'until' to remove file after mutate", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -480,7 +481,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Script: use 'until' to remove wildcard after mutate", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -498,7 +499,7 @@ var slicerTests = []slicerTest{{ manifestPaths: map[string]string{}, }, { summary: "Script: 'until' does not remove non-empty directories", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -519,7 +520,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Script: writing same contents to existing file does not set the final hash in report", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -540,7 +541,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Script: cannot write non-mutable files", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -555,7 +556,7 @@ var slicerTests = []slicerTest{{ error: `slice test-package_myslice: cannot write file which is not mutable: /dir/text-file`, }, { summary: "Script: cannot write to unlisted file", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -569,7 +570,7 @@ var slicerTests = []slicerTest{{ error: `slice test-package_myslice: cannot write file which is not mutable: /dir/text-file`, }, { summary: "Script: cannot write to directory", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -584,7 +585,7 @@ var slicerTests = []slicerTest{{ error: `slice test-package_myslice: cannot write file which is not mutable: /dir/`, }, { summary: "Script: cannot read unlisted content", - slices: []setup.SliceKey{{"test-package", "myslice2"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice2"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -600,7 +601,7 @@ var slicerTests = []slicerTest{{ error: `slice test-package_myslice2: cannot read file which is not selected: /dir/text-file`, }, { summary: "Script: can read globbed content", - slices: []setup.SliceKey{{"test-package", "myslice1"}, {"test-package", "myslice2"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice1"}, {Package: "test-package", Slice: "myslice2"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -615,7 +616,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Relative content root directory must not error", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -636,7 +637,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Can list parent directories of normal paths", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -655,7 +656,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Cannot list unselected directory", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -670,7 +671,7 @@ var slicerTests = []slicerTest{{ error: `slice test-package_myslice: cannot list directory which is not selected: /a/d/`, }, { summary: "Cannot list file path as a directory", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -685,7 +686,7 @@ var slicerTests = []slicerTest{{ error: `slice test-package_myslice: content is not a directory: /a/b/c`, }, { summary: "Can list parent directories of globs", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -699,7 +700,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Cannot list directories not matched by glob", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -714,7 +715,7 @@ var slicerTests = []slicerTest{{ error: `slice test-package_myslice: cannot list directory which is not selected: /other-dir/`, }, { summary: "Duplicate copyright symlink is ignored", - slices: []setup.SliceKey{{"copyright-symlink-openssl", "bins"}}, + slices: []setup.SliceKey{{Package: "copyright-symlink-openssl", Slice: "bins"}}, pkgs: []*testutil.TestPackage{{ Name: "copyright-symlink-openssl", Data: testutil.MustMakeDeb(packageEntries["copyright-symlink-openssl"]), @@ -746,7 +747,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Can list unclean directory paths", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -765,7 +766,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Cannot read directories", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -780,14 +781,14 @@ var slicerTests = []slicerTest{{ error: `slice test-package_myslice: content is not a file: /x/y`, }, { summary: "Multiple archives with priority", - slices: []setup.SliceKey{{"test-package", "myslice"}, {"other-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}, {Package: "other-package", Slice: "myslice"}}, pkgs: []*testutil.TestPackage{{ Name: "test-package", Hash: "h1", Version: "v1", Arch: "a1", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Reg(0644, "./file", "from foo"), + testutil.Reg(0o644, "./file", "from foo"), }), Archives: []string{"foo"}, }, { @@ -796,7 +797,7 @@ var slicerTests = []slicerTest{{ Version: "v2", Arch: "a2", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Reg(0644, "./file", "from bar"), + testutil.Reg(0o644, "./file", "from bar"), }), Archives: []string{"bar"}, }, { @@ -805,7 +806,7 @@ var slicerTests = []slicerTest{{ Version: "v3", Arch: "a3", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Reg(0644, "./other-file", "from bar"), + testutil.Reg(0o644, "./other-file", "from bar"), }), Archives: []string{"bar"}, }}, @@ -864,14 +865,14 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Pinned archive bypasses higher priority", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, pkgs: []*testutil.TestPackage{{ Name: "test-package", Hash: "h1", Version: "v1", Arch: "a1", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Reg(0644, "./file", "from foo"), + testutil.Reg(0o644, "./file", "from foo"), }), Archives: []string{"foo"}, }, { @@ -880,7 +881,7 @@ var slicerTests = []slicerTest{{ Version: "v2", Arch: "a2", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Reg(0644, "./file", "from bar"), + testutil.Reg(0o644, "./file", "from bar"), }), Archives: []string{"bar"}, }}, @@ -932,11 +933,11 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Pinned archive does not have the package", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Reg(0644, "./file", "from foo"), + testutil.Reg(0o644, "./file", "from foo"), }), Archives: []string{"foo"}, }}, @@ -978,7 +979,7 @@ var slicerTests = []slicerTest{{ error: `cannot find package "test-package" in archive\(s\)`, }, { summary: "No archives have the package", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, pkgs: []*testutil.TestPackage{}, release: map[string]string{ "chisel.yaml": ` @@ -1015,11 +1016,11 @@ var slicerTests = []slicerTest{{ error: `cannot find package "test-package" in archive\(s\)`, }, { summary: "Negative priority archives are ignored when not explicitly pinned in package", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Reg(0644, "./file", "from foo"), + testutil.Reg(0o644, "./file", "from foo"), }), Archives: []string{"foo"}, }}, @@ -1052,14 +1053,14 @@ var slicerTests = []slicerTest{{ error: `cannot find package "test-package" in archive\(s\)`, }, { summary: "Negative priority archive explicitly pinned in package", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, pkgs: []*testutil.TestPackage{{ Name: "test-package", Hash: "h1", Version: "v1", Arch: "a1", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Reg(0644, "./file", "from foo"), + testutil.Reg(0o644, "./file", "from foo"), }), Archives: []string{"foo"}, }}, @@ -1102,8 +1103,8 @@ var slicerTests = []slicerTest{{ }, { summary: "Multiple slices of same package", slices: []setup.SliceKey{ - {"test-package", "myslice1"}, - {"test-package", "myslice2"}, + {Package: "test-package", Slice: "myslice1"}, + {Package: "test-package", Slice: "myslice2"}, }, release: map[string]string{ "slices/mydir/test-package.yaml": ` @@ -1140,8 +1141,8 @@ var slicerTests = []slicerTest{{ }, { summary: "Same glob in several entries with until:mutate and reading from script", slices: []setup.SliceKey{ - {"test-package", "myslice1"}, - {"test-package", "myslice2"}, + {Package: "test-package", Slice: "myslice1"}, + {Package: "test-package", Slice: "myslice2"}, }, release: map[string]string{ "slices/mydir/test-package.yaml": ` @@ -1186,8 +1187,8 @@ var slicerTests = []slicerTest{{ }, { summary: "Overlapping globs, until:mutate and reading from script", slices: []setup.SliceKey{ - {"test-package", "myslice2"}, - {"test-package", "myslice1"}, + {Package: "test-package", Slice: "myslice2"}, + {Package: "test-package", Slice: "myslice1"}, }, release: map[string]string{ "slices/mydir/test-package.yaml": ` @@ -1232,8 +1233,8 @@ var slicerTests = []slicerTest{{ }, { summary: "Overlapping glob and single entry, until:mutate on entry and reading from script", slices: []setup.SliceKey{ - {"test-package", "myslice1"}, - {"test-package", "myslice2"}, + {Package: "test-package", Slice: "myslice1"}, + {Package: "test-package", Slice: "myslice2"}, }, release: map[string]string{ "slices/mydir/test-package.yaml": ` @@ -1278,8 +1279,8 @@ var slicerTests = []slicerTest{{ }, { summary: "Overlapping glob and single entry, until:mutate on glob and reading from script", slices: []setup.SliceKey{ - {"test-package", "myslice1"}, - {"test-package", "myslice2"}, + {Package: "test-package", Slice: "myslice1"}, + {Package: "test-package", Slice: "myslice2"}, }, release: map[string]string{ "slices/mydir/test-package.yaml": ` @@ -1307,8 +1308,8 @@ var slicerTests = []slicerTest{{ }, { summary: "Overlapping glob and single entry, until:mutate on both and reading from script", slices: []setup.SliceKey{ - {"test-package", "myslice1"}, - {"test-package", "myslice2"}, + {Package: "test-package", Slice: "myslice1"}, + {Package: "test-package", Slice: "myslice2"}, }, release: map[string]string{ "slices/mydir/test-package.yaml": ` @@ -1331,8 +1332,8 @@ var slicerTests = []slicerTest{{ }, { summary: "Content not created in packages with until:mutate on one and reading from script", slices: []setup.SliceKey{ - {"test-package", "myslice1"}, - {"test-package", "myslice2"}, + {Package: "test-package", Slice: "myslice1"}, + {Package: "test-package", Slice: "myslice2"}, }, release: map[string]string{ "slices/mydir/test-package.yaml": ` @@ -1355,8 +1356,8 @@ var slicerTests = []slicerTest{{ }, { summary: "Install two packages, both are recorded", slices: []setup.SliceKey{ - {"test-package", "myslice"}, - {"other-package", "myslice"}, + {Package: "test-package", Slice: "myslice"}, + {Package: "other-package", Slice: "myslice"}, }, pkgs: []*testutil.TestPackage{{ Name: "test-package", @@ -1392,7 +1393,7 @@ var slicerTests = []slicerTest{{ }, { summary: "Two packages, only one is selected and recorded", slices: []setup.SliceKey{ - {"test-package", "myslice"}, + {Package: "test-package", Slice: "myslice"}, }, pkgs: []*testutil.TestPackage{{ Name: "test-package", @@ -1426,7 +1427,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Relative paths are properly trimmed during extraction", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.MustMakeDeb([]testutil.TarEntry{ @@ -1436,12 +1437,12 @@ var slicerTests = []slicerTest{{ // relative path. Since TrimLeft takes in a cutset instead of a // prefix, the desired relative path was not produced. // See https://github.com/canonical/chisel/pull/145. - testutil.Dir(0755, "./foo-bar/"), + testutil.Dir(0o755, "./foo-bar/"), }), }}, hackopt: func(c *C, opts *slicer.RunOptions) { opts.TargetDir = filepath.Join(filepath.Clean(opts.TargetDir), "foo") - err := os.Mkdir(opts.TargetDir, 0755) + err := os.Mkdir(opts.TargetDir, 0o755) c.Assert(err, IsNil) }, release: map[string]string{ @@ -1457,7 +1458,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Producing a manifest is not mandatory", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, hackopt: func(c *C, opts *slicer.RunOptions) { // Remove the manifest slice that the tests add automatically. var index int @@ -1479,7 +1480,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "No valid archives defined due to invalid pro value", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, release: map[string]string{ "chisel.yaml": ` format: v1 @@ -1506,14 +1507,15 @@ var slicerTests = []slicerTest{{ }, { summary: "Valid hard link in two slices in the same package", slices: []setup.SliceKey{ - {"test-package", "slice1"}, - {"test-package", "slice2"}}, + {Package: "test-package", Slice: "slice1"}, + {Package: "test-package", Slice: "slice2"}, + }, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Reg(0644, "./file", "foo"), - testutil.Hrd(0644, "./hardlink", "./file"), + testutil.Dir(0o755, "./"), + testutil.Reg(0o644, "./file", "foo"), + testutil.Hrd(0o644, "./hardlink", "./file"), }), }}, release: map[string]string{ @@ -1540,14 +1542,15 @@ var slicerTests = []slicerTest{{ }, { summary: "Hard link entries can be extracted without extracting the regular file", slices: []setup.SliceKey{ - {"test-package", "myslice"}}, + {Package: "test-package", Slice: "myslice"}, + }, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Reg(0644, "./file", "foo"), - testutil.Hrd(0644, "./hardlink1", "./file"), - testutil.Hrd(0644, "./hardlink2", "./file"), + testutil.Dir(0o755, "./"), + testutil.Reg(0o644, "./file", "foo"), + testutil.Hrd(0o644, "./hardlink1", "./file"), + testutil.Hrd(0o644, "./hardlink2", "./file"), }), }}, release: map[string]string{ @@ -1570,15 +1573,16 @@ var slicerTests = []slicerTest{{ }, { summary: "Hard link identifier for different groups", slices: []setup.SliceKey{ - {"test-package", "myslice"}}, + {Package: "test-package", Slice: "myslice"}, + }, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Reg(0644, "./file1", "text for file1"), - testutil.Reg(0644, "./file2", "text for file2"), - testutil.Hrd(0644, "./hardlink1", "./file1"), - testutil.Hrd(0644, "./hardlink2", "./file2"), + testutil.Dir(0o755, "./"), + testutil.Reg(0o644, "./file1", "text for file1"), + testutil.Reg(0o644, "./file2", "text for file2"), + testutil.Hrd(0o644, "./hardlink1", "./file1"), + testutil.Hrd(0o644, "./hardlink2", "./file2"), }), }}, release: map[string]string{ @@ -1605,13 +1609,14 @@ var slicerTests = []slicerTest{{ }, { summary: "Single hard link entry can be extracted without regular file and no hard links are created", slices: []setup.SliceKey{ - {"test-package", "myslice"}}, + {Package: "test-package", Slice: "myslice"}, + }, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Reg(0644, "./file", "foo"), - testutil.Hrd(0644, "./hardlink", "./file"), + testutil.Dir(0o755, "./"), + testutil.Reg(0o644, "./file", "foo"), + testutil.Hrd(0o644, "./hardlink", "./file"), }), }}, release: map[string]string{ @@ -1632,15 +1637,16 @@ var slicerTests = []slicerTest{{ }, { summary: "Hard link to symlink does not follow symlink", slices: []setup.SliceKey{ - {"test-package", "myslice"}}, + {Package: "test-package", Slice: "myslice"}, + }, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Dir(0755, "./"), - testutil.Reg(0644, "./file", "foo"), - testutil.Lnk(0644, "./symlink", "./file"), - testutil.Hrd(0644, "./hardlink", "./symlink"), + testutil.Dir(0o755, "./"), + testutil.Dir(0o755, "./"), + testutil.Reg(0o644, "./file", "foo"), + testutil.Lnk(0o644, "./symlink", "./file"), + testutil.Hrd(0o644, "./hardlink", "./symlink"), }), }}, release: map[string]string{ @@ -1664,22 +1670,22 @@ var slicerTests = []slicerTest{{ }, { summary: "Hard link identifiers are unique across packages", slices: []setup.SliceKey{ - {"test-package1", "myslice"}, - {"test-package2", "myslice"}, + {Package: "test-package1", Slice: "myslice"}, + {Package: "test-package2", Slice: "myslice"}, }, pkgs: []*testutil.TestPackage{{ Name: "test-package1", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Reg(0644, "./file1", "foo"), - testutil.Hrd(0644, "./hardlink1", "./file1"), + testutil.Dir(0o755, "./"), + testutil.Reg(0o644, "./file1", "foo"), + testutil.Hrd(0o644, "./hardlink1", "./file1"), }), }, { Name: "test-package2", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Reg(0644, "./file2", "foo"), - testutil.Hrd(0644, "./hardlink2", "./file2"), + testutil.Dir(0o755, "./"), + testutil.Reg(0o644, "./file2", "foo"), + testutil.Hrd(0o644, "./hardlink2", "./file2"), }), }}, release: map[string]string{ @@ -1715,13 +1721,14 @@ var slicerTests = []slicerTest{{ }, { summary: "Mutations for hard links are forbidden", slices: []setup.SliceKey{ - {"test-package", "myslice"}}, + {Package: "test-package", Slice: "myslice"}, + }, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Reg(0644, "./file", "foo"), - testutil.Hrd(0644, "./hardlink", "./file"), + testutil.Dir(0o755, "./"), + testutil.Reg(0o644, "./file", "foo"), + testutil.Hrd(0o644, "./hardlink", "./file"), }), }}, release: map[string]string{ @@ -1740,13 +1747,14 @@ var slicerTests = []slicerTest{{ }, { summary: "Hard links can be marked as mutable, but not mutated", slices: []setup.SliceKey{ - {"test-package", "myslice"}}, + {Package: "test-package", Slice: "myslice"}, + }, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Reg(0644, "./file", "foo"), - testutil.Hrd(0644, "./hardlink", "./file"), + testutil.Dir(0o755, "./"), + testutil.Reg(0o644, "./file", "foo"), + testutil.Hrd(0o644, "./hardlink", "./file"), }), }}, release: map[string]string{ @@ -1769,12 +1777,12 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Hard links cannot escape the target directory", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Hrd(0644, "./hardlink", "/etc/group"), + testutil.Dir(0o755, "./"), + testutil.Hrd(0o644, "./hardlink", "/etc/group"), }), }}, release: map[string]string{ @@ -1789,12 +1797,12 @@ var slicerTests = []slicerTest{{ error: `cannot extract from package "test-package": invalid link target /etc/group`, }, { summary: "Cannot extract outside of target directory", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, pkgs: []*testutil.TestPackage{{ Name: "test-package", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Reg(0644, "./../file", "hijacking system file"), + testutil.Dir(0o755, "./"), + testutil.Reg(0o644, "./../file", "hijacking system file"), }), }}, release: map[string]string{ @@ -1810,25 +1818,25 @@ var slicerTests = []slicerTest{{ }, { summary: "Extract conflicting paths with prefer from proper package", slices: []setup.SliceKey{ - {"test-package1", "myslice"}, - {"test-package2", "myslice"}, + {Package: "test-package1", Slice: "myslice"}, + {Package: "test-package2", Slice: "myslice"}, }, pkgs: []*testutil.TestPackage{{ Name: "test-package1", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Reg(0644, "./file", "foo"), + testutil.Dir(0o755, "./"), + testutil.Reg(0o644, "./file", "foo"), }), }, { Name: "test-package2", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), - testutil.Reg(0644, "./file", "bar"), + testutil.Dir(0o755, "./"), + testutil.Reg(0o644, "./file", "bar"), }), }, { Name: "test-package3", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), + testutil.Dir(0o755, "./"), }), }}, release: map[string]string{ @@ -1871,24 +1879,24 @@ var slicerTests = []slicerTest{{ }, { summary: "Warning when implicit parent directories conflict", slices: []setup.SliceKey{ - {"test-package1", "myslice"}, - {"test-package2", "myslice"}, + {Package: "test-package1", Slice: "myslice"}, + {Package: "test-package2", Slice: "myslice"}, }, pkgs: []*testutil.TestPackage{{ Name: "test-package1", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), + testutil.Dir(0o755, "./"), // Note that both implicit parents have different permissions. - testutil.Dir(0766, "./parent/"), - testutil.Reg(0644, "./parent/foo", "whatever"), + testutil.Dir(0o766, "./parent/"), + testutil.Reg(0o644, "./parent/foo", "whatever"), }), }, { Name: "test-package2", Data: testutil.MustMakeDeb([]testutil.TarEntry{ - testutil.Dir(0755, "./"), + testutil.Dir(0o755, "./"), // And here. - testutil.Dir(0755, "./parent/"), - testutil.Reg(0644, "./parent/bar", "whatever"), + testutil.Dir(0o755, "./parent/"), + testutil.Reg(0o644, "./parent/bar", "whatever"), }), }}, release: map[string]string{ @@ -1910,7 +1918,7 @@ var slicerTests = []slicerTest{{ logOutput: `(?s).*Warning: Path "/parent/" has diverging modes in different packages\. Please report\..*`, }, { summary: "Arch specific slice is not installed when it does not match requested arch", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, arch: "amd64", release: map[string]string{ "slices/mydir/test-package.yaml": ` @@ -1929,7 +1937,7 @@ var slicerTests = []slicerTest{{ manifestPaths: map[string]string{}, }, { summary: "Arch specific slice is installed when it matches requested arch", - slices: []setup.SliceKey{{"test-package", "myslice"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "myslice"}}, arch: "arm64", release: map[string]string{ "slices/mydir/test-package.yaml": ` @@ -1953,7 +1961,7 @@ var slicerTests = []slicerTest{{ }, }, { summary: "Transitive essential", - slices: []setup.SliceKey{{"test-package", "first"}}, + slices: []setup.SliceKey{{Package: "test-package", Slice: "first"}}, release: map[string]string{ "slices/mydir/test-package.yaml": ` package: test-package @@ -2049,9 +2057,9 @@ func runSlicerTests(s *S, c *C, tests []slicerTest) { releaseDir := c.MkDir() for path, data := range test.release { fpath := filepath.Join(releaseDir, path) - err := os.MkdirAll(filepath.Dir(fpath), 0755) + err := os.MkdirAll(filepath.Dir(fpath), 0o755) c.Assert(err, IsNil) - err = os.WriteFile(fpath, testutil.Reindent(data), 0644) + err = os.WriteFile(fpath, testutil.Reindent(data), 0o644) c.Assert(err, IsNil) } @@ -2229,9 +2237,9 @@ func readManifest(c *C, targetDir, manifestPath string) *manifest.Manifest { // in the manifest itself. s, err := os.Stat(path.Join(targetDir, manifestPath)) c.Assert(err, IsNil) - c.Assert(s.Mode(), Equals, fs.FileMode(0644)) + c.Assert(s.Mode(), Equals, fs.FileMode(0o644)) err = mfest.IteratePaths(manifestPath, func(p *manifest.Path) error { - c.Assert(p.Mode, Equals, fmt.Sprintf("%#o", fs.FileMode(0644))) + c.Assert(p.Mode, Equals, fmt.Sprintf("%#o", fs.FileMode(0o644))) return nil }) c.Assert(err, IsNil)