mirror of
https://github.com/mudler/luet.git
synced 2025-07-13 23:25:00 +00:00
Merge pull request #56 from mudler/gorl
Add resolvers to ehnance solver's heuristic
This commit is contained in:
commit
0fb3497a3b
17
README.md
17
README.md
@ -30,6 +30,23 @@ To install luet, you can grab a release on the [Release page](https://github.com
|
|||||||
|
|
||||||
Luet is not feature-complete yet, it can build, install/uninstall/upgrade packages - but it doesn't support yet all the features you would normally expect from a Package Manager nowadays.
|
Luet is not feature-complete yet, it can build, install/uninstall/upgrade packages - but it doesn't support yet all the features you would normally expect from a Package Manager nowadays.
|
||||||
|
|
||||||
|
# Dependency solving
|
||||||
|
|
||||||
|
Luet uses SAT and Reinforcement learning engine for dependency solving.
|
||||||
|
It encodes the package requirements into a SAT problem, using gophersat to solve the dependency tree and give a concrete model as result.
|
||||||
|
|
||||||
|
## SAT encoding
|
||||||
|
|
||||||
|
Each package and its constraints are encoded and built around [OPIUM](https://ranjitjhala.github.io/static/opium.pdf). Additionally, Luet treats
|
||||||
|
also selectors seamlessly while building the model, adding *ALO* ( *At least one* ) and *AMO* ( *At most one* ) rules to guarantee coherence within the installed system.
|
||||||
|
|
||||||
|
## Reinforcement learning
|
||||||
|
|
||||||
|
Luet also implements a small and portable qlearning agent that will try to solve conflict on your behalf
|
||||||
|
when they arises while trying to validate your queries against the system model.
|
||||||
|
|
||||||
|
To leverage it, simply pass ```--solver-type qlearning``` to the subcommands that supports it ( you can check out by invoking ```--help``` ).
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
[Documentation](https://luet-lab.github.io/docs) is available, or
|
[Documentation](https://luet-lab.github.io/docs) is available, or
|
||||||
|
25
cmd/build.go
25
cmd/build.go
@ -45,6 +45,11 @@ var buildCmd = &cobra.Command{
|
|||||||
viper.BindPFlag("revdeps", cmd.Flags().Lookup("revdeps"))
|
viper.BindPFlag("revdeps", cmd.Flags().Lookup("revdeps"))
|
||||||
viper.BindPFlag("all", cmd.Flags().Lookup("all"))
|
viper.BindPFlag("all", cmd.Flags().Lookup("all"))
|
||||||
viper.BindPFlag("compression", cmd.Flags().Lookup("compression"))
|
viper.BindPFlag("compression", cmd.Flags().Lookup("compression"))
|
||||||
|
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.type", cmd.Flags().Lookup("solver-type"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
|
||||||
},
|
},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
@ -92,7 +97,22 @@ var buildCmd = &cobra.Command{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
Fatal("Error: " + err.Error())
|
Fatal("Error: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stype := LuetCfg.Viper.GetString("solver.type")
|
||||||
|
discount := LuetCfg.Viper.GetFloat64("solver.discount")
|
||||||
|
rate := LuetCfg.Viper.GetFloat64("solver.rate")
|
||||||
|
attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
|
||||||
|
|
||||||
|
LuetCfg.GetSolverOptions().Type = stype
|
||||||
|
LuetCfg.GetSolverOptions().LearnRate = float32(rate)
|
||||||
|
LuetCfg.GetSolverOptions().Discount = float32(discount)
|
||||||
|
LuetCfg.GetSolverOptions().MaxAttempts = attempts
|
||||||
|
|
||||||
|
Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
|
||||||
|
|
||||||
opts := compiler.NewDefaultCompilerOptions()
|
opts := compiler.NewDefaultCompilerOptions()
|
||||||
|
opts.SolverOptions = *LuetCfg.GetSolverOptions()
|
||||||
|
|
||||||
opts.Clean = clean
|
opts.Clean = clean
|
||||||
luetCompiler := compiler.NewLuetCompiler(compilerBackend, generalRecipe.GetDatabase(), opts)
|
luetCompiler := compiler.NewLuetCompiler(compilerBackend, generalRecipe.GetDatabase(), opts)
|
||||||
luetCompiler.SetConcurrency(concurrency)
|
luetCompiler.SetConcurrency(concurrency)
|
||||||
@ -177,5 +197,10 @@ func init() {
|
|||||||
buildCmd.Flags().String("destination", path, "Destination folder")
|
buildCmd.Flags().String("destination", path, "Destination folder")
|
||||||
buildCmd.Flags().String("compression", "none", "Compression alg: none, gzip")
|
buildCmd.Flags().String("compression", "none", "Compression alg: none, gzip")
|
||||||
|
|
||||||
|
buildCmd.Flags().String("solver-type", "", "Solver strategy")
|
||||||
|
buildCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
|
||||||
|
buildCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
|
||||||
|
buildCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
|
||||||
|
|
||||||
RootCmd.AddCommand(buildCmd)
|
RootCmd.AddCommand(buildCmd)
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,9 @@ var cleanupCmd = &cobra.Command{
|
|||||||
var cleaned int = 0
|
var cleaned int = 0
|
||||||
|
|
||||||
// Check if cache dir exists
|
// Check if cache dir exists
|
||||||
if helpers.Exists(helpers.GetSystemPkgsCacheDirPath()) {
|
if helpers.Exists(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath()) {
|
||||||
|
|
||||||
files, err := ioutil.ReadDir(helpers.GetSystemPkgsCacheDirPath())
|
files, err := ioutil.ReadDir(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatal("Error on read cachedir ", err.Error())
|
Fatal("Error on read cachedir ", err.Error())
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ var cleanupCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := os.RemoveAll(
|
err := os.RemoveAll(
|
||||||
filepath.Join(helpers.GetSystemPkgsCacheDirPath(), file.Name()))
|
filepath.Join(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath(), file.Name()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatal("Error on removing", file.Name())
|
Fatal("Error on removing", file.Name())
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ import (
|
|||||||
installer "github.com/mudler/luet/pkg/installer"
|
installer "github.com/mudler/luet/pkg/installer"
|
||||||
|
|
||||||
. "github.com/mudler/luet/pkg/config"
|
. "github.com/mudler/luet/pkg/config"
|
||||||
"github.com/mudler/luet/pkg/helpers"
|
|
||||||
. "github.com/mudler/luet/pkg/logger"
|
. "github.com/mudler/luet/pkg/logger"
|
||||||
pkg "github.com/mudler/luet/pkg/package"
|
pkg "github.com/mudler/luet/pkg/package"
|
||||||
|
|
||||||
@ -36,6 +35,10 @@ var installCmd = &cobra.Command{
|
|||||||
PreRun: func(cmd *cobra.Command, args []string) {
|
PreRun: func(cmd *cobra.Command, args []string) {
|
||||||
LuetCfg.Viper.BindPFlag("system.database_path", cmd.Flags().Lookup("system-dbpath"))
|
LuetCfg.Viper.BindPFlag("system.database_path", cmd.Flags().Lookup("system-dbpath"))
|
||||||
LuetCfg.Viper.BindPFlag("system.rootfs", cmd.Flags().Lookup("system-target"))
|
LuetCfg.Viper.BindPFlag("system.rootfs", cmd.Flags().Lookup("system-target"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.type", cmd.Flags().Lookup("solver-type"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
|
||||||
},
|
},
|
||||||
Long: `Install packages in parallel`,
|
Long: `Install packages in parallel`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
@ -76,12 +79,24 @@ var installCmd = &cobra.Command{
|
|||||||
repos = append(repos, r)
|
repos = append(repos, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
inst := installer.NewLuetInstaller(LuetCfg.GetGeneral().Concurrency)
|
stype := LuetCfg.Viper.GetString("solver.type")
|
||||||
|
discount := LuetCfg.Viper.GetFloat64("solver.discount")
|
||||||
|
rate := LuetCfg.Viper.GetFloat64("solver.rate")
|
||||||
|
attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
|
||||||
|
|
||||||
|
LuetCfg.GetSolverOptions().Type = stype
|
||||||
|
LuetCfg.GetSolverOptions().LearnRate = float32(rate)
|
||||||
|
LuetCfg.GetSolverOptions().Discount = float32(discount)
|
||||||
|
LuetCfg.GetSolverOptions().MaxAttempts = attempts
|
||||||
|
|
||||||
|
Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
|
||||||
|
|
||||||
|
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{Concurrency: LuetCfg.GetGeneral().Concurrency, SolverOptions: *LuetCfg.GetSolverOptions()})
|
||||||
inst.Repositories(repos)
|
inst.Repositories(repos)
|
||||||
|
|
||||||
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
|
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
|
||||||
systemDB = pkg.NewBoltDatabase(
|
systemDB = pkg.NewBoltDatabase(
|
||||||
filepath.Join(helpers.GetSystemRepoDatabaseDirPath(), "luet.db"))
|
filepath.Join(LuetCfg.GetSystem().GetSystemRepoDatabaseDirPath(), "luet.db"))
|
||||||
} else {
|
} else {
|
||||||
systemDB = pkg.NewInMemoryDatabase(true)
|
systemDB = pkg.NewInMemoryDatabase(true)
|
||||||
}
|
}
|
||||||
@ -100,6 +115,10 @@ func init() {
|
|||||||
}
|
}
|
||||||
installCmd.Flags().String("system-dbpath", path, "System db path")
|
installCmd.Flags().String("system-dbpath", path, "System db path")
|
||||||
installCmd.Flags().String("system-target", path, "System rootpath")
|
installCmd.Flags().String("system-target", path, "System rootpath")
|
||||||
|
installCmd.Flags().String("solver-type", "", "Solver strategy ( Defaults none, available: "+AvailableResolvers+" )")
|
||||||
|
installCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
|
||||||
|
installCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
|
||||||
|
installCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
|
||||||
|
|
||||||
RootCmd.AddCommand(installCmd)
|
RootCmd.AddCommand(installCmd)
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "github.com/mudler/luet/pkg/config"
|
. "github.com/mudler/luet/pkg/config"
|
||||||
"github.com/mudler/luet/pkg/helpers"
|
|
||||||
installer "github.com/mudler/luet/pkg/installer"
|
installer "github.com/mudler/luet/pkg/installer"
|
||||||
|
|
||||||
. "github.com/logrusorgru/aurora"
|
. "github.com/logrusorgru/aurora"
|
||||||
@ -69,7 +68,7 @@ func NewRepoListCommand() *cobra.Command {
|
|||||||
repoText = Yellow(repo.Urls[0]).String()
|
repoText = Yellow(repo.Urls[0]).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
repobasedir := helpers.GetRepoDatabaseDirPath(repo.Name)
|
repobasedir := LuetCfg.GetSystem().GetRepoDatabaseDirPath(repo.Name)
|
||||||
if repo.Cached {
|
if repo.Cached {
|
||||||
|
|
||||||
r := installer.NewSystemRepository(repo)
|
r := installer.NewSystemRepository(repo)
|
||||||
|
@ -20,13 +20,11 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
. "github.com/mudler/luet/pkg/config"
|
. "github.com/mudler/luet/pkg/config"
|
||||||
"github.com/mudler/luet/pkg/helpers"
|
|
||||||
installer "github.com/mudler/luet/pkg/installer"
|
installer "github.com/mudler/luet/pkg/installer"
|
||||||
. "github.com/mudler/luet/pkg/logger"
|
. "github.com/mudler/luet/pkg/logger"
|
||||||
pkg "github.com/mudler/luet/pkg/package"
|
pkg "github.com/mudler/luet/pkg/package"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var searchCmd = &cobra.Command{
|
var searchCmd = &cobra.Command{
|
||||||
@ -36,7 +34,11 @@ var searchCmd = &cobra.Command{
|
|||||||
PreRun: func(cmd *cobra.Command, args []string) {
|
PreRun: func(cmd *cobra.Command, args []string) {
|
||||||
LuetCfg.Viper.BindPFlag("system.database_path", cmd.Flags().Lookup("system-dbpath"))
|
LuetCfg.Viper.BindPFlag("system.database_path", cmd.Flags().Lookup("system-dbpath"))
|
||||||
LuetCfg.Viper.BindPFlag("system.rootfs", cmd.Flags().Lookup("system-target"))
|
LuetCfg.Viper.BindPFlag("system.rootfs", cmd.Flags().Lookup("system-target"))
|
||||||
viper.BindPFlag("installed", cmd.Flags().Lookup("installed"))
|
LuetCfg.Viper.BindPFlag("installed", cmd.Flags().Lookup("installed"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.type", cmd.Flags().Lookup("solver-type"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
|
||||||
},
|
},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
var systemDB pkg.PackageDatabase
|
var systemDB pkg.PackageDatabase
|
||||||
@ -44,7 +46,18 @@ var searchCmd = &cobra.Command{
|
|||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
Fatal("Wrong number of arguments (expected 1)")
|
Fatal("Wrong number of arguments (expected 1)")
|
||||||
}
|
}
|
||||||
installed := viper.GetBool("installed")
|
installed := LuetCfg.Viper.GetBool("installed")
|
||||||
|
stype := LuetCfg.Viper.GetString("solver.type")
|
||||||
|
discount := LuetCfg.Viper.GetFloat64("solver.discount")
|
||||||
|
rate := LuetCfg.Viper.GetFloat64("solver.rate")
|
||||||
|
attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
|
||||||
|
|
||||||
|
LuetCfg.GetSolverOptions().Type = stype
|
||||||
|
LuetCfg.GetSolverOptions().LearnRate = float32(rate)
|
||||||
|
LuetCfg.GetSolverOptions().Discount = float32(discount)
|
||||||
|
LuetCfg.GetSolverOptions().MaxAttempts = attempts
|
||||||
|
|
||||||
|
Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
|
||||||
|
|
||||||
if !installed {
|
if !installed {
|
||||||
|
|
||||||
@ -57,7 +70,8 @@ var searchCmd = &cobra.Command{
|
|||||||
repos = append(repos, r)
|
repos = append(repos, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
inst := installer.NewLuetInstaller(LuetCfg.GetGeneral().Concurrency)
|
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{Concurrency: LuetCfg.GetGeneral().Concurrency, SolverOptions: *LuetCfg.GetSolverOptions()})
|
||||||
|
|
||||||
inst.Repositories(repos)
|
inst.Repositories(repos)
|
||||||
synced, err := inst.SyncRepositories(false)
|
synced, err := inst.SyncRepositories(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -75,7 +89,7 @@ var searchCmd = &cobra.Command{
|
|||||||
|
|
||||||
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
|
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
|
||||||
systemDB = pkg.NewBoltDatabase(
|
systemDB = pkg.NewBoltDatabase(
|
||||||
filepath.Join(helpers.GetSystemRepoDatabaseDirPath(), "luet.db"))
|
filepath.Join(LuetCfg.GetSystem().GetSystemRepoDatabaseDirPath(), "luet.db"))
|
||||||
} else {
|
} else {
|
||||||
systemDB = pkg.NewInMemoryDatabase(true)
|
systemDB = pkg.NewInMemoryDatabase(true)
|
||||||
}
|
}
|
||||||
@ -101,5 +115,9 @@ func init() {
|
|||||||
searchCmd.Flags().String("system-dbpath", path, "System db path")
|
searchCmd.Flags().String("system-dbpath", path, "System db path")
|
||||||
searchCmd.Flags().String("system-target", path, "System rootpath")
|
searchCmd.Flags().String("system-target", path, "System rootpath")
|
||||||
searchCmd.Flags().Bool("installed", false, "Search between system packages")
|
searchCmd.Flags().Bool("installed", false, "Search between system packages")
|
||||||
|
searchCmd.Flags().String("solver-type", "", "Solver strategy ( Defaults none, available: "+AvailableResolvers+" )")
|
||||||
|
searchCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
|
||||||
|
searchCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
|
||||||
|
searchCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
|
||||||
RootCmd.AddCommand(searchCmd)
|
RootCmd.AddCommand(searchCmd)
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
. "github.com/mudler/luet/pkg/config"
|
. "github.com/mudler/luet/pkg/config"
|
||||||
"github.com/mudler/luet/pkg/helpers"
|
|
||||||
installer "github.com/mudler/luet/pkg/installer"
|
installer "github.com/mudler/luet/pkg/installer"
|
||||||
. "github.com/mudler/luet/pkg/logger"
|
. "github.com/mudler/luet/pkg/logger"
|
||||||
pkg "github.com/mudler/luet/pkg/package"
|
pkg "github.com/mudler/luet/pkg/package"
|
||||||
@ -36,6 +35,10 @@ var uninstallCmd = &cobra.Command{
|
|||||||
PreRun: func(cmd *cobra.Command, args []string) {
|
PreRun: func(cmd *cobra.Command, args []string) {
|
||||||
LuetCfg.Viper.BindPFlag("system.database_path", cmd.Flags().Lookup("system-dbpath"))
|
LuetCfg.Viper.BindPFlag("system.database_path", cmd.Flags().Lookup("system-dbpath"))
|
||||||
LuetCfg.Viper.BindPFlag("system.rootfs", cmd.Flags().Lookup("system-target"))
|
LuetCfg.Viper.BindPFlag("system.rootfs", cmd.Flags().Lookup("system-target"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.type", cmd.Flags().Lookup("solver-type"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
|
||||||
},
|
},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
var systemDB pkg.PackageDatabase
|
var systemDB pkg.PackageDatabase
|
||||||
@ -60,11 +63,23 @@ var uninstallCmd = &cobra.Command{
|
|||||||
Uri: make([]string, 0),
|
Uri: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
inst := installer.NewLuetInstaller(LuetCfg.GetGeneral().Concurrency)
|
stype := LuetCfg.Viper.GetString("solver.type")
|
||||||
|
discount := LuetCfg.Viper.GetFloat64("solver.discount")
|
||||||
|
rate := LuetCfg.Viper.GetFloat64("solver.rate")
|
||||||
|
attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
|
||||||
|
|
||||||
|
LuetCfg.GetSolverOptions().Type = stype
|
||||||
|
LuetCfg.GetSolverOptions().LearnRate = float32(rate)
|
||||||
|
LuetCfg.GetSolverOptions().Discount = float32(discount)
|
||||||
|
LuetCfg.GetSolverOptions().MaxAttempts = attempts
|
||||||
|
|
||||||
|
Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
|
||||||
|
|
||||||
|
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{Concurrency: LuetCfg.GetGeneral().Concurrency, SolverOptions: *LuetCfg.GetSolverOptions()})
|
||||||
|
|
||||||
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
|
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
|
||||||
systemDB = pkg.NewBoltDatabase(
|
systemDB = pkg.NewBoltDatabase(
|
||||||
filepath.Join(helpers.GetSystemRepoDatabaseDirPath(), "luet.db"))
|
filepath.Join(LuetCfg.GetSystem().GetSystemRepoDatabaseDirPath(), "luet.db"))
|
||||||
} else {
|
} else {
|
||||||
systemDB = pkg.NewInMemoryDatabase(true)
|
systemDB = pkg.NewInMemoryDatabase(true)
|
||||||
}
|
}
|
||||||
@ -84,5 +99,9 @@ func init() {
|
|||||||
}
|
}
|
||||||
uninstallCmd.Flags().String("system-dbpath", path, "System db path")
|
uninstallCmd.Flags().String("system-dbpath", path, "System db path")
|
||||||
uninstallCmd.Flags().String("system-target", path, "System rootpath")
|
uninstallCmd.Flags().String("system-target", path, "System rootpath")
|
||||||
|
uninstallCmd.Flags().String("solver-type", "", "Solver strategy ( Defaults none, available: "+AvailableResolvers+" )")
|
||||||
|
uninstallCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
|
||||||
|
uninstallCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
|
||||||
|
uninstallCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
|
||||||
RootCmd.AddCommand(uninstallCmd)
|
RootCmd.AddCommand(uninstallCmd)
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
. "github.com/mudler/luet/pkg/config"
|
. "github.com/mudler/luet/pkg/config"
|
||||||
"github.com/mudler/luet/pkg/helpers"
|
|
||||||
installer "github.com/mudler/luet/pkg/installer"
|
installer "github.com/mudler/luet/pkg/installer"
|
||||||
. "github.com/mudler/luet/pkg/logger"
|
. "github.com/mudler/luet/pkg/logger"
|
||||||
pkg "github.com/mudler/luet/pkg/package"
|
pkg "github.com/mudler/luet/pkg/package"
|
||||||
@ -33,6 +32,10 @@ var upgradeCmd = &cobra.Command{
|
|||||||
PreRun: func(cmd *cobra.Command, args []string) {
|
PreRun: func(cmd *cobra.Command, args []string) {
|
||||||
LuetCfg.Viper.BindPFlag("system.database_path", installCmd.Flags().Lookup("system-dbpath"))
|
LuetCfg.Viper.BindPFlag("system.database_path", installCmd.Flags().Lookup("system-dbpath"))
|
||||||
LuetCfg.Viper.BindPFlag("system.rootfs", installCmd.Flags().Lookup("system-target"))
|
LuetCfg.Viper.BindPFlag("system.rootfs", installCmd.Flags().Lookup("system-target"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.type", cmd.Flags().Lookup("solver-type"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
|
||||||
|
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
|
||||||
},
|
},
|
||||||
Long: `Upgrades packages in parallel`,
|
Long: `Upgrades packages in parallel`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
@ -48,7 +51,19 @@ var upgradeCmd = &cobra.Command{
|
|||||||
repos = append(repos, r)
|
repos = append(repos, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
inst := installer.NewLuetInstaller(LuetCfg.GetGeneral().Concurrency)
|
stype := LuetCfg.Viper.GetString("solver.type")
|
||||||
|
discount := LuetCfg.Viper.GetFloat64("solver.discount")
|
||||||
|
rate := LuetCfg.Viper.GetFloat64("solver.rate")
|
||||||
|
attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
|
||||||
|
|
||||||
|
LuetCfg.GetSolverOptions().Type = stype
|
||||||
|
LuetCfg.GetSolverOptions().LearnRate = float32(rate)
|
||||||
|
LuetCfg.GetSolverOptions().Discount = float32(discount)
|
||||||
|
LuetCfg.GetSolverOptions().MaxAttempts = attempts
|
||||||
|
|
||||||
|
Debug("Solver", LuetCfg.GetSolverOptions().String())
|
||||||
|
|
||||||
|
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{Concurrency: LuetCfg.GetGeneral().Concurrency, SolverOptions: *LuetCfg.GetSolverOptions()})
|
||||||
inst.Repositories(repos)
|
inst.Repositories(repos)
|
||||||
_, err := inst.SyncRepositories(false)
|
_, err := inst.SyncRepositories(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -57,7 +72,7 @@ var upgradeCmd = &cobra.Command{
|
|||||||
|
|
||||||
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
|
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
|
||||||
systemDB = pkg.NewBoltDatabase(
|
systemDB = pkg.NewBoltDatabase(
|
||||||
filepath.Join(helpers.GetSystemRepoDatabaseDirPath(), "luet.db"))
|
filepath.Join(LuetCfg.GetSystem().GetSystemRepoDatabaseDirPath(), "luet.db"))
|
||||||
} else {
|
} else {
|
||||||
systemDB = pkg.NewInMemoryDatabase(true)
|
systemDB = pkg.NewInMemoryDatabase(true)
|
||||||
}
|
}
|
||||||
@ -76,6 +91,9 @@ func init() {
|
|||||||
}
|
}
|
||||||
upgradeCmd.Flags().String("system-dbpath", path, "System db path")
|
upgradeCmd.Flags().String("system-dbpath", path, "System db path")
|
||||||
upgradeCmd.Flags().String("system-target", path, "System rootpath")
|
upgradeCmd.Flags().String("system-target", path, "System rootpath")
|
||||||
|
upgradeCmd.Flags().String("solver-type", "", "Solver strategy ( Defaults none, available: "+AvailableResolvers+" )")
|
||||||
|
upgradeCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
|
||||||
|
upgradeCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
|
||||||
|
upgradeCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
|
||||||
RootCmd.AddCommand(upgradeCmd)
|
RootCmd.AddCommand(upgradeCmd)
|
||||||
}
|
}
|
||||||
|
@ -108,3 +108,21 @@
|
|||||||
# basic: "mybasicauth"
|
# basic: "mybasicauth"
|
||||||
# Define token authentication header
|
# Define token authentication header
|
||||||
# token: "mytoken"
|
# token: "mytoken"
|
||||||
|
# ---------------------------------------------
|
||||||
|
# Solver parameter configuration:
|
||||||
|
# ---------------------------------------------
|
||||||
|
# solver:
|
||||||
|
#
|
||||||
|
# Solver strategy to solve possible conflicts during depedency
|
||||||
|
# solving. Defaults to empty (none). Available: qlearning
|
||||||
|
# type: ""
|
||||||
|
#
|
||||||
|
# Solver agent learning rate. 0.1 to 1.0
|
||||||
|
# rate: 0.7
|
||||||
|
#
|
||||||
|
# Learning discount factor.
|
||||||
|
# discount: 1.0
|
||||||
|
#
|
||||||
|
# Number of overall attempts that the solver has available before bailing out.
|
||||||
|
# max_attempts: 9000
|
||||||
|
#
|
8
go.mod
8
go.mod
@ -4,16 +4,14 @@ go 1.12
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/DataDog/zstd v1.4.4 // indirect
|
github.com/DataDog/zstd v1.4.4 // indirect
|
||||||
github.com/MottainaiCI/simplestreams-builder v0.0.0-20190710131531-efb382161f56 // indirect
|
|
||||||
github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7
|
github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7
|
||||||
github.com/asdine/storm v0.0.0-20190418133842-e0f77eada154
|
github.com/asdine/storm v0.0.0-20190418133842-e0f77eada154
|
||||||
github.com/briandowns/spinner v1.7.0
|
github.com/briandowns/spinner v1.7.0
|
||||||
github.com/cavaliercoder/grab v2.0.0+incompatible
|
github.com/cavaliercoder/grab v2.0.0+incompatible
|
||||||
github.com/creack/pty v1.1.9 // indirect
|
github.com/crillab/gophersat v1.1.9-0.20200211102949-9a8bf7f2f0a3
|
||||||
github.com/crillab/gophersat v1.1.7
|
|
||||||
github.com/docker/docker v0.7.3-0.20180827131323-0c5f8d2b9b23
|
github.com/docker/docker v0.7.3-0.20180827131323-0c5f8d2b9b23
|
||||||
|
github.com/ecooper/qlearning v0.0.0-20160612200101-3075011a69fd
|
||||||
github.com/ghodss/yaml v1.0.0
|
github.com/ghodss/yaml v1.0.0
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible // indirect
|
|
||||||
github.com/hashicorp/go-version v1.2.0
|
github.com/hashicorp/go-version v1.2.0
|
||||||
github.com/jinzhu/copier v0.0.0-20180308034124-7e38e58719c3
|
github.com/jinzhu/copier v0.0.0-20180308034124-7e38e58719c3
|
||||||
github.com/klauspost/pgzip v1.2.1
|
github.com/klauspost/pgzip v1.2.1
|
||||||
@ -29,7 +27,6 @@ require (
|
|||||||
github.com/pelletier/go-toml v1.6.0 // indirect
|
github.com/pelletier/go-toml v1.6.0 // indirect
|
||||||
github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f
|
github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f
|
||||||
github.com/pkg/errors v0.8.1
|
github.com/pkg/errors v0.8.1
|
||||||
github.com/rogpeppe/go-internal v1.5.1 // indirect
|
|
||||||
github.com/spf13/afero v1.2.2 // indirect
|
github.com/spf13/afero v1.2.2 // indirect
|
||||||
github.com/spf13/cobra v0.0.5
|
github.com/spf13/cobra v0.0.5
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
@ -46,6 +43,5 @@ require (
|
|||||||
golang.org/x/tools v0.0.0-20200102200121-6de373a2766c // indirect
|
golang.org/x/tools v0.0.0-20200102200121-6de373a2766c // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
|
||||||
gopkg.in/yaml.v2 v2.2.7
|
gopkg.in/yaml.v2 v2.2.7
|
||||||
mvdan.cc/sh v2.6.4+incompatible // indirect
|
|
||||||
mvdan.cc/sh/v3 v3.0.0-beta1
|
mvdan.cc/sh/v3 v3.0.0-beta1
|
||||||
)
|
)
|
||||||
|
28
go.sum
28
go.sum
@ -9,15 +9,9 @@ github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6
|
|||||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||||
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
|
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
|
||||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||||
github.com/MottainaiCI/simplestreams-builder v0.0.0-20190710131531-efb382161f56 h1:XCZM9J5KqLsr5NqtrZuXiD3X5fe5IfgU7IIUZzpeFBk=
|
|
||||||
github.com/MottainaiCI/simplestreams-builder v0.0.0-20190710131531-efb382161f56/go.mod h1:+Gbv6dg6TPHWq4oDjZY1vn978PLCEZ2hOu8kvn+S7t4=
|
|
||||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
||||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/Sabayon/pkgs-checker v0.4.1 h1:NImZhA5Z9souyr9Ff3nDzP0Bs9SGuDLxRzduVqci3dQ=
|
|
||||||
github.com/Sabayon/pkgs-checker v0.4.1/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE=
|
|
||||||
github.com/Sabayon/pkgs-checker v0.4.2-0.20191225230938-5ebdcd474e55 h1:rblyWFJ61sx69WwRsHK/BHcBlFm5X38sQmJnzVhyUoE=
|
|
||||||
github.com/Sabayon/pkgs-checker v0.4.2-0.20191225230938-5ebdcd474e55/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE=
|
|
||||||
github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7 h1:Vf80sSLu1ZWjjMmUKhw0FqM43lEOvT8O5B22NaHB6AQ=
|
github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7 h1:Vf80sSLu1ZWjjMmUKhw0FqM43lEOvT8O5B22NaHB6AQ=
|
||||||
github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE=
|
github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE=
|
||||||
github.com/Sereal/Sereal v0.0.0-20181211220259-509a78ddbda3 h1:Xu7z47ZiE/J+sKXHZMGxEor/oY2q6dq51fkO0JqdSwY=
|
github.com/Sereal/Sereal v0.0.0-20181211220259-509a78ddbda3 h1:Xu7z47ZiE/J+sKXHZMGxEor/oY2q6dq51fkO0JqdSwY=
|
||||||
@ -59,9 +53,10 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
github.com/crillab/gophersat v1.1.7 h1:f2Phe0W9jGyN1OefygKdcTdNM99q/goSjbWrFRjZGWc=
|
github.com/crillab/gophersat v1.1.7 h1:f2Phe0W9jGyN1OefygKdcTdNM99q/goSjbWrFRjZGWc=
|
||||||
github.com/crillab/gophersat v1.1.7/go.mod h1:S91tHga1PCZzYhCkStwZAhvp1rCc+zqtSi55I+vDWGc=
|
github.com/crillab/gophersat v1.1.7/go.mod h1:S91tHga1PCZzYhCkStwZAhvp1rCc+zqtSi55I+vDWGc=
|
||||||
|
github.com/crillab/gophersat v1.1.9-0.20200211102949-9a8bf7f2f0a3 h1:HO63LCf9kTXQgUnlvFeS2qSDQhZ/cLP8DAJO89CythY=
|
||||||
|
github.com/crillab/gophersat v1.1.9-0.20200211102949-9a8bf7f2f0a3/go.mod h1:S91tHga1PCZzYhCkStwZAhvp1rCc+zqtSi55I+vDWGc=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
|
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -82,6 +77,8 @@ github.com/docker/libnetwork v0.8.0-dev.2.0.20180608203834-19279f049241 h1:+ebE/
|
|||||||
github.com/docker/libnetwork v0.8.0-dev.2.0.20180608203834-19279f049241/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8=
|
github.com/docker/libnetwork v0.8.0-dev.2.0.20180608203834-19279f049241/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8=
|
||||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
|
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
|
||||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||||
|
github.com/ecooper/qlearning v0.0.0-20160612200101-3075011a69fd h1:JWEotl+g5uCCn37eVAYLF3UjBqO5HJ0ezZ5Zgnsdoqc=
|
||||||
|
github.com/ecooper/qlearning v0.0.0-20160612200101-3075011a69fd/go.mod h1:y0+kb0ORo7mC8lQbUzC4oa7ufu565J6SyUgWd39Z1Ic=
|
||||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
@ -94,8 +91,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
|
|||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
|
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||||
@ -152,6 +147,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv
|
|||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
@ -204,7 +200,9 @@ github.com/opencontainers/runtime-spec v1.0.1 h1:wY4pOY8fBdSIvs9+IDHC55thBuEulhz
|
|||||||
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
github.com/otiai10/copy v1.0.2 h1:DDNipYy6RkIkjMwy+AWzgKiNTyj2RUI9yEMeETEpVyc=
|
github.com/otiai10/copy v1.0.2 h1:DDNipYy6RkIkjMwy+AWzgKiNTyj2RUI9yEMeETEpVyc=
|
||||||
github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY=
|
github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY=
|
||||||
|
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95 h1:+OLn68pqasWca0z5ryit9KGfp3sUsW4Lqg32iRMJyzs=
|
||||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||||
|
github.com/otiai10/mint v1.3.0 h1:Ady6MKVezQwHBkGzLFbrsywyp09Ah7rkmfjV3Bcr5uc=
|
||||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
|
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
|
||||||
@ -231,7 +229,6 @@ github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
|
|||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.5.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
|
||||||
github.com/rootless-containers/proto v0.1.0 h1:gS1JOMEtk1YDYHCzBAf/url+olMJbac7MTrgSeP6zh4=
|
github.com/rootless-containers/proto v0.1.0 h1:gS1JOMEtk1YDYHCzBAf/url+olMJbac7MTrgSeP6zh4=
|
||||||
github.com/rootless-containers/proto v0.1.0/go.mod h1:vgkUFZbQd0gcE/K/ZwtE4MYjZPu0UNHLXIQxhyqAFh8=
|
github.com/rootless-containers/proto v0.1.0/go.mod h1:vgkUFZbQd0gcE/K/ZwtE4MYjZPu0UNHLXIQxhyqAFh8=
|
||||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||||
@ -266,8 +263,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
|
|||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||||
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
|
|
||||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
|
||||||
github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4=
|
github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4=
|
||||||
github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4=
|
github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4=
|
||||||
github.com/stevenle/topsort v0.0.0-20130922064739-8130c1d7596b h1:wJSBFlabo96ySlmSX0a02WAPyGxagzTo9c5sk3sHP3E=
|
github.com/stevenle/topsort v0.0.0-20130922064739-8130c1d7596b h1:wJSBFlabo96ySlmSX0a02WAPyGxagzTo9c5sk3sHP3E=
|
||||||
@ -326,8 +321,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152 h1:ZC1Xn5A1nlpSmQCIva4bZ3ob3lmhYIefc+GU+DLg1Ow=
|
|
||||||
golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc=
|
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc=
|
||||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
@ -336,7 +329,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
|
|||||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
|
||||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@ -372,8 +364,6 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190913121621-c3b328c6e5a7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190913121621-c3b328c6e5a7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
|
||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4 h1:Hynbrlo6LbYI3H1IqXpkVDOcX/3HiPdhVEuyj5a59RM=
|
|
||||||
golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200102141924-c96a22e43c9c h1:OYFUffxXPezb7BVTx9AaD4Vl0qtxmklBIkwCKH1YwDY=
|
golang.org/x/sys v0.0.0-20200102141924-c96a22e43c9c h1:OYFUffxXPezb7BVTx9AaD4Vl0qtxmklBIkwCKH1YwDY=
|
||||||
golang.org/x/sys v0.0.0-20200102141924-c96a22e43c9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200102141924-c96a22e43c9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@ -419,8 +409,6 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gotest.tools v2.1.0+incompatible h1:5USw7CrJBYKqjg9R7QlA6jzqZKEAtvW82aNmsxxGPxw=
|
gotest.tools v2.1.0+incompatible h1:5USw7CrJBYKqjg9R7QlA6jzqZKEAtvW82aNmsxxGPxw=
|
||||||
@ -429,7 +417,5 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
|
|||||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
mvdan.cc/editorconfig v0.1.1-0.20191109213504-890940e3f00e/go.mod h1:Ge4atmRUYqueGppvJ7JNrtqpqokoJEFxYbP0Z+WeKS8=
|
mvdan.cc/editorconfig v0.1.1-0.20191109213504-890940e3f00e/go.mod h1:Ge4atmRUYqueGppvJ7JNrtqpqokoJEFxYbP0Z+WeKS8=
|
||||||
mvdan.cc/sh v2.6.4+incompatible h1:eD6tDeh0pw+/TOTI1BBEryZ02rD2nMcFsgcvde7jffM=
|
|
||||||
mvdan.cc/sh v2.6.4+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8=
|
|
||||||
mvdan.cc/sh/v3 v3.0.0-beta1 h1:UqiwBEXEPzelaGxuvixaOtzc7WzKtrElePJ8HqvW7K8=
|
mvdan.cc/sh/v3 v3.0.0-beta1 h1:UqiwBEXEPzelaGxuvixaOtzc7WzKtrElePJ8HqvW7K8=
|
||||||
mvdan.cc/sh/v3 v3.0.0-beta1/go.mod h1:rBIndNJFYPp8xSppiZcGIk6B5d1g3OEARxEaXjPxwVI=
|
mvdan.cc/sh/v3 v3.0.0-beta1/go.mod h1:rBIndNJFYPp8xSppiZcGIk6B5d1g3OEARxEaXjPxwVI=
|
||||||
|
@ -42,6 +42,7 @@ type LuetCompiler struct {
|
|||||||
PullFirst, KeepImg, Clean bool
|
PullFirst, KeepImg, Clean bool
|
||||||
Concurrency int
|
Concurrency int
|
||||||
CompressionType CompressionImplementation
|
CompressionType CompressionImplementation
|
||||||
|
Options CompilerOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLuetCompiler(backend CompilerBackend, db pkg.PackageDatabase, opt *CompilerOptions) Compiler {
|
func NewLuetCompiler(backend CompilerBackend, db pkg.PackageDatabase, opt *CompilerOptions) Compiler {
|
||||||
@ -58,6 +59,7 @@ func NewLuetCompiler(backend CompilerBackend, db pkg.PackageDatabase, opt *Compi
|
|||||||
KeepImg: opt.KeepImg,
|
KeepImg: opt.KeepImg,
|
||||||
Concurrency: opt.Concurrency,
|
Concurrency: opt.Concurrency,
|
||||||
Clean: opt.Clean,
|
Clean: opt.Clean,
|
||||||
|
Options: *opt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,7 +461,7 @@ func (cs *LuetCompiler) packageFromImage(p CompilationSpec, tag string, keepPerm
|
|||||||
|
|
||||||
func (cs *LuetCompiler) ComputeDepTree(p CompilationSpec) (solver.PackagesAssertions, error) {
|
func (cs *LuetCompiler) ComputeDepTree(p CompilationSpec) (solver.PackagesAssertions, error) {
|
||||||
|
|
||||||
s := solver.NewSolver(pkg.NewInMemoryDatabase(false), cs.Database, pkg.NewInMemoryDatabase(false))
|
s := solver.NewResolver(pkg.NewInMemoryDatabase(false), cs.Database, pkg.NewInMemoryDatabase(false), cs.Options.SolverOptions.Resolver())
|
||||||
|
|
||||||
solution, err := s.Install([]pkg.Package{p.GetPackage()})
|
solution, err := s.Install([]pkg.Package{p.GetPackage()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -18,6 +18,7 @@ package compiler
|
|||||||
import (
|
import (
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/mudler/luet/pkg/config"
|
||||||
pkg "github.com/mudler/luet/pkg/package"
|
pkg "github.com/mudler/luet/pkg/package"
|
||||||
"github.com/mudler/luet/pkg/solver"
|
"github.com/mudler/luet/pkg/solver"
|
||||||
)
|
)
|
||||||
@ -48,6 +49,8 @@ type CompilerOptions struct {
|
|||||||
Concurrency int
|
Concurrency int
|
||||||
CompressionType CompressionImplementation
|
CompressionType CompressionImplementation
|
||||||
Clean bool
|
Clean bool
|
||||||
|
|
||||||
|
SolverOptions config.LuetSolverOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultCompilerOptions() *CompilerOptions {
|
func NewDefaultCompilerOptions() *CompilerOptions {
|
||||||
|
@ -19,14 +19,20 @@ package config
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
solver "github.com/mudler/luet/pkg/solver"
|
||||||
v "github.com/spf13/viper"
|
v "github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var LuetCfg = NewLuetConfig(v.GetViper())
|
var LuetCfg = NewLuetConfig(v.GetViper())
|
||||||
|
var AvailableResolvers = strings.Join([]string{solver.QLearningResolverType}, " ")
|
||||||
|
|
||||||
type LuetLoggingConfig struct {
|
type LuetLoggingConfig struct {
|
||||||
Path string `mapstructure:"path"`
|
Path string `mapstructure:"path"`
|
||||||
@ -44,6 +50,31 @@ type LuetGeneralConfig struct {
|
|||||||
FatalWarns bool `mapstructure:"fatal_warnings"`
|
FatalWarns bool `mapstructure:"fatal_warnings"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LuetSolverOptions struct {
|
||||||
|
Type string `mapstructure:"type"`
|
||||||
|
LearnRate float32 `mapstructure:"rate"`
|
||||||
|
Discount float32 `mapstructure:"discount"`
|
||||||
|
MaxAttempts int `mapstructure:"max_attempts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts LuetSolverOptions) Resolver() solver.PackageResolver {
|
||||||
|
switch opts.Type {
|
||||||
|
case solver.QLearningResolverType:
|
||||||
|
if opts.LearnRate != 0.0 {
|
||||||
|
return solver.NewQLearningResolver(opts.LearnRate, opts.Discount, opts.MaxAttempts, 999999)
|
||||||
|
|
||||||
|
}
|
||||||
|
return solver.SimpleQLearningSolver()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &solver.DummyPackageResolver{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *LuetSolverOptions) CompactString() string {
|
||||||
|
return fmt.Sprintf("type: %s rate: %f, discount: %f, attempts: %d, initialobserved: %d",
|
||||||
|
opts.Type, opts.LearnRate, opts.Discount, opts.MaxAttempts, 999999)
|
||||||
|
}
|
||||||
|
|
||||||
type LuetSystemConfig struct {
|
type LuetSystemConfig struct {
|
||||||
DatabaseEngine string `yaml:"database_engine" mapstructure:"database_engine"`
|
DatabaseEngine string `yaml:"database_engine" mapstructure:"database_engine"`
|
||||||
DatabasePath string `yaml:"database_path" mapstructure:"database_path"`
|
DatabasePath string `yaml:"database_path" mapstructure:"database_path"`
|
||||||
@ -51,6 +82,44 @@ type LuetSystemConfig struct {
|
|||||||
PkgsCachePath string `yaml:"pkgs_cache_path" mapstructure:"pkgs_cache_path"`
|
PkgsCachePath string `yaml:"pkgs_cache_path" mapstructure:"pkgs_cache_path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sc LuetSystemConfig) GetRepoDatabaseDirPath(name string) string {
|
||||||
|
dbpath := filepath.Join(sc.Rootfs, sc.DatabasePath)
|
||||||
|
dbpath = filepath.Join(dbpath, "repos/"+name)
|
||||||
|
err := os.MkdirAll(dbpath, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return dbpath
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc LuetSystemConfig) GetSystemRepoDatabaseDirPath() string {
|
||||||
|
dbpath := filepath.Join(sc.Rootfs,
|
||||||
|
sc.DatabasePath)
|
||||||
|
err := os.MkdirAll(dbpath, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return dbpath
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc LuetSystemConfig) GetSystemPkgsCacheDirPath() (ans string) {
|
||||||
|
var cachepath string
|
||||||
|
if sc.PkgsCachePath != "" {
|
||||||
|
cachepath = sc.PkgsCachePath
|
||||||
|
} else {
|
||||||
|
// Create dynamic cache for test suites
|
||||||
|
cachepath, _ = ioutil.TempDir(os.TempDir(), "cachepkgs")
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.IsAbs(cachepath) {
|
||||||
|
ans = cachepath
|
||||||
|
} else {
|
||||||
|
ans = filepath.Join(sc.GetSystemRepoDatabaseDirPath(), cachepath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
type LuetRepository struct {
|
type LuetRepository struct {
|
||||||
Name string `json:"name" yaml:"name" mapstructure:"name"`
|
Name string `json:"name" yaml:"name" mapstructure:"name"`
|
||||||
Description string `json:"description,omitempty" yaml:"description,omitempty" mapstructure:"description"`
|
Description string `json:"description,omitempty" yaml:"description,omitempty" mapstructure:"description"`
|
||||||
@ -112,6 +181,7 @@ type LuetConfig struct {
|
|||||||
Logging LuetLoggingConfig `mapstructure:"logging"`
|
Logging LuetLoggingConfig `mapstructure:"logging"`
|
||||||
General LuetGeneralConfig `mapstructure:"general"`
|
General LuetGeneralConfig `mapstructure:"general"`
|
||||||
System LuetSystemConfig `mapstructure:"system"`
|
System LuetSystemConfig `mapstructure:"system"`
|
||||||
|
Solver LuetSolverOptions `mapstructure:"solver"`
|
||||||
|
|
||||||
RepositoriesConfDir []string `mapstructure:"repos_confdir"`
|
RepositoriesConfDir []string `mapstructure:"repos_confdir"`
|
||||||
CacheRepositories []LuetRepository `mapstructure:"repetitors"`
|
CacheRepositories []LuetRepository `mapstructure:"repetitors"`
|
||||||
@ -154,6 +224,11 @@ func GenDefault(viper *v.Viper) {
|
|||||||
viper.SetDefault("repos_confdir", []string{"/etc/luet/repos.conf.d"})
|
viper.SetDefault("repos_confdir", []string{"/etc/luet/repos.conf.d"})
|
||||||
viper.SetDefault("cache_repositories", []string{})
|
viper.SetDefault("cache_repositories", []string{})
|
||||||
viper.SetDefault("system_repositories", []string{})
|
viper.SetDefault("system_repositories", []string{})
|
||||||
|
|
||||||
|
viper.SetDefault("solver.type", "")
|
||||||
|
viper.SetDefault("solver.rate", 0.7)
|
||||||
|
viper.SetDefault("solver.discount", 1.0)
|
||||||
|
viper.SetDefault("solver.max_attempts", 9000)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *LuetConfig) AddSystemRepository(r LuetRepository) {
|
func (c *LuetConfig) AddSystemRepository(r LuetRepository) {
|
||||||
@ -172,6 +247,10 @@ func (c *LuetConfig) GetSystem() *LuetSystemConfig {
|
|||||||
return &c.System
|
return &c.System
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *LuetConfig) GetSolverOptions() *LuetSolverOptions {
|
||||||
|
return &c.Solver
|
||||||
|
}
|
||||||
|
|
||||||
func (c *LuetConfig) GetSystemRepository(name string) (*LuetRepository, error) {
|
func (c *LuetConfig) GetSystemRepository(name string) (*LuetRepository, error) {
|
||||||
var ans *LuetRepository = nil
|
var ans *LuetRepository = nil
|
||||||
|
|
||||||
@ -188,6 +267,18 @@ func (c *LuetConfig) GetSystemRepository(name string) (*LuetRepository, error) {
|
|||||||
return ans, nil
|
return ans, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *LuetSolverOptions) String() string {
|
||||||
|
ans := fmt.Sprintf(`
|
||||||
|
solver:
|
||||||
|
type: %s
|
||||||
|
rate: %f
|
||||||
|
discount: %f
|
||||||
|
max_attempts: %d`, c.Type, c.LearnRate, c.Discount,
|
||||||
|
c.MaxAttempts)
|
||||||
|
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
func (c *LuetGeneralConfig) String() string {
|
func (c *LuetGeneralConfig) String() string {
|
||||||
ans := fmt.Sprintf(`
|
ans := fmt.Sprintf(`
|
||||||
general:
|
general:
|
||||||
|
@ -21,8 +21,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
. "github.com/mudler/luet/pkg/config"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -61,7 +59,7 @@ func Untar(src, dest string, sameOwner bool) error {
|
|||||||
}
|
}
|
||||||
defer in.Close()
|
defer in.Close()
|
||||||
|
|
||||||
if LuetCfg.GetGeneral().SameOwner {
|
if sameOwner {
|
||||||
// PRE: i have root privileged.
|
// PRE: i have root privileged.
|
||||||
|
|
||||||
opts := &archive.TarOptions{
|
opts := &archive.TarOptions{
|
||||||
|
24
pkg/helpers/math.go
Normal file
24
pkg/helpers/math.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright © 2020 Ettore Di Giacinto <mudler@gentoo.org>
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along
|
||||||
|
// with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package helpers
|
||||||
|
|
||||||
|
func Factorial(n uint64) (result uint64) {
|
||||||
|
if n > 0 {
|
||||||
|
result = n * Factorial(n-1)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
@ -1,63 +0,0 @@
|
|||||||
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
|
|
||||||
// Daniele Rondina <geaaru@sabayonlinux.org>
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation; either version 2 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along
|
|
||||||
// with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package helpers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/mudler/luet/pkg/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetRepoDatabaseDirPath(name string) string {
|
|
||||||
dbpath := filepath.Join(config.LuetCfg.GetSystem().Rootfs, config.LuetCfg.GetSystem().DatabasePath)
|
|
||||||
dbpath = filepath.Join(dbpath, "repos/"+name)
|
|
||||||
err := os.MkdirAll(dbpath, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return dbpath
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetSystemRepoDatabaseDirPath() string {
|
|
||||||
dbpath := filepath.Join(config.LuetCfg.GetSystem().Rootfs,
|
|
||||||
config.LuetCfg.GetSystem().DatabasePath)
|
|
||||||
err := os.MkdirAll(dbpath, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return dbpath
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetSystemPkgsCacheDirPath() (ans string) {
|
|
||||||
var cachepath string
|
|
||||||
if config.LuetCfg.GetSystem().PkgsCachePath != "" {
|
|
||||||
cachepath = config.LuetCfg.GetSystem().PkgsCachePath
|
|
||||||
} else {
|
|
||||||
// Create dynamic cache for test suites
|
|
||||||
cachepath, _ = ioutil.TempDir(os.TempDir(), "cachepkgs")
|
|
||||||
}
|
|
||||||
|
|
||||||
if filepath.IsAbs(cachepath) {
|
|
||||||
ans = cachepath
|
|
||||||
} else {
|
|
||||||
ans = filepath.Join(GetSystemRepoDatabaseDirPath(), cachepath)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -27,6 +27,7 @@ import (
|
|||||||
. "github.com/mudler/luet/pkg/logger"
|
. "github.com/mudler/luet/pkg/logger"
|
||||||
|
|
||||||
"github.com/mudler/luet/pkg/compiler"
|
"github.com/mudler/luet/pkg/compiler"
|
||||||
|
"github.com/mudler/luet/pkg/config"
|
||||||
"github.com/mudler/luet/pkg/helpers"
|
"github.com/mudler/luet/pkg/helpers"
|
||||||
|
|
||||||
"github.com/cavaliercoder/grab"
|
"github.com/cavaliercoder/grab"
|
||||||
@ -70,7 +71,7 @@ func (c *HttpClient) DownloadArtifact(artifact compiler.Artifact) (compiler.Arti
|
|||||||
var temp string
|
var temp string
|
||||||
|
|
||||||
artifactName := path.Base(artifact.GetPath())
|
artifactName := path.Base(artifact.GetPath())
|
||||||
cacheFile := filepath.Join(helpers.GetSystemPkgsCacheDirPath(), artifactName)
|
cacheFile := filepath.Join(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath(), artifactName)
|
||||||
ok := false
|
ok := false
|
||||||
|
|
||||||
// Check if file is already in cache
|
// Check if file is already in cache
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/mudler/luet/pkg/config"
|
||||||
. "github.com/mudler/luet/pkg/logger"
|
. "github.com/mudler/luet/pkg/logger"
|
||||||
|
|
||||||
"github.com/mudler/luet/pkg/compiler"
|
"github.com/mudler/luet/pkg/compiler"
|
||||||
@ -39,7 +40,7 @@ func (c *LocalClient) DownloadArtifact(artifact compiler.Artifact) (compiler.Art
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
artifactName := path.Base(artifact.GetPath())
|
artifactName := path.Base(artifact.GetPath())
|
||||||
cacheFile := filepath.Join(helpers.GetSystemPkgsCacheDirPath(), artifactName)
|
cacheFile := filepath.Join(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath(), artifactName)
|
||||||
|
|
||||||
// Check if file is already in cache
|
// Check if file is already in cache
|
||||||
if helpers.Exists(cacheFile) {
|
if helpers.Exists(cacheFile) {
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
compiler "github.com/mudler/luet/pkg/compiler"
|
compiler "github.com/mudler/luet/pkg/compiler"
|
||||||
|
"github.com/mudler/luet/pkg/config"
|
||||||
"github.com/mudler/luet/pkg/helpers"
|
"github.com/mudler/luet/pkg/helpers"
|
||||||
. "github.com/mudler/luet/pkg/logger"
|
. "github.com/mudler/luet/pkg/logger"
|
||||||
pkg "github.com/mudler/luet/pkg/package"
|
pkg "github.com/mudler/luet/pkg/package"
|
||||||
@ -34,9 +35,15 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type LuetInstallerOptions struct {
|
||||||
|
SolverOptions config.LuetSolverOptions
|
||||||
|
Concurrency int
|
||||||
|
}
|
||||||
|
|
||||||
type LuetInstaller struct {
|
type LuetInstaller struct {
|
||||||
PackageRepositories Repositories
|
PackageRepositories Repositories
|
||||||
Concurrency int
|
|
||||||
|
Options LuetInstallerOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
type ArtifactMatch struct {
|
type ArtifactMatch struct {
|
||||||
@ -86,8 +93,8 @@ func NewLuetFinalizerFromYaml(data []byte) (*LuetFinalizer, error) {
|
|||||||
return &p, err
|
return &p, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLuetInstaller(concurrency int) Installer {
|
func NewLuetInstaller(opts LuetInstallerOptions) Installer {
|
||||||
return &LuetInstaller{Concurrency: concurrency}
|
return &LuetInstaller{Options: opts}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LuetInstaller) Upgrade(s *System) error {
|
func (l *LuetInstaller) Upgrade(s *System) error {
|
||||||
@ -100,7 +107,7 @@ func (l *LuetInstaller) Upgrade(s *System) error {
|
|||||||
allRepos := pkg.NewInMemoryDatabase(false)
|
allRepos := pkg.NewInMemoryDatabase(false)
|
||||||
syncedRepos.SyncDatabase(allRepos)
|
syncedRepos.SyncDatabase(allRepos)
|
||||||
// compute a "big" world
|
// compute a "big" world
|
||||||
solv := solver.NewSolver(s.Database, allRepos, pkg.NewInMemoryDatabase(false))
|
solv := solver.NewResolver(s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
|
||||||
uninstall, solution, err := solv.Upgrade()
|
uninstall, solution, err := solv.Upgrade()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed solving solution for upgrade")
|
return errors.Wrap(err, "Failed solving solution for upgrade")
|
||||||
@ -179,7 +186,8 @@ 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)
|
||||||
solv := solver.NewSolver(s.Database, allRepos, pkg.NewInMemoryDatabase(false))
|
|
||||||
|
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 {
|
||||||
return errors.Wrap(err, "Failed solving solution for package")
|
return errors.Wrap(err, "Failed solving solution for package")
|
||||||
@ -214,7 +222,7 @@ func (l *LuetInstaller) Install(cp []pkg.Package, s *System) error {
|
|||||||
all := make(chan ArtifactMatch)
|
all := make(chan ArtifactMatch)
|
||||||
|
|
||||||
var wg = new(sync.WaitGroup)
|
var wg = new(sync.WaitGroup)
|
||||||
for i := 0; i < l.Concurrency; i++ {
|
for i := 0; i < l.Options.Concurrency; i++ {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go l.installerWorker(i, wg, all, s)
|
go l.installerWorker(i, wg, all, s)
|
||||||
}
|
}
|
||||||
@ -356,8 +364,7 @@ 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
|
||||||
|
solv := solver.NewResolver(s.Database, s.Database, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
|
||||||
solv := solver.NewSolver(s.Database, s.Database, pkg.NewInMemoryDatabase(false))
|
|
||||||
solution, err := solv.Uninstall(p)
|
solution, err := solv.Uninstall(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Uninstall failed")
|
return errors.Wrap(err, "Uninstall failed")
|
||||||
|
@ -99,7 +99,7 @@ var _ = Describe("Installer", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
defer os.RemoveAll(fakeroot) // clean up
|
defer os.RemoveAll(fakeroot) // clean up
|
||||||
|
|
||||||
inst := NewLuetInstaller(1)
|
inst := NewLuetInstaller(LuetInstallerOptions{Concurrency: 1})
|
||||||
repo2, err := NewLuetSystemRepositoryFromYaml([]byte(`
|
repo2, err := NewLuetSystemRepositoryFromYaml([]byte(`
|
||||||
name: "test"
|
name: "test"
|
||||||
type: "disk"
|
type: "disk"
|
||||||
@ -212,7 +212,7 @@ urls:
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
defer os.RemoveAll(fakeroot) // clean up
|
defer os.RemoveAll(fakeroot) // clean up
|
||||||
|
|
||||||
inst := NewLuetInstaller(1)
|
inst := NewLuetInstaller(LuetInstallerOptions{Concurrency: 1})
|
||||||
repo2, err := NewLuetSystemRepositoryFromYaml([]byte(`
|
repo2, err := NewLuetSystemRepositoryFromYaml([]byte(`
|
||||||
name: "test"
|
name: "test"
|
||||||
type: "disk"
|
type: "disk"
|
||||||
@ -320,7 +320,7 @@ urls:
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
defer os.RemoveAll(fakeroot) // clean up
|
defer os.RemoveAll(fakeroot) // clean up
|
||||||
|
|
||||||
inst := NewLuetInstaller(1)
|
inst := NewLuetInstaller(LuetInstallerOptions{Concurrency: 1})
|
||||||
repo2, err := NewLuetSystemRepositoryFromYaml([]byte(`
|
repo2, err := NewLuetSystemRepositoryFromYaml([]byte(`
|
||||||
name: "test"
|
name: "test"
|
||||||
type: "disk"
|
type: "disk"
|
||||||
@ -435,7 +435,7 @@ urls:
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
defer os.RemoveAll(fakeroot) // clean up
|
defer os.RemoveAll(fakeroot) // clean up
|
||||||
|
|
||||||
inst := NewLuetInstaller(1)
|
inst := NewLuetInstaller(LuetInstallerOptions{Concurrency: 1})
|
||||||
repo2, err := NewLuetSystemRepositoryFromYaml([]byte(`
|
repo2, err := NewLuetSystemRepositoryFromYaml([]byte(`
|
||||||
name: "test"
|
name: "test"
|
||||||
type: "disk"
|
type: "disk"
|
||||||
|
@ -360,7 +360,7 @@ func (r *LuetSystemRepository) Sync(force bool) (Repository, error) {
|
|||||||
return nil, errors.Wrap(err, "While downloading "+REPOSITORY_SPECFILE)
|
return nil, errors.Wrap(err, "While downloading "+REPOSITORY_SPECFILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
repobasedir := helpers.GetRepoDatabaseDirPath(r.GetName())
|
repobasedir := config.LuetCfg.GetSystem().GetRepoDatabaseDirPath(r.GetName())
|
||||||
repo, err := r.ReadSpecFile(file, false)
|
repo, err := r.ReadSpecFile(file, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -85,6 +85,8 @@ type Package interface {
|
|||||||
IsSelector() bool
|
IsSelector() bool
|
||||||
VersionMatchSelector(string) (bool, error)
|
VersionMatchSelector(string) (bool, error)
|
||||||
SelectorMatchVersion(string) (bool, error)
|
SelectorMatchVersion(string) (bool, error)
|
||||||
|
|
||||||
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tree interface {
|
type Tree interface {
|
||||||
@ -128,11 +130,11 @@ func (t *DefaultPackage) JSON() ([]byte, error) {
|
|||||||
|
|
||||||
// DefaultPackage represent a standard package definition
|
// DefaultPackage represent a standard package definition
|
||||||
type DefaultPackage struct {
|
type DefaultPackage struct {
|
||||||
ID int `storm:"id,increment"` // primary key with auto increment
|
ID int `storm:"id,increment" json:"id"` // primary key with auto increment
|
||||||
Name string `json:"name"` // Affects YAML field names too.
|
Name string `json:"name"` // Affects YAML field names too.
|
||||||
Version string `json:"version"` // Affects YAML field names too.
|
Version string `json:"version"` // Affects YAML field names too.
|
||||||
Category string `json:"category"` // Affects YAML field names too.
|
Category string `json:"category"` // Affects YAML field names too.
|
||||||
UseFlags []string `json:"use_flags"` // Affects YAML field names too.
|
UseFlags []string `json:"use_flags"` // Affects YAML field names too.
|
||||||
State State
|
State State
|
||||||
PackageRequires []*DefaultPackage `json:"requires"` // Affects YAML field names too.
|
PackageRequires []*DefaultPackage `json:"requires"` // Affects YAML field names too.
|
||||||
PackageConflicts []*DefaultPackage `json:"conflicts"` // Affects YAML field names too.
|
PackageConflicts []*DefaultPackage `json:"conflicts"` // Affects YAML field names too.
|
||||||
@ -160,7 +162,7 @@ func NewPackage(name, version string, requires []*DefaultPackage, conflicts []*D
|
|||||||
func (p *DefaultPackage) String() string {
|
func (p *DefaultPackage) String() string {
|
||||||
b, err := p.JSON()
|
b, err := p.JSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Sprintf("{ id: \"%d\", name: \"%s\" }", p.ID, p.Name)
|
return fmt.Sprintf("{ id: \"%d\", name: \"%s\", version: \"%s\", category: \"%s\" }", p.ID, p.Name, p.Version, p.Category)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s", string(b))
|
return fmt.Sprintf("%s", string(b))
|
||||||
}
|
}
|
||||||
@ -171,6 +173,16 @@ func (p *DefaultPackage) GetFingerPrint() string {
|
|||||||
return fmt.Sprintf("%s-%s-%s", p.Name, p.Category, p.Version)
|
return fmt.Sprintf("%s-%s-%s", p.Name, p.Category, p.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FromString(s string) Package {
|
||||||
|
var unescaped DefaultPackage
|
||||||
|
|
||||||
|
err := json.Unmarshal([]byte(s), &unescaped)
|
||||||
|
if err != nil {
|
||||||
|
return &unescaped
|
||||||
|
}
|
||||||
|
return &unescaped
|
||||||
|
}
|
||||||
|
|
||||||
func (p *DefaultPackage) GetPackageName() string {
|
func (p *DefaultPackage) GetPackageName() string {
|
||||||
return fmt.Sprintf("%s-%s", p.Name, p.Category)
|
return fmt.Sprintf("%s-%s", p.Name, p.Category)
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,15 @@ import (
|
|||||||
|
|
||||||
var _ = Describe("Package", func() {
|
var _ = Describe("Package", func() {
|
||||||
|
|
||||||
|
Context("Encoding/Decoding", func() {
|
||||||
|
a := &DefaultPackage{Name: "test", Version: "1", Category: "t"}
|
||||||
|
It("Encodes and decodes correctly", func() {
|
||||||
|
Expect(a.String()).ToNot(Equal(""))
|
||||||
|
p := FromString(a.String())
|
||||||
|
Expect(p).To(Equal(a))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Context("Simple package", func() {
|
Context("Simple package", func() {
|
||||||
a := NewPackage("A", ">=1.0", []*DefaultPackage{}, []*DefaultPackage{})
|
a := NewPackage("A", ">=1.0", []*DefaultPackage{}, []*DefaultPackage{})
|
||||||
a1 := NewPackage("A", "1.0", []*DefaultPackage{}, []*DefaultPackage{})
|
a1 := NewPackage("A", "1.0", []*DefaultPackage{}, []*DefaultPackage{})
|
||||||
|
336
pkg/solver/resolver.go
Normal file
336
pkg/solver/resolver.go
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
// Copyright © 2020 Ettore Di Giacinto <mudler@gentoo.org>
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along
|
||||||
|
// with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package solver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/crillab/gophersat/bf"
|
||||||
|
"github.com/mudler/luet/pkg/helpers"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
"github.com/ecooper/qlearning"
|
||||||
|
pkg "github.com/mudler/luet/pkg/package"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ActionType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
NoAction = 0
|
||||||
|
Solved = iota
|
||||||
|
NoSolution = iota
|
||||||
|
Going = iota
|
||||||
|
ActionRemoved = iota
|
||||||
|
ActionAdded = iota
|
||||||
|
|
||||||
|
DoNoop = false
|
||||||
|
|
||||||
|
ActionDomains = 3 // Bump it if you increase the number of actions
|
||||||
|
|
||||||
|
DefaultMaxAttempts = 9000
|
||||||
|
DefaultLearningRate = 0.7
|
||||||
|
DefaultDiscount = 1.0
|
||||||
|
DefaultInitialObserved = 999999
|
||||||
|
|
||||||
|
QLearningResolverType = "qlearning"
|
||||||
|
)
|
||||||
|
|
||||||
|
//. "github.com/mudler/luet/pkg/logger"
|
||||||
|
|
||||||
|
// PackageResolver assists PackageSolver on unsat cases
|
||||||
|
type PackageResolver interface {
|
||||||
|
Solve(bf.Formula, PackageSolver) (PackagesAssertions, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DummyPackageResolver struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*DummyPackageResolver) Solve(bf.Formula, PackageSolver) (PackagesAssertions, error) {
|
||||||
|
return nil, errors.New("Could not satisfy the constraints. Try again by removing deps ")
|
||||||
|
}
|
||||||
|
|
||||||
|
type QLearningResolver struct {
|
||||||
|
Attempts int
|
||||||
|
|
||||||
|
ToAttempt int
|
||||||
|
|
||||||
|
attempts int
|
||||||
|
|
||||||
|
Attempted map[string]bool
|
||||||
|
|
||||||
|
Solver PackageSolver
|
||||||
|
Formula bf.Formula
|
||||||
|
|
||||||
|
Targets []pkg.Package
|
||||||
|
Current []pkg.Package
|
||||||
|
|
||||||
|
observedDelta int
|
||||||
|
observedDeltaChoice []pkg.Package
|
||||||
|
|
||||||
|
Agent *qlearning.SimpleAgent
|
||||||
|
}
|
||||||
|
|
||||||
|
func SimpleQLearningSolver() PackageResolver {
|
||||||
|
return NewQLearningResolver(DefaultLearningRate, DefaultDiscount, DefaultMaxAttempts, DefaultInitialObserved)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defaults LearningRate 0.7, Discount 1.0
|
||||||
|
func NewQLearningResolver(LearningRate, Discount float32, MaxAttempts, initialObservedDelta int) PackageResolver {
|
||||||
|
return &QLearningResolver{
|
||||||
|
Agent: qlearning.NewSimpleAgent(LearningRate, Discount),
|
||||||
|
observedDelta: initialObservedDelta,
|
||||||
|
Attempts: MaxAttempts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (resolver *QLearningResolver) Solve(f bf.Formula, s PackageSolver) (PackagesAssertions, error) {
|
||||||
|
// Info("Using QLearning solver to resolve conflicts. Please be patient.")
|
||||||
|
resolver.Solver = s
|
||||||
|
|
||||||
|
s.SetResolver(&DummyPackageResolver{}) // Set dummy. Otherwise the attempts will run again a QLearning instance.
|
||||||
|
defer s.SetResolver(resolver) // Set back ourselves as resolver
|
||||||
|
|
||||||
|
resolver.Formula = f
|
||||||
|
|
||||||
|
// Our agent by default has a learning rate of 0.7 and discount of 1.0.
|
||||||
|
if resolver.Agent == nil {
|
||||||
|
resolver.Agent = qlearning.NewSimpleAgent(DefaultLearningRate, DefaultDiscount) // FIXME: Remove hardcoded values
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3 are the action domains, counting noop regardless if enabled or not
|
||||||
|
// get the permutations to attempt
|
||||||
|
resolver.ToAttempt = int(helpers.Factorial(uint64(len(resolver.Solver.(*Solver).Wanted)-1) * ActionDomains)) // TODO: type assertions must go away
|
||||||
|
resolver.Targets = resolver.Solver.(*Solver).Wanted
|
||||||
|
|
||||||
|
resolver.attempts = resolver.Attempts
|
||||||
|
|
||||||
|
resolver.Attempted = make(map[string]bool, len(resolver.Targets))
|
||||||
|
|
||||||
|
for resolver.IsComplete() == Going {
|
||||||
|
// Pick the next move, which is going to be a letter choice.
|
||||||
|
action := qlearning.Next(resolver.Agent, resolver)
|
||||||
|
|
||||||
|
// Whatever that choice is, let's update our model for its
|
||||||
|
// impact. If the package chosen makes the formula sat,
|
||||||
|
// then this action will be positive. Otherwise, it will be
|
||||||
|
// negative.
|
||||||
|
resolver.Agent.Learn(action, resolver)
|
||||||
|
|
||||||
|
// Reward doesn't change state so we can check what the
|
||||||
|
// reward would be for this action, and report how the
|
||||||
|
// env changed.
|
||||||
|
// score := resolver.Reward(action)
|
||||||
|
// if score > 0.0 {
|
||||||
|
// resolver.Log("%s was correct", action.Action.String())
|
||||||
|
// } else {
|
||||||
|
// resolver.Log("%s was incorrect", action.Action.String())
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get good result, take it
|
||||||
|
// Take the result also if we did reached overall maximum attempts
|
||||||
|
if resolver.IsComplete() == Solved || resolver.IsComplete() == NoSolution {
|
||||||
|
|
||||||
|
if len(resolver.observedDeltaChoice) != 0 {
|
||||||
|
// Take the minimum delta observed choice result, and consume it (Try sets the wanted list)
|
||||||
|
resolver.Solver.(*Solver).Wanted = resolver.observedDeltaChoice
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolver.Solver.Solve()
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("QLearning resolver failed ")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the current state.
|
||||||
|
func (resolver *QLearningResolver) IsComplete() int {
|
||||||
|
if resolver.attempts < 1 {
|
||||||
|
return NoSolution
|
||||||
|
}
|
||||||
|
|
||||||
|
if resolver.ToAttempt > 0 {
|
||||||
|
return Going
|
||||||
|
}
|
||||||
|
|
||||||
|
return Solved
|
||||||
|
}
|
||||||
|
|
||||||
|
func (resolver *QLearningResolver) Try(c Choice) error {
|
||||||
|
pack := c.Package
|
||||||
|
packtoAdd := pkg.FromString(pack)
|
||||||
|
resolver.Attempted[pack+strconv.Itoa(int(c.Action))] = true // increase the count
|
||||||
|
s, _ := resolver.Solver.(*Solver)
|
||||||
|
var filtered []pkg.Package
|
||||||
|
|
||||||
|
switch c.Action {
|
||||||
|
case ActionAdded:
|
||||||
|
found := false
|
||||||
|
for _, p := range s.Wanted {
|
||||||
|
if p.String() == pack {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
resolver.Solver.(*Solver).Wanted = append(resolver.Solver.(*Solver).Wanted, packtoAdd)
|
||||||
|
}
|
||||||
|
|
||||||
|
case ActionRemoved:
|
||||||
|
for _, p := range s.Wanted {
|
||||||
|
if p.String() != pack {
|
||||||
|
filtered = append(filtered, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolver.Solver.(*Solver).Wanted = filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := resolver.Solver.Solve()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choose applies a pack attempt, returning
|
||||||
|
// true if the formula returns sat.
|
||||||
|
//
|
||||||
|
// Choose updates the resolver's state.
|
||||||
|
func (resolver *QLearningResolver) Choose(c Choice) bool {
|
||||||
|
//pack := pkg.FromString(c.Package)
|
||||||
|
|
||||||
|
err := resolver.Try(c)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
resolver.ToAttempt--
|
||||||
|
resolver.attempts-- // Decrease attempts - it's a barrier. We could also do not decrease it here, allowing more attempts to be made
|
||||||
|
} else {
|
||||||
|
resolver.attempts--
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reward returns a score for a given qlearning.StateAction. Reward is a
|
||||||
|
// member of the qlearning.Rewarder interface. If the choice will make sat the formula, a positive score is returned.
|
||||||
|
// Otherwise, a static -1000 is returned.
|
||||||
|
func (resolver *QLearningResolver) Reward(action *qlearning.StateAction) float32 {
|
||||||
|
choice := action.Action.(*Choice)
|
||||||
|
|
||||||
|
//_, err := resolver.Solver.Solve()
|
||||||
|
err := resolver.Try(*choice)
|
||||||
|
|
||||||
|
toBeInstalled := len(resolver.Solver.(*Solver).Wanted)
|
||||||
|
originalTarget := len(resolver.Targets)
|
||||||
|
noaction := choice.Action == NoAction
|
||||||
|
delta := originalTarget - toBeInstalled
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
// if toBeInstalled == originalTarget { // Base case: all the targets matches (it shouldn't happen, but lets put a higher)
|
||||||
|
// Debug("Target match, maximum score")
|
||||||
|
// return 24.0 / float32(len(resolver.Attempted))
|
||||||
|
|
||||||
|
// }
|
||||||
|
if DoNoop {
|
||||||
|
if noaction && toBeInstalled == 0 { // We decided to stay in the current state, and no targets have been chosen
|
||||||
|
return -100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if delta <= resolver.observedDelta { // Try to maximise observedDelta
|
||||||
|
resolver.observedDelta = delta
|
||||||
|
resolver.observedDeltaChoice = resolver.Solver.(*Solver).Wanted // we store it as this is our return value at the end
|
||||||
|
return 24.0 / float32(len(resolver.Attempted))
|
||||||
|
} else if toBeInstalled > 0 { // If we installed something, at least give a good score
|
||||||
|
return 24.0 / float32(len(resolver.Attempted))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1000
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next creates a new slice of qlearning.Action instances. A possible
|
||||||
|
// action is created for each package that could be removed from the formula's target
|
||||||
|
func (resolver *QLearningResolver) Next() []qlearning.Action {
|
||||||
|
actions := make([]qlearning.Action, 0, (len(resolver.Targets)-1)*3)
|
||||||
|
|
||||||
|
TARGETS:
|
||||||
|
for _, pack := range resolver.Targets {
|
||||||
|
for _, current := range resolver.Solver.(*Solver).Wanted {
|
||||||
|
if current.String() == pack.String() {
|
||||||
|
actions = append(actions, &Choice{Package: pack.String(), Action: ActionRemoved})
|
||||||
|
continue TARGETS
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
actions = append(actions, &Choice{Package: pack.String(), Action: ActionAdded})
|
||||||
|
}
|
||||||
|
|
||||||
|
if DoNoop {
|
||||||
|
actions = append(actions, &Choice{Package: "", Action: NoAction}) // NOOP
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log is a wrapper of fmt.Printf. If Game.debug is true, Log will print
|
||||||
|
// to stdout.
|
||||||
|
func (resolver *QLearningResolver) Log(msg string, args ...interface{}) {
|
||||||
|
logMsg := fmt.Sprintf("(%d moves, %d remaining attempts) %s\n", len(resolver.Attempted), resolver.attempts, msg)
|
||||||
|
fmt.Println(fmt.Sprintf(logMsg, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a consistent hash for the current env state to be
|
||||||
|
// used in a qlearning.Agent.
|
||||||
|
func (resolver *QLearningResolver) String() string {
|
||||||
|
return fmt.Sprintf("%v", resolver.Solver.(*Solver).Wanted)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choice implements qlearning.Action for a package choice for removal from wanted targets
|
||||||
|
type Choice struct {
|
||||||
|
Package string `json:"pack"`
|
||||||
|
Action ActionType `json:"action"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChoiceFromString(s string) (*Choice, error) {
|
||||||
|
var p *Choice
|
||||||
|
err := yaml.Unmarshal([]byte(s), &p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the character for the current action.
|
||||||
|
func (choice *Choice) String() string {
|
||||||
|
data, err := json.Marshal(choice)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply updates the state of the solver for the package choice.
|
||||||
|
func (choice *Choice) Apply(state qlearning.State) qlearning.State {
|
||||||
|
resolver := state.(*QLearningResolver)
|
||||||
|
resolver.Choose(*choice)
|
||||||
|
|
||||||
|
return resolver
|
||||||
|
}
|
182
pkg/solver/resolver_test.go
Normal file
182
pkg/solver/resolver_test.go
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along
|
||||||
|
// with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package solver_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
pkg "github.com/mudler/luet/pkg/package"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
. "github.com/mudler/luet/pkg/solver"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Resolver", func() {
|
||||||
|
|
||||||
|
db := pkg.NewInMemoryDatabase(false)
|
||||||
|
dbInstalled := pkg.NewInMemoryDatabase(false)
|
||||||
|
dbDefinitions := pkg.NewInMemoryDatabase(false)
|
||||||
|
s := NewSolver(dbInstalled, dbDefinitions, db)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
db = pkg.NewInMemoryDatabase(false)
|
||||||
|
dbInstalled = pkg.NewInMemoryDatabase(false)
|
||||||
|
dbDefinitions = pkg.NewInMemoryDatabase(false)
|
||||||
|
s = NewSolver(dbInstalled, dbDefinitions, db)
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("Conflict set", func() {
|
||||||
|
Context("DummyPackageResolver", func() {
|
||||||
|
It("is unsolvable - as we something we ask to install conflict with system stuff", func() {
|
||||||
|
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C})
|
||||||
|
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{A, B, C} {
|
||||||
|
_, err := dbDefinitions.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{C} {
|
||||||
|
_, err := dbInstalled.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
solution, err := s.Install([]pkg.Package{A})
|
||||||
|
Expect(len(solution)).To(Equal(0))
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
})
|
||||||
|
It("succeeds to install D and F if explictly requested", func() {
|
||||||
|
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C})
|
||||||
|
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
|
||||||
|
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
E := pkg.NewPackage("E", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
|
||||||
|
F := pkg.NewPackage("F", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{A, B, C, D, E, F} {
|
||||||
|
_, err := dbDefinitions.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{C} {
|
||||||
|
_, err := dbInstalled.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
solution, err := s.Install([]pkg.Package{D, F}) // D and F should go as they have no deps. A/E should be filtered by QLearn
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(len(solution)).To(Equal(6))
|
||||||
|
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: A, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: E, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: F, Value: true}))
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
Context("QLearningResolver", func() {
|
||||||
|
It("will find out that we can install D by ignoring A", func() {
|
||||||
|
s.SetResolver(SimpleQLearningSolver())
|
||||||
|
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C})
|
||||||
|
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
|
||||||
|
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{A, B, C, D} {
|
||||||
|
_, err := dbDefinitions.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{C} {
|
||||||
|
_, err := dbInstalled.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
solution, err := s.Install([]pkg.Package{A, D})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: A, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
|
||||||
|
|
||||||
|
Expect(len(solution)).To(Equal(4))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("will find out that we can install D and F by ignoring E and A", func() {
|
||||||
|
s.SetResolver(SimpleQLearningSolver())
|
||||||
|
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C})
|
||||||
|
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
|
||||||
|
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
E := pkg.NewPackage("E", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
|
||||||
|
F := pkg.NewPackage("F", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{A, B, C, D, E, F} {
|
||||||
|
_, err := dbDefinitions.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{C} {
|
||||||
|
_, err := dbInstalled.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
solution, err := s.Install([]pkg.Package{A, D, E, F}) // D and F should go as they have no deps. A/E should be filtered by QLearn
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: A, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true})) // Was already installed
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: E, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: F, Value: true}))
|
||||||
|
Expect(len(solution)).To(Equal(6))
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("DummyPackageResolver", func() {
|
||||||
|
It("cannot find a solution", func() {
|
||||||
|
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C})
|
||||||
|
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
|
||||||
|
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{A, B, C, D} {
|
||||||
|
_, err := dbDefinitions.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{C} {
|
||||||
|
_, err := dbInstalled.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
solution, err := s.Install([]pkg.Package{A, D})
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(len(solution)).To(Equal(0))
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -33,6 +33,10 @@ type PackageSolver interface {
|
|||||||
ConflictsWith(p pkg.Package, ls []pkg.Package) (bool, error)
|
ConflictsWith(p pkg.Package, ls []pkg.Package) (bool, error)
|
||||||
World() []pkg.Package
|
World() []pkg.Package
|
||||||
Upgrade() ([]pkg.Package, PackagesAssertions, error)
|
Upgrade() ([]pkg.Package, PackagesAssertions, error)
|
||||||
|
|
||||||
|
SetResolver(PackageResolver)
|
||||||
|
|
||||||
|
Solve() (PackagesAssertions, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Solver is the default solver for luet
|
// Solver is the default solver for luet
|
||||||
@ -41,20 +45,33 @@ type Solver struct {
|
|||||||
SolverDatabase pkg.PackageDatabase
|
SolverDatabase pkg.PackageDatabase
|
||||||
Wanted []pkg.Package
|
Wanted []pkg.Package
|
||||||
InstalledDatabase pkg.PackageDatabase
|
InstalledDatabase pkg.PackageDatabase
|
||||||
|
|
||||||
|
Resolver PackageResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSolver accepts as argument two lists of packages, the first is the initial set,
|
// NewSolver accepts as argument two lists of packages, the first is the initial set,
|
||||||
// the second represent all the known packages.
|
// the second represent all the known packages.
|
||||||
func NewSolver(installed pkg.PackageDatabase, definitiondb pkg.PackageDatabase, solverdb pkg.PackageDatabase) PackageSolver {
|
func NewSolver(installed pkg.PackageDatabase, definitiondb pkg.PackageDatabase, solverdb pkg.PackageDatabase) PackageSolver {
|
||||||
return &Solver{InstalledDatabase: installed, DefinitionDatabase: definitiondb, SolverDatabase: solverdb}
|
return NewResolver(installed, definitiondb, solverdb, &DummyPackageResolver{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetWorld is a setter for the list of all known packages to the solver
|
// NewReSolver accepts as argument two lists of packages, the first is the initial set,
|
||||||
|
// the second represent all the known packages.
|
||||||
|
func NewResolver(installed pkg.PackageDatabase, definitiondb pkg.PackageDatabase, solverdb pkg.PackageDatabase, re PackageResolver) PackageSolver {
|
||||||
|
return &Solver{InstalledDatabase: installed, DefinitionDatabase: definitiondb, SolverDatabase: solverdb, Resolver: re}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefinitionDatabase is a setter for the definition Database
|
||||||
|
|
||||||
func (s *Solver) SetDefinitionDatabase(db pkg.PackageDatabase) {
|
func (s *Solver) SetDefinitionDatabase(db pkg.PackageDatabase) {
|
||||||
s.DefinitionDatabase = db
|
s.DefinitionDatabase = db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetResolver is a setter for the unsat resolver backend
|
||||||
|
func (s *Solver) SetResolver(r PackageResolver) {
|
||||||
|
s.Resolver = r
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Solver) World() []pkg.Package {
|
func (s *Solver) World() []pkg.Package {
|
||||||
return s.DefinitionDatabase.World()
|
return s.DefinitionDatabase.World()
|
||||||
}
|
}
|
||||||
@ -221,6 +238,7 @@ func (s *Solver) Upgrade() ([]pkg.Package, PackagesAssertions, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s2 := NewSolver(installedcopy, s.DefinitionDatabase, pkg.NewInMemoryDatabase(false))
|
s2 := NewSolver(installedcopy, s.DefinitionDatabase, pkg.NewInMemoryDatabase(false))
|
||||||
|
s2.SetResolver(s.Resolver)
|
||||||
// Then try to uninstall the versions in the system, and store that tree
|
// Then try to uninstall the versions in the system, and store that tree
|
||||||
for _, p := range toUninstall {
|
for _, p := range toUninstall {
|
||||||
r, err := s.Uninstall(p)
|
r, err := s.Uninstall(p)
|
||||||
@ -361,13 +379,20 @@ func (s *Solver) solve(f bf.Formula) (map[string]bool, bf.Formula, error) {
|
|||||||
|
|
||||||
// Solve builds the formula given the current state and returns package assertions
|
// Solve builds the formula given the current state and returns package assertions
|
||||||
func (s *Solver) Solve() (PackagesAssertions, error) {
|
func (s *Solver) Solve() (PackagesAssertions, error) {
|
||||||
|
var model map[string]bool
|
||||||
|
var err error
|
||||||
|
|
||||||
f, err := s.BuildFormula()
|
f, err := s.BuildFormula()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
model, _, err := s.solve(f)
|
model, _, err = s.solve(f)
|
||||||
|
if err != nil && s.Resolver != nil {
|
||||||
|
return s.Resolver.Solve(f, s)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
8
tests/fixtures/qlearning/a/build.yaml
vendored
Normal file
8
tests/fixtures/qlearning/a/build.yaml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
prelude:
|
||||||
|
- echo a > /aprelude
|
||||||
|
steps:
|
||||||
|
- echo a > /a
|
||||||
|
requires:
|
||||||
|
- category: "test"
|
||||||
|
name: "b"
|
||||||
|
version: "1.0"
|
8
tests/fixtures/qlearning/a/definition.yaml
vendored
Normal file
8
tests/fixtures/qlearning/a/definition.yaml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
category: "test"
|
||||||
|
name: "a"
|
||||||
|
version: "1.0"
|
||||||
|
requires:
|
||||||
|
- category: "test"
|
||||||
|
name: "b"
|
||||||
|
version: "1.0"
|
||||||
|
|
5
tests/fixtures/qlearning/b/build.yaml
vendored
Normal file
5
tests/fixtures/qlearning/b/build.yaml
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
image: "alpine"
|
||||||
|
prelude:
|
||||||
|
- echo b > /bprelude
|
||||||
|
steps:
|
||||||
|
- echo b > /b
|
7
tests/fixtures/qlearning/b/definition.yaml
vendored
Normal file
7
tests/fixtures/qlearning/b/definition.yaml
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
category: "test"
|
||||||
|
name: "b"
|
||||||
|
version: "1.0"
|
||||||
|
conflicts:
|
||||||
|
- category: "test"
|
||||||
|
name: "c"
|
||||||
|
version: "1.0"
|
5
tests/fixtures/qlearning/c/build.yaml
vendored
Normal file
5
tests/fixtures/qlearning/c/build.yaml
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
image: "alpine"
|
||||||
|
prelude:
|
||||||
|
- echo c > /cprelude
|
||||||
|
steps:
|
||||||
|
- echo c > /c
|
3
tests/fixtures/qlearning/c/definition.yaml
vendored
Normal file
3
tests/fixtures/qlearning/c/definition.yaml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
category: "test"
|
||||||
|
name: "c"
|
||||||
|
version: "1.0"
|
5
tests/fixtures/qlearning/d/build.yaml
vendored
Normal file
5
tests/fixtures/qlearning/d/build.yaml
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
image: "alpine"
|
||||||
|
prelude:
|
||||||
|
- echo d > /dprelude
|
||||||
|
steps:
|
||||||
|
- echo d > /d
|
3
tests/fixtures/qlearning/d/definition.yaml
vendored
Normal file
3
tests/fixtures/qlearning/d/definition.yaml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
category: "test"
|
||||||
|
name: "d"
|
||||||
|
version: "1.0"
|
9
tests/fixtures/qlearning/e/build.yaml
vendored
Normal file
9
tests/fixtures/qlearning/e/build.yaml
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
prelude:
|
||||||
|
- echo e > /eprelude
|
||||||
|
steps:
|
||||||
|
- echo e > /e
|
||||||
|
requires:
|
||||||
|
- category: "test"
|
||||||
|
name: "b"
|
||||||
|
version: "1.0"
|
||||||
|
|
8
tests/fixtures/qlearning/e/definition.yaml
vendored
Normal file
8
tests/fixtures/qlearning/e/definition.yaml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
category: "test"
|
||||||
|
name: "e"
|
||||||
|
version: "1.0"
|
||||||
|
requires:
|
||||||
|
- category: "test"
|
||||||
|
name: "b"
|
||||||
|
version: "1.0"
|
||||||
|
|
5
tests/fixtures/qlearning/f/build.yaml
vendored
Normal file
5
tests/fixtures/qlearning/f/build.yaml
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
prelude:
|
||||||
|
- echo f > /eprelude
|
||||||
|
steps:
|
||||||
|
- echo f > /f
|
||||||
|
image: "alpine"
|
3
tests/fixtures/qlearning/f/definition.yaml
vendored
Normal file
3
tests/fixtures/qlearning/f/definition.yaml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
category: "test"
|
||||||
|
name: "f"
|
||||||
|
version: "1.0"
|
94
tests/integration/03_qlearning.sh
Executable file
94
tests/integration/03_qlearning.sh
Executable file
@ -0,0 +1,94 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export LUET_NOLOCK=true
|
||||||
|
|
||||||
|
oneTimeSetUp() {
|
||||||
|
export tmpdir="$(mktemp -d)"
|
||||||
|
}
|
||||||
|
|
||||||
|
oneTimeTearDown() {
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
}
|
||||||
|
|
||||||
|
testBuild() {
|
||||||
|
mkdir $tmpdir/testbuild
|
||||||
|
luet build --all --tree "$ROOT_DIR/tests/fixtures/qlearning" --destination $tmpdir/testbuild --compression gzip
|
||||||
|
buildst=$?
|
||||||
|
assertEquals 'builds successfully' "$buildst" "0"
|
||||||
|
assertTrue 'create package dep B' "[ -e '$tmpdir/testbuild/b-test-1.0.package.tar.gz' ]"
|
||||||
|
assertTrue 'create package' "[ -e '$tmpdir/testbuild/c-test-1.0.package.tar.gz' ]"
|
||||||
|
}
|
||||||
|
|
||||||
|
testRepo() {
|
||||||
|
assertTrue 'no repository' "[ ! -e '$tmpdir/testbuild/repository.yaml' ]"
|
||||||
|
luet create-repo --tree "$ROOT_DIR/tests/fixtures/qlearning" \
|
||||||
|
--output $tmpdir/testbuild \
|
||||||
|
--packages $tmpdir/testbuild \
|
||||||
|
--name "test" \
|
||||||
|
--descr "Test Repo" \
|
||||||
|
--urls $tmpdir/testrootfs \
|
||||||
|
--type disk > /dev/null
|
||||||
|
|
||||||
|
createst=$?
|
||||||
|
assertEquals 'create repo successfully' "$createst" "0"
|
||||||
|
assertTrue 'create repository' "[ -e '$tmpdir/testbuild/repository.yaml' ]"
|
||||||
|
}
|
||||||
|
|
||||||
|
testConfig() {
|
||||||
|
mkdir $tmpdir/testrootfs
|
||||||
|
cat <<EOF > $tmpdir/luet.yaml
|
||||||
|
general:
|
||||||
|
debug: true
|
||||||
|
system:
|
||||||
|
rootfs: $tmpdir/testrootfs
|
||||||
|
database_path: "/"
|
||||||
|
database_engine: "boltdb"
|
||||||
|
repositories:
|
||||||
|
- name: "main"
|
||||||
|
type: "disk"
|
||||||
|
enable: true
|
||||||
|
urls:
|
||||||
|
- "$tmpdir/testbuild"
|
||||||
|
EOF
|
||||||
|
luet config --config $tmpdir/luet.yaml
|
||||||
|
res=$?
|
||||||
|
assertEquals 'config test successfully' "$res" "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
testInstall() {
|
||||||
|
luet install --config $tmpdir/luet.yaml test/c
|
||||||
|
#luet install --config $tmpdir/luet.yaml test/c-1.0 > /dev/null
|
||||||
|
installst=$?
|
||||||
|
assertEquals 'install test successfully' "$installst" "0"
|
||||||
|
assertTrue 'package C installed' "[ -e '$tmpdir/testrootfs/c' ]"
|
||||||
|
}
|
||||||
|
|
||||||
|
testFullInstall() {
|
||||||
|
output=$(luet install --config $tmpdir/luet.yaml test/d test/f test/e test/a)
|
||||||
|
installst=$?
|
||||||
|
assertEquals 'cannot install' "$installst" "1"
|
||||||
|
assertTrue 'package D installed' "[ ! -e '$tmpdir/testrootfs/d' ]"
|
||||||
|
assertTrue 'package F installed' "[ ! -e '$tmpdir/testrootfs/f' ]"
|
||||||
|
}
|
||||||
|
|
||||||
|
testInstallAgain() {
|
||||||
|
output=$(luet install --solver-type qlearning --config $tmpdir/luet.yaml test/d test/f test/e test/a)
|
||||||
|
installst=$?
|
||||||
|
assertEquals 'install test successfully' "$installst" "0"
|
||||||
|
assertNotContains 'contains warning' "$output" 'Filtering out'
|
||||||
|
assertTrue 'package D installed' "[ -e '$tmpdir/testrootfs/d' ]"
|
||||||
|
assertTrue 'package F installed' "[ -e '$tmpdir/testrootfs/f' ]"
|
||||||
|
assertTrue 'package E not installed' "[ ! -e '$tmpdir/testrootfs/e' ]"
|
||||||
|
assertTrue 'package A not installed' "[ ! -e '$tmpdir/testrootfs/a' ]"
|
||||||
|
}
|
||||||
|
|
||||||
|
testCleanup() {
|
||||||
|
luet cleanup --config $tmpdir/luet.yaml
|
||||||
|
installst=$?
|
||||||
|
assertEquals 'install test successfully' "$installst" "0"
|
||||||
|
assertTrue 'package installed' "[ ! -e '$tmpdir/testrootfs/packages/c-test-1.0.package.tar.gz' ]"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load shUnit2.
|
||||||
|
. "$ROOT_DIR/tests/integration/shunit2"/shunit2
|
||||||
|
|
@ -11,7 +11,7 @@ popd
|
|||||||
|
|
||||||
export PATH=$ROOT_DIR/tests/integration/bin/:$PATH
|
export PATH=$ROOT_DIR/tests/integration/bin/:$PATH
|
||||||
|
|
||||||
"$ROOT_DIR/tests/integration/01_simple.sh"
|
for script in $(ls "$ROOT_DIR/tests/integration/" | grep '^[0-9]*_.*.sh'); do
|
||||||
"$ROOT_DIR/tests/integration/01_simple_gzip.sh"
|
echo "Executing script '$script'."
|
||||||
"$ROOT_DIR/tests/integration/02_create_repo_from_config.sh"
|
$ROOT_DIR/tests/integration/$script
|
||||||
|
done
|
8
vendor/github.com/crillab/gophersat/bf/bf.go
generated
vendored
8
vendor/github.com/crillab/gophersat/bf/bf.go
generated
vendored
@ -72,8 +72,8 @@ type trueConst struct{}
|
|||||||
// True is the constant denoting a tautology.
|
// True is the constant denoting a tautology.
|
||||||
var True Formula = trueConst{}
|
var True Formula = trueConst{}
|
||||||
|
|
||||||
func (t trueConst) nnf() Formula { return t }
|
func (t trueConst) nnf() Formula { return t }
|
||||||
func (t trueConst) String() string { return "⊤" }
|
func (t trueConst) String() string { return "⊤" }
|
||||||
func (t trueConst) Eval(model map[string]bool) bool { return true }
|
func (t trueConst) Eval(model map[string]bool) bool { return true }
|
||||||
|
|
||||||
// The "false" constant.
|
// The "false" constant.
|
||||||
@ -82,8 +82,8 @@ type falseConst struct{}
|
|||||||
// False is the constant denoting a contradiction.
|
// False is the constant denoting a contradiction.
|
||||||
var False Formula = falseConst{}
|
var False Formula = falseConst{}
|
||||||
|
|
||||||
func (f falseConst) nnf() Formula { return f }
|
func (f falseConst) nnf() Formula { return f }
|
||||||
func (f falseConst) String() string { return "⊥" }
|
func (f falseConst) String() string { return "⊥" }
|
||||||
func (f falseConst) Eval(model map[string]bool) bool { return false }
|
func (f falseConst) Eval(model map[string]bool) bool { return false }
|
||||||
|
|
||||||
// Var generates a named boolean variable in a formula.
|
// Var generates a named boolean variable in a formula.
|
||||||
|
5
vendor/github.com/crillab/gophersat/solver/card.go
generated
vendored
5
vendor/github.com/crillab/gophersat/solver/card.go
generated
vendored
@ -15,10 +15,11 @@ func AtLeast1(lits ...int) CardConstr {
|
|||||||
|
|
||||||
// AtMost1 returns a cardinality constraint stating that at most one of the given lits can be true.
|
// AtMost1 returns a cardinality constraint stating that at most one of the given lits can be true.
|
||||||
func AtMost1(lits ...int) CardConstr {
|
func AtMost1(lits ...int) CardConstr {
|
||||||
|
negated := make([]int, len(lits))
|
||||||
for i, lit := range lits {
|
for i, lit := range lits {
|
||||||
lits[i] = -lit
|
negated[i] = -lit
|
||||||
}
|
}
|
||||||
return CardConstr{Lits: lits, AtLeast: len(lits) - 1}
|
return CardConstr{Lits: negated, AtLeast: len(lits) - 1}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exactly1 returns two cardinality constraints stating that exactly one of the given lits must be true.
|
// Exactly1 returns two cardinality constraints stating that exactly one of the given lits must be true.
|
||||||
|
10
vendor/github.com/crillab/gophersat/solver/parser.go
generated
vendored
10
vendor/github.com/crillab/gophersat/solver/parser.go
generated
vendored
@ -55,17 +55,21 @@ func ParseSlice(cnf [][]int) *Problem {
|
|||||||
return &pb
|
return &pb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pb.simplify()
|
pb.simplify2()
|
||||||
return &pb
|
return &pb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isSpace(b byte) bool {
|
||||||
|
return b == ' ' || b == '\t' || b == '\n' || b == '\r'
|
||||||
|
}
|
||||||
|
|
||||||
// readInt reads an int from r.
|
// readInt reads an int from r.
|
||||||
// 'b' is the last read byte. It can be a space, a '-' or a digit.
|
// 'b' is the last read byte. It can be a space, a '-' or a digit.
|
||||||
// The int can be negated.
|
// The int can be negated.
|
||||||
// All spaces before the int value are ignored.
|
// All spaces before the int value are ignored.
|
||||||
// Can return EOF.
|
// Can return EOF.
|
||||||
func readInt(b *byte, r *bufio.Reader) (res int, err error) {
|
func readInt(b *byte, r *bufio.Reader) (res int, err error) {
|
||||||
for err == nil && (*b == ' ' || *b == '\t' || *b == '\n' || *b == '\r') {
|
for err == nil && isSpace(*b) {
|
||||||
*b, err = r.ReadByte()
|
*b, err = r.ReadByte()
|
||||||
}
|
}
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@ -88,7 +92,7 @@ func readInt(b *byte, r *bufio.Reader) (res int, err error) {
|
|||||||
}
|
}
|
||||||
res = 10*res + int(*b-'0')
|
res = 10*res + int(*b-'0')
|
||||||
*b, err = r.ReadByte()
|
*b, err = r.ReadByte()
|
||||||
if *b == ' ' || *b == '\t' || *b == '\n' || *b == '\r' {
|
if isSpace(*b) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
vendor/github.com/crillab/gophersat/solver/parser_pb.go
generated
vendored
14
vendor/github.com/crillab/gophersat/solver/parser_pb.go
generated
vendored
@ -65,6 +65,14 @@ func ParseCardConstrs(constrs []CardConstr) *Problem {
|
|||||||
return &pb
|
return &pb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pb *Problem) appendClause(constr PBConstr) {
|
||||||
|
lits := make([]Lit, len(constr.Lits))
|
||||||
|
for j, val := range constr.Lits {
|
||||||
|
lits[j] = IntToLit(int32(val))
|
||||||
|
}
|
||||||
|
pb.Clauses = append(pb.Clauses, NewPBClause(lits, constr.Weights, constr.AtLeast))
|
||||||
|
}
|
||||||
|
|
||||||
// ParsePBConstrs parses and returns a PB problem from PBConstr values.
|
// ParsePBConstrs parses and returns a PB problem from PBConstr values.
|
||||||
func ParsePBConstrs(constrs []PBConstr) *Problem {
|
func ParsePBConstrs(constrs []PBConstr) *Problem {
|
||||||
var pb Problem
|
var pb Problem
|
||||||
@ -100,11 +108,7 @@ func ParsePBConstrs(constrs []PBConstr) *Problem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lits := make([]Lit, len(constr.Lits))
|
pb.appendClause(constr)
|
||||||
for j, val := range constr.Lits {
|
|
||||||
lits[j] = IntToLit(int32(val))
|
|
||||||
}
|
|
||||||
pb.Clauses = append(pb.Clauses, NewPBClause(lits, constr.Weights, card))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pb.Model = make([]decLevel, pb.NbVars)
|
pb.Model = make([]decLevel, pb.NbVars)
|
||||||
|
7
vendor/github.com/crillab/gophersat/solver/pb.go
generated
vendored
7
vendor/github.com/crillab/gophersat/solver/pb.go
generated
vendored
@ -57,12 +57,17 @@ func GtEq(lits []int, weights []int, n int) PBConstr {
|
|||||||
if len(weights) != 0 && len(lits) != len(weights) {
|
if len(weights) != 0 && len(lits) != len(weights) {
|
||||||
panic("not as many lits as weights")
|
panic("not as many lits as weights")
|
||||||
}
|
}
|
||||||
for i := range weights {
|
for i := 0; i < len(weights); i++ {
|
||||||
if weights[i] < 0 {
|
if weights[i] < 0 {
|
||||||
weights[i] = -weights[i]
|
weights[i] = -weights[i]
|
||||||
n += weights[i]
|
n += weights[i]
|
||||||
lits[i] = -lits[i]
|
lits[i] = -lits[i]
|
||||||
}
|
}
|
||||||
|
if weights[i] == 0 {
|
||||||
|
weights = append(weights[:i], weights[i+1:]...)
|
||||||
|
lits = append(lits[:i], lits[i+1:]...)
|
||||||
|
i--
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return PBConstr{Lits: lits, Weights: weights, AtLeast: n}
|
return PBConstr{Lits: lits, Weights: weights, AtLeast: n}
|
||||||
}
|
}
|
||||||
|
14
vendor/github.com/crillab/gophersat/solver/solver.go
generated
vendored
14
vendor/github.com/crillab/gophersat/solver/solver.go
generated
vendored
@ -87,10 +87,16 @@ func New(problem *Problem) *Solver {
|
|||||||
return &Solver{status: Unsat}
|
return &Solver{status: Unsat}
|
||||||
}
|
}
|
||||||
nbVars := problem.NbVars
|
nbVars := problem.NbVars
|
||||||
|
|
||||||
|
trailCap := nbVars
|
||||||
|
if len(problem.Units) > trailCap {
|
||||||
|
trailCap = len(problem.Units)
|
||||||
|
}
|
||||||
|
|
||||||
s := &Solver{
|
s := &Solver{
|
||||||
nbVars: nbVars,
|
nbVars: nbVars,
|
||||||
status: problem.Status,
|
status: problem.Status,
|
||||||
trail: make([]Lit, len(problem.Units), nbVars),
|
trail: make([]Lit, len(problem.Units), trailCap),
|
||||||
model: problem.Model,
|
model: problem.Model,
|
||||||
activity: make([]float64, nbVars),
|
activity: make([]float64, nbVars),
|
||||||
polarity: make([]bool, nbVars),
|
polarity: make([]bool, nbVars),
|
||||||
@ -343,7 +349,7 @@ func (s *Solver) propagateAndSearch(lit Lit, lvl decLevel) Status {
|
|||||||
return Indet
|
return Indet
|
||||||
}
|
}
|
||||||
if s.Stats.NbConflicts >= s.wl.idxReduce*s.wl.nbMax {
|
if s.Stats.NbConflicts >= s.wl.idxReduce*s.wl.nbMax {
|
||||||
s.wl.idxReduce = (s.Stats.NbConflicts / s.wl.nbMax) + 1
|
s.wl.idxReduce = s.Stats.NbConflicts/s.wl.nbMax + 1
|
||||||
s.reduceLearned()
|
s.reduceLearned()
|
||||||
s.bumpNbMax()
|
s.bumpNbMax()
|
||||||
}
|
}
|
||||||
@ -738,7 +744,7 @@ func (s *Solver) Optimal(results chan Result, stop chan struct{}) (res Result) {
|
|||||||
copy(s.lastModel, s.model) // Save this model: it might be the last one
|
copy(s.lastModel, s.model) // Save this model: it might be the last one
|
||||||
cost = 0
|
cost = 0
|
||||||
for i, lit := range s.minLits {
|
for i, lit := range s.minLits {
|
||||||
if (s.model[lit.Var()] > 0) == lit.IsPositive() {
|
if s.model[lit.Var()] > 0 == lit.IsPositive() {
|
||||||
if s.minWeights == nil {
|
if s.minWeights == nil {
|
||||||
cost++
|
cost++
|
||||||
} else {
|
} else {
|
||||||
@ -803,7 +809,7 @@ func (s *Solver) Minimize() int {
|
|||||||
copy(s.lastModel, s.model) // Save this model: it might be the last one
|
copy(s.lastModel, s.model) // Save this model: it might be the last one
|
||||||
cost = 0
|
cost = 0
|
||||||
for i, lit := range s.minLits {
|
for i, lit := range s.minLits {
|
||||||
if (s.model[lit.Var()] > 0) == lit.IsPositive() {
|
if s.model[lit.Var()] > 0 == lit.IsPositive() {
|
||||||
if s.minWeights == nil {
|
if s.minWeights == nil {
|
||||||
cost++
|
cost++
|
||||||
} else {
|
} else {
|
||||||
|
24
vendor/github.com/ecooper/qlearning/.gitignore
generated
vendored
Normal file
24
vendor/github.com/ecooper/qlearning/.gitignore
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
21
vendor/github.com/ecooper/qlearning/LICENSE
generated
vendored
Normal file
21
vendor/github.com/ecooper/qlearning/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016 Eric Cooper
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
72
vendor/github.com/ecooper/qlearning/README.md
generated
vendored
Normal file
72
vendor/github.com/ecooper/qlearning/README.md
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# qlearning
|
||||||
|
|
||||||
|
The qlearning package provides a series of interfaces and utilities to implement
|
||||||
|
the [Q-Learning](https://en.wikipedia.org/wiki/Q-learning) algorithm in
|
||||||
|
Go.
|
||||||
|
|
||||||
|
This project was largely inspired by [flappybird-qlearning-
|
||||||
|
bot](https://github.com/chncyhn/flappybird-qlearning-bot).
|
||||||
|
|
||||||
|
*Until a release is tagged, qlearning should be considered highly
|
||||||
|
experimental and mostly a fun toy.*
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ go get https://github.com/ecooper/qlearning
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
qlearning provides example implementations in the [examples](examples/)
|
||||||
|
directory of the project.
|
||||||
|
|
||||||
|
[hangman.go](examples/hangman.go) provides a naive implementation of
|
||||||
|
[Hangman](https://en.wikipedia.org/wiki/Hangman_(game)) for use with
|
||||||
|
qlearning.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ cd $GOPATH/src/github.com/ecooper/qlearning/examples
|
||||||
|
$ go run hangman.go -h
|
||||||
|
Usage of hangman:
|
||||||
|
-debug
|
||||||
|
Set debug
|
||||||
|
-games int
|
||||||
|
Play N games (default 5000000)
|
||||||
|
-progress int
|
||||||
|
Print progress messages every N games (default 1000)
|
||||||
|
-wordlist string
|
||||||
|
Path to a wordlist (default "./wordlist.txt")
|
||||||
|
-words int
|
||||||
|
Use N words from wordlist (default 10000)
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, running [hangman.go](examples/hangman.go) will play millions
|
||||||
|
of games against a 10,000-word corpus. That's a bit overkill for just
|
||||||
|
trying out qlearning. You can run it against a smaller number of words
|
||||||
|
for a few number of games using the `-games` and `-words` flags.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ go run hangman.go -words 100 -progress 1000 -games 5000
|
||||||
|
100 words loaded
|
||||||
|
1000 games played: 92 WINS 908 LOSSES 9% WIN RATE
|
||||||
|
2000 games played: 447 WINS 1553 LOSSES 36% WIN RATE
|
||||||
|
3000 games played: 1064 WINS 1936 LOSSES 62% WIN RATE
|
||||||
|
4000 games played: 1913 WINS 2087 LOSSES 85% WIN RATE
|
||||||
|
5000 games played: 2845 WINS 2155 LOSSES 93% WIN RATE
|
||||||
|
|
||||||
|
Agent performance: 5000 games played, 2845 WINS 2155 LOSSES 57% WIN RATE
|
||||||
|
```
|
||||||
|
|
||||||
|
"WIN RATE" per progress report is isolated within that cycle, a group of
|
||||||
|
1000 games in this example. The win rate is meant to show the velocity
|
||||||
|
of learning by the agent. If it is "learning", the win rate should be
|
||||||
|
increasing until reaching convergence.
|
||||||
|
|
||||||
|
As you can see, after 5000 games, the agent is able to "learn" and play
|
||||||
|
hangman against a 100-word vocabulary.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
See [godocs](https://godoc.org/github.com/ecooper/qlearning) for the
|
||||||
|
package documentation.
|
167
vendor/github.com/ecooper/qlearning/qlearning.go
generated
vendored
Normal file
167
vendor/github.com/ecooper/qlearning/qlearning.go
generated
vendored
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
// Package qlearning is an experimental set of interfaces and helpers to
|
||||||
|
// implement the Q-learning algorithm in Go.
|
||||||
|
//
|
||||||
|
// This is highly experimental and should be considered a toy.
|
||||||
|
//
|
||||||
|
// See https://github.com/ecooper/qlearning/tree/master/examples for
|
||||||
|
// implementation examples.
|
||||||
|
package qlearning
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State is an interface wrapping the current state of the model.
|
||||||
|
type State interface {
|
||||||
|
|
||||||
|
// String returns a string representation of the given state.
|
||||||
|
// Implementers should take care to insure that this is a consistent
|
||||||
|
// hash for a given state.
|
||||||
|
String() string
|
||||||
|
|
||||||
|
// Next provides a slice of possible Actions that could be applied to
|
||||||
|
// a state.
|
||||||
|
Next() []Action
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action is an interface wrapping an action that can be applied to the
|
||||||
|
// model's current state.
|
||||||
|
//
|
||||||
|
// BUG (ecooper): A state should apply an action, not the other way
|
||||||
|
// around.
|
||||||
|
type Action interface {
|
||||||
|
String() string
|
||||||
|
Apply(State) State
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewarder is an interface wrapping the ability to provide a reward
|
||||||
|
// for the execution of an action in a given state.
|
||||||
|
type Rewarder interface {
|
||||||
|
// Reward calculates the reward value for a given action in a given
|
||||||
|
// state.
|
||||||
|
Reward(action *StateAction) float32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agent is an interface for a model's agent and is able to learn
|
||||||
|
// from actions and return the current Q-value of an action at a given state.
|
||||||
|
type Agent interface {
|
||||||
|
// Learn updates the model for a given state and action, using the
|
||||||
|
// provided Rewarder implementation.
|
||||||
|
Learn(*StateAction, Rewarder)
|
||||||
|
|
||||||
|
// Value returns the current Q-value for a State and Action.
|
||||||
|
Value(State, Action) float32
|
||||||
|
|
||||||
|
// Return a string representation of the Agent.
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateAction is a struct grouping an action to a given State. Additionally,
|
||||||
|
// a Value can be associated to StateAction, which is typically the Q-value.
|
||||||
|
type StateAction struct {
|
||||||
|
State State
|
||||||
|
Action Action
|
||||||
|
Value float32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStateAction creates a new StateAction for a State and Action.
|
||||||
|
func NewStateAction(state State, action Action, val float32) *StateAction {
|
||||||
|
return &StateAction{
|
||||||
|
State: state,
|
||||||
|
Action: action,
|
||||||
|
Value: val,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next uses an Agent and State to find the highest scored Action.
|
||||||
|
//
|
||||||
|
// In the case of Q-value ties for a set of actions, a random
|
||||||
|
// value is selected.
|
||||||
|
func Next(agent Agent, state State) *StateAction {
|
||||||
|
best := make([]*StateAction, 0)
|
||||||
|
bestVal := float32(0.0)
|
||||||
|
|
||||||
|
for _, action := range state.Next() {
|
||||||
|
val := agent.Value(state, action)
|
||||||
|
|
||||||
|
if bestVal == float32(0.0) {
|
||||||
|
best = append(best, NewStateAction(state, action, val))
|
||||||
|
bestVal = val
|
||||||
|
} else {
|
||||||
|
if val > bestVal {
|
||||||
|
best = []*StateAction{NewStateAction(state, action, val)}
|
||||||
|
bestVal = val
|
||||||
|
} else if val == bestVal {
|
||||||
|
best = append(best, NewStateAction(state, action, val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return best[rand.Intn(len(best))]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimpleAgent is an Agent implementation that stores Q-values in a
|
||||||
|
// map of maps.
|
||||||
|
type SimpleAgent struct {
|
||||||
|
q map[string]map[string]float32
|
||||||
|
lr float32
|
||||||
|
d float32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSimpleAgent creates a SimpleAgent with the provided learning rate
|
||||||
|
// and discount factor.
|
||||||
|
func NewSimpleAgent(lr, d float32) *SimpleAgent {
|
||||||
|
return &SimpleAgent{
|
||||||
|
q: make(map[string]map[string]float32),
|
||||||
|
d: d,
|
||||||
|
lr: lr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getActions returns the current Q-values for a given state.
|
||||||
|
func (agent *SimpleAgent) getActions(state string) map[string]float32 {
|
||||||
|
if _, ok := agent.q[state]; !ok {
|
||||||
|
agent.q[state] = make(map[string]float32)
|
||||||
|
}
|
||||||
|
|
||||||
|
return agent.q[state]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Learn updates the existing Q-value for the given State and Action
|
||||||
|
// using the Rewarder.
|
||||||
|
//
|
||||||
|
// See https://en.wikipedia.org/wiki/Q-learning#Algorithm
|
||||||
|
func (agent *SimpleAgent) Learn(action *StateAction, reward Rewarder) {
|
||||||
|
current := action.State.String()
|
||||||
|
next := action.Action.Apply(action.State).String()
|
||||||
|
|
||||||
|
actions := agent.getActions(current)
|
||||||
|
|
||||||
|
maxNextVal := float32(0.0)
|
||||||
|
for _, v := range agent.getActions(next) {
|
||||||
|
if v > maxNextVal {
|
||||||
|
maxNextVal = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentVal := actions[action.Action.String()]
|
||||||
|
actions[action.Action.String()] = currentVal + agent.lr*(reward.Reward(action)+agent.d*maxNextVal-currentVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value gets the current Q-value for a State and Action.
|
||||||
|
func (agent *SimpleAgent) Value(state State, action Action) float32 {
|
||||||
|
return agent.getActions(state.String())[action.String()]
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the current Q-value map as a printed string.
|
||||||
|
//
|
||||||
|
// BUG (ecooper): This is useless.
|
||||||
|
func (agent *SimpleAgent) String() string {
|
||||||
|
return fmt.Sprintf("%v", agent.q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rand.Seed(time.Now().UTC().UnixNano())
|
||||||
|
}
|
6
vendor/modules.txt
vendored
6
vendor/modules.txt
vendored
@ -49,7 +49,7 @@ github.com/containerd/continuity/pathdriver
|
|||||||
github.com/containerd/continuity/syscallx
|
github.com/containerd/continuity/syscallx
|
||||||
# github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d
|
# github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d
|
||||||
github.com/cpuguy83/go-md2man/v2/md2man
|
github.com/cpuguy83/go-md2man/v2/md2man
|
||||||
# github.com/crillab/gophersat v1.1.7
|
# github.com/crillab/gophersat v1.1.9-0.20200211102949-9a8bf7f2f0a3
|
||||||
github.com/crillab/gophersat/bf
|
github.com/crillab/gophersat/bf
|
||||||
github.com/crillab/gophersat/solver
|
github.com/crillab/gophersat/solver
|
||||||
# github.com/cyphar/filepath-securejoin v0.2.2
|
# github.com/cyphar/filepath-securejoin v0.2.2
|
||||||
@ -93,6 +93,8 @@ github.com/docker/go-units
|
|||||||
github.com/docker/libnetwork/ipamutils
|
github.com/docker/libnetwork/ipamutils
|
||||||
# github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7
|
# github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7
|
||||||
github.com/docker/libtrust
|
github.com/docker/libtrust
|
||||||
|
# github.com/ecooper/qlearning v0.0.0-20160612200101-3075011a69fd
|
||||||
|
github.com/ecooper/qlearning
|
||||||
# github.com/fatih/color v1.7.0
|
# github.com/fatih/color v1.7.0
|
||||||
github.com/fatih/color
|
github.com/fatih/color
|
||||||
# github.com/fsnotify/fsnotify v1.4.7
|
# github.com/fsnotify/fsnotify v1.4.7
|
||||||
@ -263,8 +265,8 @@ go.uber.org/multierr
|
|||||||
go.uber.org/tools/update-license
|
go.uber.org/tools/update-license
|
||||||
# go.uber.org/zap v1.13.0
|
# go.uber.org/zap v1.13.0
|
||||||
go.uber.org/zap
|
go.uber.org/zap
|
||||||
go.uber.org/zap/internal/bufferpool
|
|
||||||
go.uber.org/zap/zapcore
|
go.uber.org/zap/zapcore
|
||||||
|
go.uber.org/zap/internal/bufferpool
|
||||||
go.uber.org/zap/buffer
|
go.uber.org/zap/buffer
|
||||||
go.uber.org/zap/internal/color
|
go.uber.org/zap/internal/color
|
||||||
go.uber.org/zap/internal/exit
|
go.uber.org/zap/internal/exit
|
||||||
|
Loading…
Reference in New Issue
Block a user