-
Notifications
You must be signed in to change notification settings - Fork 65
feat: abstract interactions with archive/store #311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c5d397c
f79521e
a2e22f2
2eb85d0
3004a6e
ed0e070
268501e
107103d
1249e7b
4cfed24
7b124b3
076ff37
eaddfd9
bb39d06
b930f97
8c24bda
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| package slicer | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "io" | ||
| "slices" | ||
|
|
||
| "github.com/canonical/chisel/internal/archive" | ||
| "github.com/canonical/chisel/internal/manifestutil" | ||
| "github.com/canonical/chisel/internal/setup" | ||
| ) | ||
|
|
||
| type Fetcher interface { | ||
| Arch() string | ||
| Fetch() (io.ReadSeekCloser, manifestutil.PackageInfo, error) | ||
| } | ||
|
|
||
| var ( | ||
| _ Fetcher = (*debFetcher)(nil) | ||
| _ Fetcher = (*binFetcher)(nil) | ||
| ) | ||
|
|
||
| // debFetcher fetches deb packages from an archive. | ||
| type debFetcher struct { | ||
| archive archive.Archive | ||
| name string | ||
| } | ||
|
|
||
| func (d *debFetcher) Arch() string { | ||
| return d.archive.Options().Arch | ||
| } | ||
|
|
||
| func (d *debFetcher) Fetch() (io.ReadSeekCloser, manifestutil.PackageInfo, error) { | ||
| return d.archive.Fetch(d.name) | ||
| } | ||
|
|
||
| // binFetcher fetches bin packages from a store. | ||
| type binFetcher struct { | ||
| arch string | ||
| name string | ||
| store string | ||
| } | ||
|
|
||
| func (b *binFetcher) Arch() string { | ||
| return b.arch | ||
| } | ||
|
|
||
| func (b *binFetcher) Fetch() (io.ReadSeekCloser, manifestutil.PackageInfo, error) { | ||
| return nil, nil, fmt.Errorf("cannot fetch package %q from store %q: not implemented", b.name, b.store) | ||
| } | ||
|
|
||
| // selectPkgFetchers determines the fetcher for each package in the selection. | ||
| // For packages from an archive it selects the highest priority archive | ||
| // containing the package unless a particular archive is pinned within the | ||
| // package slices file. For packages from a store it selects the store | ||
| // named in the package slices file. It returns a map of Fetcher indexed | ||
| // by package names. | ||
| func selectPkgFetchers(archives map[string]archive.Archive, selection *setup.Selection) (map[string]Fetcher, error) { | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Note to reviewer]: I think it makes sense to have this function here, but it is a refactor (and rename) of |
||
| 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 | ||
| }) | ||
|
|
||
| fetchers := make(map[string]Fetcher) | ||
| for _, s := range selection.Slices { | ||
| if _, ok := fetchers[s.Package]; ok { | ||
| continue | ||
| } | ||
| pkg := selection.Release.Packages[s.Package] | ||
| if pkg.Store != "" { | ||
| fetchers[pkg.Name] = &binFetcher{ | ||
| 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) | ||
| } | ||
| fetchers[pkg.Name] = &debFetcher{ | ||
| archive: chosen, | ||
| name: pkg.RealName, | ||
| } | ||
| } | ||
|
|
||
| return fetchers, nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Note to reviewer]: This is a smell that having PackageInfo in manifestutil shows its limit, as already discussed.
I considered moving it to the
slicerpkg but that would be wrong becausemanifestutil(a leaf package) would need to import it.I also considered creating a standalone package with fetch.go and
PackageInfobut that would couple manifestutil with thisfetchpackage and it looks wrong too.So the next best solution looks to be a small
pkginfoleaf package, holding the interface. Let me know what you think about it and I will proceed with it in a follow-up PR (to not mix refactor and feature in this one) if we agree on a refactor.