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
21 changes: 19 additions & 2 deletions api/mirror.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,8 +520,10 @@ type mirrorUpdateParams struct {
ForceUpdate bool ` json:"ForceUpdate"`
// Set "true" to skip downloading already downloaded packages
SkipExistingPackages bool ` json:"SkipExistingPackages"`
// Set "true" to download only the latest version per package/architecture
// Set "true" to download only the latest version per package/architecture (same as `MaxVersions`: 1)
LatestOnly bool ` json:"LatestOnly"`
// Download only the N most recent versions per package/architecture, 0 means all versions
MaxVersions int ` json:"MaxVersions" example:"5"`
}

// @Summary Update Mirror
Expand Down Expand Up @@ -573,6 +575,14 @@ func apiMirrorsUpdate(c *gin.Context) {
}
}

if b.MaxVersions < 0 {
AbortWithJSONError(c, 400, fmt.Errorf("MaxVersions should be a positive number"))
return
}
if b.LatestOnly && b.MaxVersions == 0 {
b.MaxVersions = 1
}

verifier, err := getVerifier(b.Keyrings)
if err != nil {
AbortWithJSONError(c, 400, fmt.Errorf("unable to initialize GPG verifier: %s", err))
Expand Down Expand Up @@ -639,8 +649,15 @@ func apiMirrorsUpdate(c *gin.Context) {
}
}

if b.MaxVersions > 0 {
_, _, err := remote.FilterLatest(b.MaxVersions)
if err != nil {
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to update: %s", err)
}
}

queue, downloadSize, err := remote.BuildDownloadQueue(context.PackagePool(), taskCollectionFactory.PackageCollection(),
taskCollectionFactory.ChecksumCollection(nil), b.SkipExistingPackages, b.LatestOnly)
taskCollectionFactory.ChecksumCollection(nil), b.SkipExistingPackages)
if err != nil {
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to update: %s", err)
}
Expand Down
27 changes: 24 additions & 3 deletions cmd/mirror_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {

name := args[0]

maxVersions := context.Flags().Lookup("max-versions").Value.Get().(int)
if maxVersions < 0 {
return fmt.Errorf("unable to update: -max-versions should be a positive number")
}
if maxVersions == 0 && context.Flags().Lookup("latest").Value.Get().(bool) {
maxVersions = 1
}

collectionFactory := context.NewCollectionFactory()
repo, err := collectionFactory.RemoteRepoCollection().ByName(name)
if err != nil {
Expand Down Expand Up @@ -90,17 +98,29 @@ func aptlyMirrorUpdate(cmd *commander.Command, args []string) error {
context.Progress().Printf("Packages filtered: %d -> %d.\n", oldLen, newLen)
}

if maxVersions > 0 {
var oldLen, newLen int
oldLen, newLen, err = repo.FilterLatest(maxVersions)
if err != nil {
return fmt.Errorf("unable to update: %s", err)
}
versionWord := "versions"
if maxVersions == 1 {
versionWord = "version"
}
context.Progress().Printf("Keeping at most %d %s of each package: %d -> %d.\n", maxVersions, versionWord, oldLen, newLen)
}

var (
downloadSize int64
queue []deb.PackageDownloadTask
)

skipExistingPackages := context.Flags().Lookup("skip-existing-packages").Value.Get().(bool)
latestOnly := context.Flags().Lookup("latest").Value.Get().(bool)

context.Progress().Printf("Building download queue...\n")
queue, downloadSize, err = repo.BuildDownloadQueue(context.PackagePool(), collectionFactory.PackageCollection(),
collectionFactory.ChecksumCollection(nil), skipExistingPackages, latestOnly)
collectionFactory.ChecksumCollection(nil), skipExistingPackages)

if err != nil {
return fmt.Errorf("unable to update: %s", err)
Expand Down Expand Up @@ -302,7 +322,8 @@ Example:
cmd.Flag.Bool("ignore-checksums", false, "ignore checksum mismatches while downloading package files and metadata")
cmd.Flag.Bool("ignore-signatures", false, "disable verification of Release file signatures")
cmd.Flag.Bool("skip-existing-packages", false, "do not check file existence for packages listed in the internal database of the mirror")
cmd.Flag.Bool("latest", false, "download only latest version of each package (per architecture)")
cmd.Flag.Bool("latest", false, "download only latest version of each package (per architecture), same as -max-versions=1")
cmd.Flag.Int("max-versions", 0, "download only the N most recent versions of each package (per architecture), 0 means all versions")
cmd.Flag.Int64("download-limit", 0, "limit download speed (kbytes/sec)")
cmd.Flag.String("downloader", "default", "downloader to use (e.g. grab)")
cmd.Flag.Int("max-tries", 1, "max download tries till process fails with download error")
Expand Down
3 changes: 2 additions & 1 deletion completion.d/_aptly
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ local keyring="*-keyring=[gpg keyring to use when verifying Release file (could
$keyring \
"-max-tries=[max download tries till process fails with download error]:number: " \
"-skip-existing-packages=[do not check file existence for packages listed in the internal database of the mirror]:$bool" \
"-latest=[download only latest version of each package (per architecture)]:$bool" \
"-latest=[download only latest version of each package (per architecture), same as -max-versions=1]:$bool" \
"-max-versions=[download only the N most recent versions of each package (per architecture), 0 means all versions]:number: " \
"(-)2:mirror name:$mirrors"
;;
rename)
Expand Down
2 changes: 1 addition & 1 deletion completion.d/aptly
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ _aptly()
"update")
if [[ $numargs -eq 0 ]]; then
if [[ "$cur" == -* ]]; then
COMPREPLY=($(compgen -W "-force -download-limit= -downloader= -ignore-checksums -ignore-signatures -keyring= -skip-existing-packages -latest" -- ${cur}))
COMPREPLY=($(compgen -W "-force -download-limit= -downloader= -ignore-checksums -ignore-signatures -keyring= -skip-existing-packages -latest -max-versions=" -- ${cur}))
else
COMPREPLY=($(compgen -W "$(__aptly_mirror_list)" -- ${cur}))
fi
Expand Down
38 changes: 27 additions & 11 deletions deb/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,32 +173,48 @@ func (l *PackageList) ForEach(handler func(*Package) error) error {
}

// FilterLatest creates a copy of the package list containing only the
// latest version for each package name/architecture pair.
func (l *PackageList) FilterLatest() (*PackageList, error) {
// count latest versions for each package name/architecture pair.
func (l *PackageList) FilterLatest(count int) (*PackageList, error) {
if l == nil {
return nil, fmt.Errorf("package list is nil")
}

filtered := make(map[string]*Package, l.Len())
if count < 1 {
return nil, fmt.Errorf("number of latest versions should be at least 1, got %d", count)
}

groups := make(map[string][]*Package, l.Len())

err := l.ForEach(func(p *Package) error {
key := p.Architecture + "|" + p.Name

if existing, found := filtered[key]; !found || CompareVersions(p.Version, existing.Version) > 0 {
filtered[key] = p
}
groups[key] = append(groups[key], p)

return nil
})
if err != nil {
return nil, err
}

result := NewPackageListWithDuplicates(l.duplicatesAllowed, len(filtered))
result := NewPackageListWithDuplicates(l.duplicatesAllowed, len(groups)*count)

for _, pkgs := range groups {
// sort from latest to oldest
sort.Slice(pkgs, func(i, j int) bool {
return CompareVersions(pkgs[i].Version, pkgs[j].Version) > 0
})

for _, pkg := range filtered {
if err = result.Add(pkg); err != nil {
return nil, err
versions := 0
for i, p := range pkgs {
if i == 0 || CompareVersions(p.Version, pkgs[i-1].Version) != 0 {
versions++
if versions > count {
break
}
}

if err = result.Add(p); err != nil {
return nil, err
}
}
}

Expand Down
48 changes: 45 additions & 3 deletions deb/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,13 +524,55 @@ func (s *PackageListSuite) TestFilterLatest(c *C) {
sharedPkg := NewPackageFromControlFile(shared)
_ = list.Add(sharedPkg)

filtered, err := list.FilterLatest()
filtered, err := list.FilterLatest(1)
c.Assert(err, IsNil)
c.Assert(filtered.Len(), Equals, 2)
c.Check(filtered.Has(newerPkg), Equals, true)
c.Check(filtered.Has(sharedPkg), Equals, true)
}

func (s *PackageListSuite) TestFilterLatestMultiple(c *C) {
list := NewPackageList()

pkgs := make([]*Package, 0, 4)
for _, version := range []string{"2.0", "1.0", "10.0", "1:0.1"} {
stanza := packageStanza.Copy()
stanza["Version"] = version
pkg := NewPackageFromControlFile(stanza)
_ = list.Add(pkg)
pkgs = append(pkgs, pkg)
}

shared := packageStanza.Copy()
shared["Architecture"] = ArchitectureAll
shared["Version"] = "3.0"
shared["Package"] = "shared"
sharedPkg := NewPackageFromControlFile(shared)
_ = list.Add(sharedPkg)

filtered, err := list.FilterLatest(3)
c.Assert(err, IsNil)
c.Assert(filtered.Len(), Equals, 4)
c.Check(filtered.Has(pkgs[0]), Equals, true)
c.Check(filtered.Has(pkgs[1]), Equals, false)
c.Check(filtered.Has(pkgs[2]), Equals, true)
c.Check(filtered.Has(pkgs[3]), Equals, true)
c.Check(filtered.Has(sharedPkg), Equals, true)

// fewer versions available than requested: everything is kept
filtered, err = list.FilterLatest(5)
c.Assert(err, IsNil)
c.Check(filtered.Len(), Equals, 5)
}

func (s *PackageListSuite) TestFilterLatestInvalidCount(c *C) {
list := NewPackageList()

filtered, err := list.FilterLatest(0)
c.Assert(err, ErrorMatches, "number of latest versions should be at least 1, got 0")
c.Assert(filtered, IsNil)
}

func (s *PackageListSuite) TestFilterLatestPreservesDuplicatesFlag(c *C) {
list := NewPackageListWithDuplicates(true, 2)

Expand All @@ -540,15 +582,15 @@ func (s *PackageListSuite) TestFilterLatestPreservesDuplicatesFlag(c *C) {
another["Version"] = "7.41-1"
_ = list.Add(NewPackageFromControlFile(another))

filtered, err := list.FilterLatest()
filtered, err := list.FilterLatest(1)
c.Assert(err, IsNil)
c.Assert(filtered.duplicatesAllowed, Equals, true)
}

func (s *PackageListSuite) TestFilterLatestNil(c *C) {
var list *PackageList

filtered, err := list.FilterLatest()
filtered, err := list.FilterLatest(1)
c.Assert(err, ErrorMatches, "package list is nil")
c.Assert(filtered, IsNil)
}
22 changes: 15 additions & 7 deletions deb/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -698,18 +698,26 @@ func (repo *RemoteRepo) ApplyFilter(dependencyOptions int, filterQuery PackageQu
return
}

// BuildDownloadQueue builds queue, discards current PackageList
func (repo *RemoteRepo) BuildDownloadQueue(packagePool aptly.PackagePool, packageCollection *PackageCollection, checksumStorage aptly.ChecksumStorage, skipExistingPackages, latestOnly bool) (queue []PackageDownloadTask, downloadSize int64, err error) {
// FilterLatest keeps only count latest versions of each package (per architecture)
func (repo *RemoteRepo) FilterLatest(count int) (oldLen, newLen int, err error) {
if repo.packageList == nil {
err = fmt.Errorf("package list is empty, please (re)download package indexes")
return
}

if latestOnly {
repo.packageList, err = repo.packageList.FilterLatest()
if err != nil {
return
}
oldLen = repo.packageList.Len()
repo.packageList, err = repo.packageList.FilterLatest(count)
if repo.packageList != nil {
newLen = repo.packageList.Len()
}
return
}

// BuildDownloadQueue builds queue, discards current PackageList
func (repo *RemoteRepo) BuildDownloadQueue(packagePool aptly.PackagePool, packageCollection *PackageCollection, checksumStorage aptly.ChecksumStorage, skipExistingPackages bool) (queue []PackageDownloadTask, downloadSize int64, err error) {
if repo.packageList == nil {
err = fmt.Errorf("package list is empty, please (re)download package indexes")
return
}

queue = make([]PackageDownloadTask, 0, repo.packageList.Len())
Expand Down
Loading
Loading