Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 5 additions & 57 deletions internal/slicer/slicer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/canonical/chisel/internal/manifestutil"
"github.com/canonical/chisel/internal/scripts"
"github.com/canonical/chisel/internal/setup"
"github.com/canonical/chisel/internal/source"
"github.com/canonical/chisel/internal/tarball"
)

Expand Down Expand Up @@ -90,7 +91,7 @@ func Run(options *RunOptions) error {
targetDir = filepath.Join(dir, targetDir)
}

pkgArchive, err := selectPkgArchives(options.Archives, options.Selection)
pkgSources, err := source.Resolve(options.Archives, options.Selection)
if err != nil {
return err
}
Expand All @@ -108,7 +109,7 @@ func Run(options *RunOptions) error {
extractPackage = make(map[string][]tarball.ExtractInfo)
extract[slice.Package] = extractPackage
}
arch := pkgArchive[slice.Package].Options().Arch
arch := pkgSources[slice.Package].Arch()
for targetPath, pathInfo := range slice.Contents {
if targetPath == "" {
continue
Expand Down Expand Up @@ -153,7 +154,7 @@ func Run(options *RunOptions) error {
continue
}
pkg := options.Selection.Release.Packages[slice.Package]
reader, info, err := pkgArchive[slice.Package].Fetch(pkg.RealName)
reader, info, err := pkgSources[pkg.Name].Fetch()
if err != nil {
return err
}
Expand Down Expand Up @@ -270,7 +271,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 := pkgSources[slice.Package].Arch()
for relPath, pathInfo := range slice.Contents {
if len(pathInfo.Arch) > 0 && !slices.Contains(pathInfo.Arch, arch) {
continue
Expand Down Expand Up @@ -489,56 +490,3 @@ func createFile(targetDir, relPath string, pathInfo setup.PathInfo) (*fsutil.Ent
MakeParents: true,
})
}

// selectPkgArchives selects the highest priority archive containing the package
// unless a particular archive is pinned within the slice definition file. It
// returns a map of archives indexed by package names.
func selectPkgArchives(archives map[string]archive.Archive, selection *setup.Selection) (map[string]archive.Archive, error) {
sortedArchives := make([]*setup.Archive, 0, len(selection.Release.Archives))
for _, archive := range selection.Release.Archives {
if archive.Priority < 0 {
// Ignore negative priority archives unless a package specifically
// asks for it with the "archive" field.
continue
}
sortedArchives = append(sortedArchives, archive)
}
slices.SortFunc(sortedArchives, func(a, b *setup.Archive) int {
return b.Priority - a.Priority
})

pkgArchive := make(map[string]archive.Archive)
for _, s := range selection.Slices {
if _, ok := pkgArchive[s.Package]; ok {
continue
}
pkg := selection.Release.Packages[s.Package]

if pkg.Store != "" {
return nil, fmt.Errorf("cannot fetch package %q from store %q: not implemented", pkg.Name, pkg.Store)
}

var candidates []*setup.Archive
if pkg.Archive == "" {
// If the package has not pinned any archive, choose the highest
// priority archive in which the package exists.
candidates = sortedArchives
} else {
candidates = []*setup.Archive{selection.Release.Archives[pkg.Archive]}
}

var chosen archive.Archive
for _, archiveInfo := range candidates {
archive := archives[archiveInfo.Name]
if archive != nil && archive.Exists(pkg.RealName) {
chosen = archive
break
}
}
if chosen == nil {
return nil, fmt.Errorf("cannot find package %q in archive(s)", pkg.RealName)
}
pkgArchive[pkg.Name] = chosen
}
return pkgArchive, nil
}
24 changes: 16 additions & 8 deletions internal/slicer/slicer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1979,21 +1979,29 @@ var slicerTests = []slicerTest{{
"/dir/file": "file 0644 cc55e2ec {test-package_third}",
},
}, {
summary: "Store package is not yet implemented",
slices: []setup.SliceKey{{"bin-curl", "bin"}},
summary: "Store package fetching not yet implemented",
slices: []setup.SliceKey{{"test-package", "myslice"}, {"bin-store-pkg", "myslice"}},
arch: "amd64",
release: map[string]string{
"chisel.yaml": testutil.DefaultChiselYamlWithStores,
"slices/curl.yaml": `
package: curl
"slices/mydir/test-package.yaml": `
package: test-package
slices:
myslice:
contents:
/dir/file:
`,
"slices/mydir/store-pkg.yaml": `
package: store-pkg
store: bin
default-track: latest
default-track: 3.1
slices:
bin:
myslice:
contents:
/usr/bin/curl:
/dir/store-file:
`,
},
error: `cannot fetch package "bin-curl" from store "bin": not implemented`,
error: `cannot fetch package "bin-store-pkg" from store "bin": not implemented`,
}}

func (s *S) TestRun(c *C) {
Expand Down
110 changes: 110 additions & 0 deletions internal/source/source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package source

import (
"fmt"
"io"
"slices"

"github.com/canonical/chisel/internal/archive"
"github.com/canonical/chisel/internal/setup"
)

// Source is a resolved package source, abstracting over archives and stores.
type Source interface {
Arch() string
Fetch() (io.ReadSeekCloser, *archive.PackageInfo, error)
}

// archiveSource adapts an archive.Archive to the Source interface for a
// specific package.
type archiveSource struct {
archive archive.Archive
name string
}

func (a *archiveSource) Arch() string {
return a.archive.Options().Arch
}

func (a *archiveSource) Fetch() (io.ReadSeekCloser, *archive.PackageInfo, error) {
return a.archive.Fetch(a.name)
}

// storeSource adapts a store to the Source interface for a specific package.
type storeSource struct {
arch string
name string
store string
}

func (s *storeSource) Arch() string {
return s.arch
}

func (s *storeSource) Fetch() (io.ReadSeekCloser, *archive.PackageInfo, error) {
return nil, nil, fmt.Errorf("cannot fetch package %q from store %q: not implemented", s.name, s.store)
}

// Resolve determines the source for each package in the selection.
// For archive packages it selects the highest priority archive containing the
// package unless a particular archive is pinned within the slice definition
// file. For store packages it records a store source. It returns a map of
// Source indexed by package names.
func Resolve(archives map[string]archive.Archive, selection *setup.Selection) (map[string]Source, error) {
sortedArchives := make([]*setup.Archive, 0, len(selection.Release.Archives))
for _, archive := range selection.Release.Archives {
if archive.Priority < 0 {
// Ignore negative priority archives unless a package specifically
// asks for it with the "archive" field.
continue
}
sortedArchives = append(sortedArchives, archive)
}
slices.SortFunc(sortedArchives, func(a, b *setup.Archive) int {
return b.Priority - a.Priority
})

sources := make(map[string]Source)
for _, s := range selection.Slices {
if _, ok := sources[s.Package]; ok {
continue
}
pkg := selection.Release.Packages[s.Package]
if pkg.Store != "" {
sources[pkg.Name] = &storeSource{
name: pkg.Name,
store: pkg.Store,
// TODO: populate arch, track and risk when implementing
// fetching from the store.
}
continue
}

var candidates []*setup.Archive
if pkg.Archive == "" {
// If the package has not pinned any archive, choose the highest
// priority archive in which the package exists.
candidates = sortedArchives
} else {
candidates = []*setup.Archive{selection.Release.Archives[pkg.Archive]}
}

var chosen archive.Archive
for _, archiveInfo := range candidates {
archive := archives[archiveInfo.Name]
if archive != nil && archive.Exists(pkg.RealName) {
chosen = archive
break
}
}
if chosen == nil {
return nil, fmt.Errorf("cannot find package %q in archive(s)", pkg.RealName)
}
sources[pkg.Name] = &archiveSource{
archive: chosen,
name: pkg.RealName,
}
}

return sources, nil
}
Loading
Loading