Add --nodeps, --onlydeps and --force to Installer

This allows also to use it on upgrade and uninstall as it exposes the Installer options to the CLI.

See #48, #49
This commit is contained in:
Ettore Di Giacinto 2020-02-18 18:37:56 +01:00
parent 2ce427d601
commit 4648dedb55
No known key found for this signature in database
GPG Key ID: 1ADA699B145A2D1C
4 changed files with 119 additions and 54 deletions

View File

@ -39,6 +39,10 @@ var installCmd = &cobra.Command{
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount")) LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate")) LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts")) LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
LuetCfg.Viper.BindPFlag("onlydeps", cmd.Flags().Lookup("onlydeps"))
LuetCfg.Viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps"))
LuetCfg.Viper.BindPFlag("force", cmd.Flags().Lookup("force"))
}, },
Long: `Install packages in parallel`, Long: `Install packages in parallel`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
@ -83,6 +87,9 @@ var installCmd = &cobra.Command{
discount := LuetCfg.Viper.GetFloat64("solver.discount") discount := LuetCfg.Viper.GetFloat64("solver.discount")
rate := LuetCfg.Viper.GetFloat64("solver.rate") rate := LuetCfg.Viper.GetFloat64("solver.rate")
attempts := LuetCfg.Viper.GetInt("solver.max_attempts") attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
force := LuetCfg.Viper.GetBool("force")
nodeps := LuetCfg.Viper.GetBool("nodeps")
onlydeps := LuetCfg.Viper.GetBool("onlydeps")
LuetCfg.GetSolverOptions().Type = stype LuetCfg.GetSolverOptions().Type = stype
LuetCfg.GetSolverOptions().LearnRate = float32(rate) LuetCfg.GetSolverOptions().LearnRate = float32(rate)
@ -91,7 +98,13 @@ var installCmd = &cobra.Command{
Debug("Solver", LuetCfg.GetSolverOptions().CompactString()) Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{Concurrency: LuetCfg.GetGeneral().Concurrency, SolverOptions: *LuetCfg.GetSolverOptions()}) inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{
Concurrency: LuetCfg.GetGeneral().Concurrency,
SolverOptions: *LuetCfg.GetSolverOptions(),
NoDeps: nodeps,
Force: force,
OnlyDeps: onlydeps,
})
inst.Repositories(repos) inst.Repositories(repos)
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" { if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
@ -119,6 +132,9 @@ func init() {
installCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate") installCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
installCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate") installCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
installCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts") installCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
installCmd.Flags().Bool("nodeps", false, "Don't consider package dependencies (harmful!)")
installCmd.Flags().Bool("onlydeps", false, "Consider **only** package dependencies")
installCmd.Flags().Bool("force", false, "Skip errors and keep going (potentially harmful)")
RootCmd.AddCommand(installCmd) RootCmd.AddCommand(installCmd)
} }

View File

@ -39,6 +39,8 @@ var uninstallCmd = &cobra.Command{
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount")) LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate")) LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts")) LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
LuetCfg.Viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps"))
LuetCfg.Viper.BindPFlag("force", cmd.Flags().Lookup("force"))
}, },
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
var systemDB pkg.PackageDatabase var systemDB pkg.PackageDatabase
@ -67,6 +69,8 @@ var uninstallCmd = &cobra.Command{
discount := LuetCfg.Viper.GetFloat64("solver.discount") discount := LuetCfg.Viper.GetFloat64("solver.discount")
rate := LuetCfg.Viper.GetFloat64("solver.rate") rate := LuetCfg.Viper.GetFloat64("solver.rate")
attempts := LuetCfg.Viper.GetInt("solver.max_attempts") attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
force := LuetCfg.Viper.GetBool("force")
nodeps := LuetCfg.Viper.GetBool("nodeps")
LuetCfg.GetSolverOptions().Type = stype LuetCfg.GetSolverOptions().Type = stype
LuetCfg.GetSolverOptions().LearnRate = float32(rate) LuetCfg.GetSolverOptions().LearnRate = float32(rate)
@ -75,7 +79,12 @@ var uninstallCmd = &cobra.Command{
Debug("Solver", LuetCfg.GetSolverOptions().CompactString()) Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{Concurrency: LuetCfg.GetGeneral().Concurrency, SolverOptions: *LuetCfg.GetSolverOptions()}) inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{
Concurrency: LuetCfg.GetGeneral().Concurrency,
SolverOptions: *LuetCfg.GetSolverOptions(),
NoDeps: nodeps,
Force: force,
})
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" { if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
systemDB = pkg.NewBoltDatabase( systemDB = pkg.NewBoltDatabase(
@ -103,5 +112,8 @@ func init() {
uninstallCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate") uninstallCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
uninstallCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate") uninstallCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
uninstallCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts") uninstallCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
uninstallCmd.Flags().Bool("nodeps", false, "Don't consider package dependencies (harmful!)")
uninstallCmd.Flags().Bool("force", false, "Force uninstall")
RootCmd.AddCommand(uninstallCmd) RootCmd.AddCommand(uninstallCmd)
} }

View File

@ -36,6 +36,7 @@ var upgradeCmd = &cobra.Command{
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount")) LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate")) LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts")) LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
LuetCfg.Viper.BindPFlag("force", cmd.Flags().Lookup("force"))
}, },
Long: `Upgrades packages in parallel`, Long: `Upgrades packages in parallel`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
@ -55,6 +56,7 @@ var upgradeCmd = &cobra.Command{
discount := LuetCfg.Viper.GetFloat64("solver.discount") discount := LuetCfg.Viper.GetFloat64("solver.discount")
rate := LuetCfg.Viper.GetFloat64("solver.rate") rate := LuetCfg.Viper.GetFloat64("solver.rate")
attempts := LuetCfg.Viper.GetInt("solver.max_attempts") attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
force := LuetCfg.Viper.GetBool("force")
LuetCfg.GetSolverOptions().Type = stype LuetCfg.GetSolverOptions().Type = stype
LuetCfg.GetSolverOptions().LearnRate = float32(rate) LuetCfg.GetSolverOptions().LearnRate = float32(rate)
@ -63,7 +65,11 @@ var upgradeCmd = &cobra.Command{
Debug("Solver", LuetCfg.GetSolverOptions().String()) Debug("Solver", LuetCfg.GetSolverOptions().String())
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{Concurrency: LuetCfg.GetGeneral().Concurrency, SolverOptions: *LuetCfg.GetSolverOptions()}) inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{
Concurrency: LuetCfg.GetGeneral().Concurrency,
SolverOptions: *LuetCfg.GetSolverOptions(),
Force: force,
})
inst.Repositories(repos) inst.Repositories(repos)
_, err := inst.SyncRepositories(false) _, err := inst.SyncRepositories(false)
if err != nil { if err != nil {
@ -95,5 +101,7 @@ func init() {
upgradeCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate") upgradeCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
upgradeCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate") upgradeCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
upgradeCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts") upgradeCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
upgradeCmd.Flags().Bool("force", false, "Force upgrade by ignoring errors")
RootCmd.AddCommand(upgradeCmd) RootCmd.AddCommand(upgradeCmd)
} }

View File

@ -38,6 +38,9 @@ import (
type LuetInstallerOptions struct { type LuetInstallerOptions struct {
SolverOptions config.LuetSolverOptions SolverOptions config.LuetSolverOptions
Concurrency int Concurrency int
NoDeps bool
OnlyDeps bool
Force bool
} }
type LuetInstaller struct { type LuetInstaller struct {
@ -115,8 +118,9 @@ func (l *LuetInstaller) Upgrade(s *System) error {
for _, u := range uninstall { for _, u := range uninstall {
err := l.Uninstall(u, s) err := l.Uninstall(u, s)
if err != nil { if err != nil && !l.Options.Force {
Warning("Failed uninstall for ", u.GetFingerPrint()) Error("Failed uninstall for ", u.HumanReadableString())
return errors.Wrap(err, "uninstalling "+u.HumanReadableString())
} }
} }
@ -161,7 +165,7 @@ func (l *LuetInstaller) Install(cp []pkg.Package, s *System) error {
vers, _ := s.Database.FindPackageVersions(pi) vers, _ := s.Database.FindPackageVersions(pi)
if len(vers) >= 1 { if len(vers) >= 1 {
Warning("Filtering out package " + pi.GetFingerPrint() + ", it has other versions already installed. Uninstall one of them first ") Warning("Filtering out package " + pi.HumanReadableString() + ", it has other versions already installed. Uninstall one of them first ")
continue continue
//return errors.New("Package " + pi.GetFingerPrint() + " has other versions already installed. Uninstall one of them first: " + strings.Join(vers, " ")) //return errors.New("Package " + pi.GetFingerPrint() + " has other versions already installed. Uninstall one of them first: " + strings.Join(vers, " "))
@ -186,20 +190,34 @@ func (l *LuetInstaller) Install(cp []pkg.Package, s *System) error {
allRepos := pkg.NewInMemoryDatabase(false) allRepos := pkg.NewInMemoryDatabase(false)
syncedRepos.SyncDatabase(allRepos) syncedRepos.SyncDatabase(allRepos)
p = syncedRepos.ResolveSelectors(p) p = syncedRepos.ResolveSelectors(p)
toInstall := map[string]ArtifactMatch{}
var packagesToInstall []pkg.Package
var solution solver.PackagesAssertions
if !l.Options.NoDeps {
solv := solver.NewResolver(s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver()) solv := solver.NewResolver(s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
solution, err := solv.Install(p) solution, err = solv.Install(p)
if err != nil { if err != nil && !l.Options.Force {
return errors.Wrap(err, "Failed solving solution for package") return errors.Wrap(err, "Failed solving solution for package")
} }
// Gathers things to install
for _, assertion := range solution {
if assertion.Value {
packagesToInstall = append(packagesToInstall, assertion.Package)
}
}
} else if !l.Options.OnlyDeps {
for _, currentPack := range p {
packagesToInstall = append(packagesToInstall, currentPack)
}
}
// Gathers things to install // Gathers things to install
toInstall := map[string]ArtifactMatch{} for _, currentPack := range packagesToInstall {
for _, assertion := range solution { matches := syncedRepos.PackageMatches([]pkg.Package{currentPack})
if assertion.Value {
matches := syncedRepos.PackageMatches([]pkg.Package{assertion.Package})
if len(matches) == 0 { if len(matches) == 0 {
return errors.New("Failed matching solutions against repository - where are definitions coming from?!") return errors.New("Failed matching solutions against repository for " + currentPack.HumanReadableString() + " where are definitions coming from?!")
} }
A: A:
for _, artefact := range matches[0].Repo.GetIndex() { for _, artefact := range matches[0].Repo.GetIndex() {
@ -209,15 +227,13 @@ func (l *LuetInstaller) Install(cp []pkg.Package, s *System) error {
} }
if matches[0].Package.Matches(artefact.GetCompileSpec().GetPackage()) { if matches[0].Package.Matches(artefact.GetCompileSpec().GetPackage()) {
// Filter out already installed // Filter out already installed
if _, err := s.Database.FindPackage(assertion.Package); err != nil { if _, err := s.Database.FindPackage(currentPack); err != nil {
toInstall[assertion.Package.GetFingerPrint()] = ArtifactMatch{Package: assertion.Package, Artifact: artefact, Repository: matches[0].Repo} toInstall[currentPack.GetFingerPrint()] = ArtifactMatch{Package: currentPack, Artifact: artefact, Repository: matches[0].Repo}
} }
break A break A
} }
} }
} }
}
// Install packages into rootfs in parallel. // Install packages into rootfs in parallel.
all := make(chan ArtifactMatch) all := make(chan ArtifactMatch)
@ -242,40 +258,39 @@ func (l *LuetInstaller) Install(cp []pkg.Package, s *System) error {
for _, ass := range ordered { for _, ass := range ordered {
if ass.Value { if ass.Value {
// Annotate to the system that the package was installed // Annotate to the system that the package was installed
// TODO: Annotate also files that belong to the package, somewhere to uninstall
if _, err := s.Database.FindPackage(ass.Package); err == nil { if _, err := s.Database.FindPackage(ass.Package); err == nil {
err := s.Database.UpdatePackage(ass.Package) err := s.Database.UpdatePackage(ass.Package)
if err != nil { if err != nil && !l.Options.Force {
return errors.Wrap(err, "Failed updating package") return errors.Wrap(err, "Failed updating package")
} }
} else { } else {
_, err := s.Database.CreatePackage(ass.Package) _, err := s.Database.CreatePackage(ass.Package)
if err != nil { if err != nil && !l.Options.Force {
return errors.Wrap(err, "Failed creating package") return errors.Wrap(err, "Failed creating package")
} }
} }
installed, ok := toInstall[ass.Package.GetFingerPrint()] installed, ok := toInstall[ass.Package.GetFingerPrint()]
if !ok { if !ok {
return errors.New("Couldn't find ArtifactMatch for " + ass.Package.GetFingerPrint()) return errors.New("Couldn't find ArtifactMatch for " + ass.Package.HumanReadableString())
} }
treePackage, err := installed.Repository.GetTree().GetDatabase().FindPackage(ass.Package) treePackage, err := installed.Repository.GetTree().GetDatabase().FindPackage(ass.Package)
if err != nil { if err != nil {
return errors.Wrap(err, "Error getting package "+ass.Package.GetFingerPrint()) return errors.Wrap(err, "Error getting package "+ass.Package.HumanReadableString())
} }
if helpers.Exists(treePackage.Rel(tree.FinalizerFile)) { if helpers.Exists(treePackage.Rel(tree.FinalizerFile)) {
Info("Executing finalizer for " + ass.Package.GetName()) Info("Executing finalizer for " + ass.Package.HumanReadableString())
finalizerRaw, err := ioutil.ReadFile(treePackage.Rel(tree.FinalizerFile)) finalizerRaw, err := ioutil.ReadFile(treePackage.Rel(tree.FinalizerFile))
if err != nil { if err != nil && !l.Options.Force {
return errors.Wrap(err, "Error reading file "+treePackage.Rel(tree.FinalizerFile)) return errors.Wrap(err, "Error reading file "+treePackage.Rel(tree.FinalizerFile))
} }
if _, exists := executedFinalizer[ass.Package.GetFingerPrint()]; !exists { if _, exists := executedFinalizer[ass.Package.GetFingerPrint()]; !exists {
finalizer, err := NewLuetFinalizerFromYaml(finalizerRaw) finalizer, err := NewLuetFinalizerFromYaml(finalizerRaw)
if err != nil { if err != nil && !l.Options.Force {
return errors.Wrap(err, "Error reading finalizer "+treePackage.Rel(tree.FinalizerFile)) return errors.Wrap(err, "Error reading finalizer "+treePackage.Rel(tree.FinalizerFile))
} }
err = finalizer.RunInstall() err = finalizer.RunInstall()
if err != nil { if err != nil && !l.Options.Force {
return errors.Wrap(err, "Error executing install finalizer "+treePackage.Rel(tree.FinalizerFile)) return errors.Wrap(err, "Error executing install finalizer "+treePackage.Rel(tree.FinalizerFile))
} }
executedFinalizer[ass.Package.GetFingerPrint()] = true executedFinalizer[ass.Package.GetFingerPrint()] = true
@ -285,7 +300,6 @@ func (l *LuetInstaller) Install(cp []pkg.Package, s *System) error {
} }
} }
return nil return nil
} }
@ -298,17 +312,17 @@ func (l *LuetInstaller) installPackage(a ArtifactMatch, s *System) error {
} }
err = artifact.Verify() err = artifact.Verify()
if err != nil { if err != nil && !l.Options.Force {
return errors.Wrap(err, "Artifact integrity check failure") return errors.Wrap(err, "Artifact integrity check failure")
} }
files, err := artifact.FileList() files, err := artifact.FileList()
if err != nil { if err != nil && !l.Options.Force {
return errors.Wrap(err, "Could not open package archive") return errors.Wrap(err, "Could not open package archive")
} }
err = artifact.Unpack(s.Target, true) err = artifact.Unpack(s.Target, true)
if err != nil { if err != nil && !l.Options.Force {
return errors.Wrap(err, "Error met while unpacking rootfs") return errors.Wrap(err, "Error met while unpacking rootfs")
} }
@ -323,11 +337,16 @@ func (l *LuetInstaller) installerWorker(i int, wg *sync.WaitGroup, c <-chan Arti
for p := range c { for p := range c {
// TODO: Keep trace of what was added from the tar, and save it into system // TODO: Keep trace of what was added from the tar, and save it into system
err := l.installPackage(p, s) err := l.installPackage(p, s)
if err != nil { if err != nil && !l.Options.Force {
//TODO: Uninstall, rollback. //TODO: Uninstall, rollback.
Fatal("Failed installing package "+p.Package.GetName(), err.Error()) Fatal("Failed installing package "+p.Package.GetName(), err.Error())
return errors.Wrap(err, "Failed installing package "+p.Package.GetName()) return errors.Wrap(err, "Failed installing package "+p.Package.GetName())
} }
if err == nil {
Info(":package: ", p.Package.HumanReadableString(), "installed")
} else if err != nil && l.Options.Force {
Info(":package: ", p.Package.HumanReadableString(), "installed with failures (force install)")
}
} }
return nil return nil
@ -364,18 +383,28 @@ func (l *LuetInstaller) uninstall(p pkg.Package, s *System) error {
func (l *LuetInstaller) Uninstall(p pkg.Package, s *System) error { func (l *LuetInstaller) Uninstall(p pkg.Package, s *System) error {
// compute uninstall from all world - remove packages in parallel - run uninstall finalizer (in order) - mark the uninstallation in db // compute uninstall from all world - remove packages in parallel - run uninstall finalizer (in order) - mark the uninstallation in db
// Get installed definition // Get installed definition
if !l.Options.NoDeps {
solv := solver.NewResolver(s.Database, s.Database, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver()) solv := solver.NewResolver(s.Database, s.Database, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
solution, err := solv.Uninstall(p) solution, err := solv.Uninstall(p)
if err != nil { if err != nil && !l.Options.Force {
return errors.Wrap(err, "Uninstall failed") return errors.Wrap(err, "Could not solve the uninstall constraints. Tip: try with --solver-type qlearning or with --force, or by removing packages excluding their dependencies with --nodeps")
} }
for _, p := range solution { for _, p := range solution {
Info("Uninstalling", p.GetFingerPrint()) Info("Uninstalling", p.HumanReadableString())
err := l.uninstall(p, s) err := l.uninstall(p, s)
if err != nil { if err != nil && !l.Options.Force {
return errors.Wrap(err, "Uninstall failed") return errors.Wrap(err, "Uninstall failed")
} }
} }
} else {
Info("Uninstalling", p.HumanReadableString(), "without deps")
err := l.uninstall(p, s)
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Uninstall failed")
}
Info(":package: ", p.HumanReadableString(), "uninstalled")
}
return nil return nil
} }