Inplace upgrades

This commit is contained in:
Ettore Di Giacinto
2021-04-24 18:15:47 +02:00
parent 55ae67be0f
commit adcb459fd2

View File

@@ -128,6 +128,8 @@ func packsToList(p pkg.Packages) string {
for _, pp := range p { for _, pp := range p {
packs = append(packs, pp.HumanReadableString()) packs = append(packs, pp.HumanReadableString())
} }
sort.Strings(packs)
return strings.Join(packs, " ") return strings.Join(packs, " ")
} }
@@ -137,6 +139,7 @@ func matchesToList(artefacts map[string]ArtifactMatch) string {
for fingerprint, match := range artefacts { for fingerprint, match := range artefacts {
packs = append(packs, fmt.Sprintf("%s (%s)", fingerprint, match.Repository.GetName())) packs = append(packs, fmt.Sprintf("%s (%s)", fingerprint, match.Repository.GetName()))
} }
sort.Strings(packs)
return strings.Join(packs, " ") return strings.Join(packs, " ")
} }
@@ -195,7 +198,7 @@ func (l *LuetInstaller) Swap(toRemove pkg.Packages, toInstall pkg.Packages, s *S
} }
} }
return l.swap(syncedRepos, toRemoveFinal, toInstall, s, false) return l.swap(syncedRepos, toRemoveFinal, toInstall, s)
} }
func (l *LuetInstaller) computeSwap(o Option, syncedRepos Repositories, toRemove pkg.Packages, toInstall pkg.Packages, s *System) (map[string]ArtifactMatch, pkg.Packages, solver.PackagesAssertions, pkg.PackageDatabase, error) { func (l *LuetInstaller) computeSwap(o Option, syncedRepos Repositories, toRemove pkg.Packages, toInstall pkg.Packages, s *System) (map[string]ArtifactMatch, pkg.Packages, solver.PackagesAssertions, pkg.PackageDatabase, error) {
@@ -214,7 +217,7 @@ func (l *LuetInstaller) computeSwap(o Option, syncedRepos Repositories, toRemove
systemAfterChanges := &System{Database: installedtmp} systemAfterChanges := &System{Database: installedtmp}
packs, err := l.computeUninstall(o, systemAfterChanges, toRemove...) packs, err := l.computeUninstall(o, systemAfterChanges, toRemove...)
if err != nil && !l.Options.Force { if err != nil && !o.Force {
Error("Failed computing uninstall for ", packsToList(toRemove)) Error("Failed computing uninstall for ", packsToList(toRemove))
return nil, nil, nil, nil, errors.Wrap(err, "computing uninstall "+packsToList(toRemove)) return nil, nil, nil, nil, errors.Wrap(err, "computing uninstall "+packsToList(toRemove))
} }
@@ -225,14 +228,14 @@ func (l *LuetInstaller) computeSwap(o Option, syncedRepos Repositories, toRemove
} }
} }
match, packages, assertions, allRepos, err := l.computeInstall(syncedRepos, toInstall, systemAfterChanges) match, packages, assertions, allRepos, err := l.computeInstall(o, syncedRepos, toInstall, systemAfterChanges)
for _, p := range toInstall { for _, p := range toInstall {
assertions = append(assertions, solver.PackageAssert{Package: p.(*pkg.DefaultPackage), Value: true}) assertions = append(assertions, solver.PackageAssert{Package: p.(*pkg.DefaultPackage), Value: true})
} }
return match, packages, assertions, allRepos, err return match, packages, assertions, allRepos, err
} }
func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove pkg.Packages, toInstall pkg.Packages, s *System, forceNodeps bool) error { func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove pkg.Packages, toInstall pkg.Packages, s *System) error {
//forced := l.Options.Force //forced := l.Options.Force
// nodeps := l.Options.NoDeps // nodeps := l.Options.NoDeps
@@ -243,18 +246,14 @@ func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove pkg.Packages, to
// if the old A results installed in the system. This is due to the fact that // if the old A results installed in the system. This is due to the fact that
// now the solver enforces the constraints and explictly denies two packages // now the solver enforces the constraints and explictly denies two packages
// of the same version installed. // of the same version installed.
l.Options.Force = true //l.Options.Force = true
nodeps := l.Options.NoDeps
if forceNodeps {
nodeps = true
}
o := Option{ o := Option{
FullUninstall: l.Options.FullUninstall, FullUninstall: false,
Force: true, Force: true,
CheckConflicts: l.Options.CheckConflicts, CheckConflicts: false,
FullCleanUninstall: l.Options.FullCleanUninstall, FullCleanUninstall: false,
NoDeps: nodeps, NoDeps: true,
} }
match, packages, assertions, allRepos, err := l.computeSwap(o, syncedRepos, toRemove, toInstall, s) match, packages, assertions, allRepos, err := l.computeSwap(o, syncedRepos, toRemove, toInstall, s)
@@ -287,23 +286,43 @@ func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove pkg.Packages, to
return nil return nil
} }
toUninstall, uninstall, err := l.generateUninstallFn(o, s, toRemove...) // TODO: Replace with installerWorkOpts down here
if err != nil && !o.Force {
return errors.Wrap(err, "while computing uninstall") ops := l.getOpsWithOptions(toRemove, match, Option{
Force: l.Options.Force,
NoDeps: l.Options.NoDeps,
OnlyDeps: o.OnlyDeps,
RunFinalizers: false,
}, o, syncedRepos, packages, assertions, allRepos)
// toUninstall, uninstall, err := l.generateUninstallFn(o, s, toRemove...)
// if err != nil && !o.Force {
// return errors.Wrap(err, "while computing uninstall")
// }
// err = uninstall()
// if err != nil && !o.Force {
// Error("Failed uninstall for ", packsToList(toUninstall))
// return errors.Wrap(err, "uninstalling "+packsToList(toUninstall))
// }
// o = Option{
// Force: l.Options.Force,
// NoDeps: l.Options.NoDeps,
// }
// return l.install(o, syncedRepos, match, packages, assertions, allRepos, s)
err = l.runOps(ops, s)
if err != nil {
return errors.Wrap(err, "failed running installer options")
} }
err = uninstall() toFinalize, err := l.getFinalizers(allRepos, assertions, match, o.NoDeps)
if err != nil && !o.Force { if err != nil {
Error("Failed uninstall for ", packsToList(toUninstall)) return errors.Wrap(err, "failed getting package to finalize")
return errors.Wrap(err, "uninstalling "+packsToList(toUninstall))
} }
o = Option{ return s.ExecuteFinalizers(toFinalize)
Force: l.Options.Force,
NoDeps: l.Options.NoDeps,
}
return l.install(o, syncedRepos, match, packages, assertions, allRepos, s)
} }
type Option struct { type Option struct {
@@ -312,6 +331,8 @@ type Option struct {
CheckConflicts bool CheckConflicts bool
FullUninstall bool FullUninstall bool
FullCleanUninstall bool FullCleanUninstall bool
OnlyDeps bool
RunFinalizers bool
} }
type operation struct { type operation struct {
@@ -319,14 +340,23 @@ type operation struct {
Package pkg.Package Package pkg.Package
} }
type installOperation struct {
operation
Reposiories Repositories
Packages pkg.Packages
Assertions solver.PackagesAssertions
Database pkg.PackageDatabase
Matches map[string]ArtifactMatch
}
// installerOp is the operation that is sent to the // installerOp is the operation that is sent to the
// upgradeWorker's channel (todo) // upgradeWorker's channel (todo)
type installerOp struct { type installerOp struct {
Uninstall operation Uninstall operation
Install operation Install installOperation
} }
func (l *LuetInstaller) runOps(syncedRepos Repositories, ops []installerOp, p pkg.Packages, solution solver.PackagesAssertions, allRepos pkg.PackageDatabase, s *System) error { func (l *LuetInstaller) runOps(ops []installerOp, s *System) error {
all := make(chan installerOp) all := make(chan installerOp)
wg := new(sync.WaitGroup) wg := new(sync.WaitGroup)
@@ -346,44 +376,81 @@ func (l *LuetInstaller) runOps(syncedRepos Repositories, ops []installerOp, p pk
return nil return nil
} }
// TODO: Finish implementation here,
func (l *LuetInstaller) installerOpWorker(i int, wg *sync.WaitGroup, c <-chan installerOp, s *System) error { func (l *LuetInstaller) installerOpWorker(i int, wg *sync.WaitGroup, c <-chan installerOp, s *System) error {
defer wg.Done() defer wg.Done()
for p := range c { for p := range c {
if p.Uninstall.Package != nil { if p.Uninstall.Package != nil {
Debug("Replacing package inplace")
toUninstall, uninstall, err := l.generateUninstallFn(p.Uninstall.Option, s, p.Uninstall.Package) toUninstall, uninstall, err := l.generateUninstallFn(p.Uninstall.Option, s, p.Uninstall.Package)
if err != nil && !l.Options.Force { if err != nil {
return errors.Wrap(err, "while computing uninstall") Error("Failed to generate Uninstall function for" + err.Error())
continue
//return errors.Wrap(err, "while computing uninstall")
} }
err = uninstall() err = uninstall()
if err != nil && !p.Uninstall.Option.Force { if err != nil {
Error("Failed uninstall for ", packsToList(toUninstall)) Error("Failed uninstall for ", packsToList(toUninstall))
return errors.Wrap(err, "uninstalling "+packsToList(toUninstall)) continue
//return errors.Wrap(err, "uninstalling "+packsToList(toUninstall))
} }
} }
if p.Install.Package != nil {
artMatch := p.Install.Matches[p.Install.Package.GetFingerPrint()]
ass := p.Install.Assertions.Search(p.Install.Package.GetFingerPrint())
packageToInstall, _ := p.Install.Packages.Find(p.Install.Package.GetPackageName())
// return l.install(p.Install.Option, match, packages, assertions, allRepos, s) err := l.install(
p.Install.Option,
p.Install.Reposiories,
map[string]ArtifactMatch{p.Install.Package.GetFingerPrint(): artMatch},
pkg.Packages{packageToInstall},
solver.PackagesAssertions{*ass},
p.Install.Database,
s,
)
if err != nil {
Error(err)
}
}
} }
return nil return nil
} }
// checks wheter we can uninstall and install in place and compose installer worker ops // checks wheter we can uninstall and install in place and compose installer worker ops
func (l *LuetInstaller) getOpsWithOptions(toUninstall pkg.Packages, installMatch map[string]ArtifactMatch, installOpt, uninstallOpt Option) []installerOp { func (l *LuetInstaller) getOpsWithOptions(
toUninstall pkg.Packages, installMatch map[string]ArtifactMatch, installOpt, uninstallOpt Option,
syncedRepos Repositories, toInstall pkg.Packages, solution solver.PackagesAssertions, allRepos pkg.PackageDatabase) []installerOp {
resOps := []installerOp{} resOps := []installerOp{}
for _, match := range installMatch { for _, match := range installMatch {
if pack, err := toUninstall.Find(match.Package.GetPackageName()); err == nil { if pack, err := toUninstall.Find(match.Package.GetPackageName()); err == nil {
resOps = append(resOps, installerOp{ resOps = append(resOps, installerOp{
Uninstall: operation{Package: pack, Option: uninstallOpt}, Uninstall: operation{Package: pack, Option: uninstallOpt},
Install: operation{Package: match.Package, Option: installOpt}, Install: installOperation{
operation: operation{
Package: match.Package,
Option: installOpt,
},
Matches: installMatch,
Packages: toInstall,
Reposiories: syncedRepos,
Assertions: solution,
Database: allRepos,
},
}) })
} else { } else {
resOps = append(resOps, installerOp{ resOps = append(resOps, installerOp{
Install: operation{Package: match.Package, Option: installOpt}, Install: installOperation{
operation: operation{Package: match.Package, Option: installOpt},
Matches: installMatch,
Reposiories: syncedRepos,
Packages: toInstall,
Assertions: solution,
Database: allRepos,
},
}) })
} }
} }
@@ -431,15 +498,13 @@ func (l *LuetInstaller) checkAndUpgrade(r Repositories, s *System) error {
Info("By going forward, you are also accepting the licenses of the packages that you are going to install in your system.") Info("By going forward, you are also accepting the licenses of the packages that you are going to install in your system.")
if Ask() { if Ask() {
l.Options.Ask = false // Don't prompt anymore l.Options.Ask = false // Don't prompt anymore
return l.swap(r, uninstall, toInstall, s, true) return l.swap(r, uninstall, toInstall, s)
} else { } else {
return errors.New("Aborted by user") return errors.New("Aborted by user")
} }
} }
Spinner(32) return l.swap(r, uninstall, toInstall, s)
defer SpinnerStop()
return l.swap(r, uninstall, toInstall, s, true)
} }
func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error { func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error {
@@ -455,7 +520,13 @@ func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error {
} }
} }
match, packages, assertions, allRepos, err := l.computeInstall(syncedRepos, cp, s) o := Option{
NoDeps: l.Options.NoDeps,
Force: l.Options.Force,
OnlyDeps: l.Options.OnlyDeps,
RunFinalizers: true,
}
match, packages, assertions, allRepos, err := l.computeInstall(o, syncedRepos, cp, s)
if err != nil { if err != nil {
return err return err
} }
@@ -487,10 +558,7 @@ func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error {
} }
} }
Info("Packages that are going to be installed in the system: \n ", Green(matchesToList(match)).BgBlack().String()) Info("Packages that are going to be installed in the system: \n ", Green(matchesToList(match)).BgBlack().String())
o := Option{
NoDeps: l.Options.NoDeps,
Force: l.Options.Force,
}
if l.Options.Ask { if l.Options.Ask {
Info("By going forward, you are also accepting the licenses of the packages that you are going to install in your system.") Info("By going forward, you are also accepting the licenses of the packages that you are going to install in your system.")
if Ask() { if Ask() {
@@ -575,7 +643,7 @@ func (l *LuetInstaller) Reclaim(s *System) error {
return nil return nil
} }
func (l *LuetInstaller) computeInstall(syncedRepos Repositories, cp pkg.Packages, s *System) (map[string]ArtifactMatch, pkg.Packages, solver.PackagesAssertions, pkg.PackageDatabase, error) { func (l *LuetInstaller) computeInstall(o Option, syncedRepos Repositories, cp pkg.Packages, s *System) (map[string]ArtifactMatch, pkg.Packages, solver.PackagesAssertions, pkg.PackageDatabase, error) {
var p pkg.Packages var p pkg.Packages
toInstall := map[string]ArtifactMatch{} toInstall := map[string]ArtifactMatch{}
allRepos := pkg.NewInMemoryDatabase(false) allRepos := pkg.NewInMemoryDatabase(false)
@@ -608,11 +676,11 @@ func (l *LuetInstaller) computeInstall(syncedRepos Repositories, cp pkg.Packages
var packagesToInstall pkg.Packages var packagesToInstall pkg.Packages
var err error var err error
if !l.Options.NoDeps { if !o.NoDeps {
solv := solver.NewResolver(solver.Options{Type: l.Options.SolverOptions.Implementation, Concurrency: l.Options.Concurrency}, s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver()) solv := solver.NewResolver(solver.Options{Type: l.Options.SolverOptions.Implementation, Concurrency: l.Options.Concurrency}, s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
solution, err = solv.Install(p) solution, err = solv.Install(p)
/// TODO: PackageAssertions needs to be a map[fingerprint]pack so lookup is in O(1) /// TODO: PackageAssertions needs to be a map[fingerprint]pack so lookup is in O(1)
if err != nil && !l.Options.Force { if err != nil && !o.Force {
return toInstall, p, solution, allRepos, errors.Wrap(err, "Failed solving solution for package") return toInstall, p, solution, allRepos, errors.Wrap(err, "Failed solving solution for package")
} }
// Gathers things to install // Gathers things to install
@@ -625,7 +693,7 @@ func (l *LuetInstaller) computeInstall(syncedRepos Repositories, cp pkg.Packages
packagesToInstall = append(packagesToInstall, assertion.Package) packagesToInstall = append(packagesToInstall, assertion.Package)
} }
} }
} else if !l.Options.OnlyDeps { } else if !o.OnlyDeps {
for _, currentPack := range p { for _, currentPack := range p {
if _, err := s.Database.FindPackage(currentPack); err == nil { if _, err := s.Database.FindPackage(currentPack); err == nil {
// skip matching if it is installed already // skip matching if it is installed already
@@ -660,6 +728,46 @@ func (l *LuetInstaller) computeInstall(syncedRepos Repositories, cp pkg.Packages
return toInstall, p, solution, allRepos, nil return toInstall, p, solution, allRepos, nil
} }
func (l *LuetInstaller) getFinalizers(allRepos pkg.PackageDatabase, solution solver.PackagesAssertions, toInstall map[string]ArtifactMatch, nodeps bool) ([]pkg.Package, error) {
var toFinalize []pkg.Package
if !nodeps {
// TODO: Lower those errors as warning
for _, w := range toInstall {
// Finalizers needs to run in order and in sequence.
ordered, err := solution.Order(allRepos, w.Package.GetFingerPrint())
if err != nil {
return toFinalize, errors.Wrap(err, "While order a solution for "+w.Package.HumanReadableString())
}
ORDER:
for _, ass := range ordered {
if ass.Value {
installed, ok := toInstall[ass.Package.GetFingerPrint()]
if !ok {
// It was a dep already installed in the system, so we can skip it safely
continue ORDER
}
treePackage, err := installed.Repository.GetTree().GetDatabase().FindPackage(ass.Package)
if err != nil {
return toFinalize, errors.Wrap(err, "Error getting package "+ass.Package.HumanReadableString())
}
toFinalize = append(toFinalize, treePackage)
}
}
}
} else {
for _, c := range toInstall {
treePackage, err := c.Repository.GetTree().GetDatabase().FindPackage(c.Package)
if err != nil {
return toFinalize, errors.Wrap(err, "Error getting package "+c.Package.HumanReadableString())
}
toFinalize = append(toFinalize, treePackage)
}
}
return toFinalize, nil
}
func (l *LuetInstaller) install(o Option, syncedRepos Repositories, toInstall map[string]ArtifactMatch, p pkg.Packages, solution solver.PackagesAssertions, allRepos pkg.PackageDatabase, s *System) error { func (l *LuetInstaller) install(o Option, syncedRepos Repositories, toInstall map[string]ArtifactMatch, p pkg.Packages, solution solver.PackagesAssertions, allRepos pkg.PackageDatabase, s *System) error {
// Install packages into rootfs in parallel. // Install packages into rootfs in parallel.
if err := l.download(syncedRepos, toInstall); err != nil { if err := l.download(syncedRepos, toInstall); err != nil {
@@ -694,41 +802,14 @@ func (l *LuetInstaller) install(o Option, syncedRepos Repositories, toInstall ma
} }
bus.Manager.Publish(bus.EventPackageInstall, c) bus.Manager.Publish(bus.EventPackageInstall, c)
} }
var toFinalize []pkg.Package
if !o.NoDeps {
// TODO: Lower those errors as warning
for _, w := range p {
// Finalizers needs to run in order and in sequence.
ordered, err := solution.Order(allRepos, w.GetFingerPrint())
if err != nil {
return errors.Wrap(err, "While order a solution for "+w.HumanReadableString())
}
ORDER:
for _, ass := range ordered {
if ass.Value {
installed, ok := toInstall[ass.Package.GetFingerPrint()]
if !ok {
// It was a dep already installed in the system, so we can skip it safely
continue ORDER
}
treePackage, err := installed.Repository.GetTree().GetDatabase().FindPackage(ass.Package)
if err != nil {
return errors.Wrap(err, "Error getting package "+ass.Package.HumanReadableString())
}
toFinalize = append(toFinalize, treePackage) if !o.RunFinalizers {
} return nil
} }
} toFinalize, err := l.getFinalizers(allRepos, solution, toInstall, o.NoDeps)
} else { if err != nil {
for _, c := range toInstall { return errors.Wrap(err, "failed getting package to finalize")
treePackage, err := c.Repository.GetTree().GetDatabase().FindPackage(c.Package)
if err != nil {
return errors.Wrap(err, "Error getting package "+c.Package.HumanReadableString())
}
toFinalize = append(toFinalize, treePackage)
}
} }
return s.ExecuteFinalizers(toFinalize) return s.ExecuteFinalizers(toFinalize)
@@ -906,10 +987,10 @@ func (l *LuetInstaller) computeUninstall(o Option, s *System, packs ...pkg.Packa
// Get installed definition // Get installed definition
checkConflicts := o.CheckConflicts checkConflicts := o.CheckConflicts
full := o.FullUninstall full := o.FullUninstall
if o.Force == true { // IF forced, we want to remove the package and all its requires // if o.Force == true { // IF forced, we want to remove the package and all its requires
checkConflicts = false // checkConflicts = false
full = false // full = false
} // }
// Create a temporary DB with the installed packages // Create a temporary DB with the installed packages
// so the solver is much faster finding the deptree // so the solver is much faster finding the deptree
@@ -919,7 +1000,7 @@ func (l *LuetInstaller) computeUninstall(o Option, s *System, packs ...pkg.Packa
return toUninstall, errors.Wrap(err, "Failed create temporary in-memory db") return toUninstall, errors.Wrap(err, "Failed create temporary in-memory db")
} }
if !l.Options.NoDeps { if !o.NoDeps {
solv := solver.NewResolver(solver.Options{Type: l.Options.SolverOptions.Implementation, Concurrency: l.Options.Concurrency}, installedtmp, installedtmp, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver()) solv := solver.NewResolver(solver.Options{Type: l.Options.SolverOptions.Implementation, Concurrency: l.Options.Concurrency}, installedtmp, installedtmp, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
var solution pkg.Packages var solution pkg.Packages
var err error var err error