mirror of
https://github.com/mudler/luet.git
synced 2025-08-01 15:28:20 +00:00
Compute upgrades
Make solver compute simply upgrades and the installer execute them. Add simple upgrade test
This commit is contained in:
parent
ff3b322ea2
commit
30a7312911
@ -92,6 +92,50 @@ func NewLuetInstaller(concurrency int) Installer {
|
|||||||
return &LuetInstaller{Concurrency: concurrency}
|
return &LuetInstaller{Concurrency: concurrency}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *LuetInstaller) Upgrade(s *System) error {
|
||||||
|
Spinner(32)
|
||||||
|
defer SpinnerStop()
|
||||||
|
syncedRepos := Repositories{}
|
||||||
|
for _, r := range l.PackageRepositories {
|
||||||
|
repo, err := r.Sync()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Failed syncing repository: "+r.GetName())
|
||||||
|
}
|
||||||
|
syncedRepos = append(syncedRepos, repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute what to install and from where
|
||||||
|
sort.Sort(syncedRepos)
|
||||||
|
|
||||||
|
// First match packages against repositories by priority
|
||||||
|
// matches := syncedRepos.PackageMatches(p)
|
||||||
|
|
||||||
|
// compute a "big" world
|
||||||
|
allRepos := pkg.NewInMemoryDatabase(false)
|
||||||
|
syncedRepos.SyncDatabase(allRepos)
|
||||||
|
solv := solver.NewSolver(s.Database, allRepos, pkg.NewInMemoryDatabase(false))
|
||||||
|
uninstall, solution, err := solv.Upgrade()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Failed solving solution for upgrade")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, u := range uninstall {
|
||||||
|
err := l.Uninstall(u, s)
|
||||||
|
if err != nil {
|
||||||
|
Warning("Failed uninstall for ", u.GetFingerPrint())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toInstall := []pkg.Package{}
|
||||||
|
for _, assertion := range solution {
|
||||||
|
if assertion.Value {
|
||||||
|
toInstall = append(toInstall, assertion.Package)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.Install(toInstall, s)
|
||||||
|
}
|
||||||
|
|
||||||
func (l *LuetInstaller) Install(p []pkg.Package, s *System) error {
|
func (l *LuetInstaller) Install(p []pkg.Package, s *System) error {
|
||||||
// First get metas from all repos (and decodes trees)
|
// First get metas from all repos (and decodes trees)
|
||||||
|
|
||||||
@ -136,8 +180,10 @@ func (l *LuetInstaller) Install(p []pkg.Package, s *System) error {
|
|||||||
|
|
||||||
}
|
}
|
||||||
if matches[0].Package.Matches(artefact.GetCompileSpec().GetPackage()) {
|
if matches[0].Package.Matches(artefact.GetCompileSpec().GetPackage()) {
|
||||||
// TODO: Filter out already installed?
|
// Filter out already installed
|
||||||
toInstall[assertion.Package.GetFingerPrint()] = ArtifactMatch{Package: assertion.Package, Artifact: artefact, Repository: matches[0].Repo}
|
if _, err := s.Database.FindPackage(assertion.Package); err != nil {
|
||||||
|
toInstall[assertion.Package.GetFingerPrint()] = ArtifactMatch{Package: assertion.Package, Artifact: artefact, Repository: matches[0].Repo}
|
||||||
|
}
|
||||||
break A
|
break A
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,4 +257,116 @@ uri: "`+tmpdir+`"
|
|||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Context("Simple upgrades", func() {
|
||||||
|
It("Installs packages and Upgrades a system with a persistent db", func() {
|
||||||
|
//repo:=NewLuetRepository()
|
||||||
|
|
||||||
|
tmpdir, err := ioutil.TempDir("", "tree")
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
defer os.RemoveAll(tmpdir) // clean up
|
||||||
|
|
||||||
|
generalRecipe := tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false))
|
||||||
|
|
||||||
|
err = generalRecipe.Load("../../tests/fixtures/upgrade")
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(len(generalRecipe.GetDatabase().GetPackages())).To(Equal(4))
|
||||||
|
|
||||||
|
c := compiler.NewLuetCompiler(backend.NewSimpleDockerBackend(), generalRecipe.GetDatabase())
|
||||||
|
|
||||||
|
spec, err := c.FromPackage(&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
spec2, err := c.FromPackage(&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.1"})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
spec3, err := c.FromPackage(&pkg.DefaultPackage{Name: "c", Category: "test", Version: "1.0"})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(spec.GetPackage().GetPath()).ToNot(Equal(""))
|
||||||
|
|
||||||
|
tmpdir, err = ioutil.TempDir("", "tree")
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
defer os.RemoveAll(tmpdir) // clean up
|
||||||
|
|
||||||
|
spec.SetOutputPath(tmpdir)
|
||||||
|
spec2.SetOutputPath(tmpdir)
|
||||||
|
spec3.SetOutputPath(tmpdir)
|
||||||
|
_, errs := c.CompileParallel(2, false, compiler.NewLuetCompilationspecs(spec, spec2, spec3))
|
||||||
|
|
||||||
|
Expect(errs).To(BeEmpty())
|
||||||
|
|
||||||
|
repo, err := GenerateRepository("test", tmpdir, "local", 1, tmpdir, "../../tests/fixtures/upgrade", pkg.NewInMemoryDatabase(false))
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(repo.GetName()).To(Equal("test"))
|
||||||
|
Expect(helpers.Exists(spec.Rel("repository.yaml"))).ToNot(BeTrue())
|
||||||
|
Expect(helpers.Exists(spec.Rel("tree.tar"))).ToNot(BeTrue())
|
||||||
|
err = repo.Write(tmpdir)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(helpers.Exists(spec.Rel("repository.yaml"))).To(BeTrue())
|
||||||
|
Expect(helpers.Exists(spec.Rel("tree.tar"))).To(BeTrue())
|
||||||
|
Expect(repo.GetUri()).To(Equal(tmpdir))
|
||||||
|
Expect(repo.GetType()).To(Equal("local"))
|
||||||
|
|
||||||
|
fakeroot, err := ioutil.TempDir("", "fakeroot")
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
defer os.RemoveAll(fakeroot) // clean up
|
||||||
|
|
||||||
|
inst := NewLuetInstaller(1)
|
||||||
|
repo2, err := NewLuetRepositoryFromYaml([]byte(`
|
||||||
|
name: "test"
|
||||||
|
type: "local"
|
||||||
|
uri: "`+tmpdir+`"
|
||||||
|
`), pkg.NewInMemoryDatabase(false))
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
inst.Repositories(Repositories{repo2})
|
||||||
|
Expect(repo.GetUri()).To(Equal(tmpdir))
|
||||||
|
Expect(repo.GetType()).To(Equal("local"))
|
||||||
|
|
||||||
|
bolt, err := ioutil.TempDir("", "db")
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
defer os.RemoveAll(bolt) // clean up
|
||||||
|
|
||||||
|
systemDB := pkg.NewBoltDatabase(filepath.Join(bolt, "db.db"))
|
||||||
|
system := &System{Database: systemDB, Target: fakeroot}
|
||||||
|
err = inst.Install([]pkg.Package{&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"}}, system)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(helpers.Exists(filepath.Join(fakeroot, "test5"))).To(BeTrue())
|
||||||
|
Expect(helpers.Exists(filepath.Join(fakeroot, "test6"))).To(BeTrue())
|
||||||
|
_, err = systemDB.FindPackage(&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(len(system.Database.GetPackages())).To(Equal(1))
|
||||||
|
p, err := system.Database.GetPackage(system.Database.GetPackages()[0])
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(p.GetName()).To(Equal("b"))
|
||||||
|
|
||||||
|
files, err := systemDB.GetPackageFiles(&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"})
|
||||||
|
Expect(files).To(Equal([]string{"artifact42", "test5", "test6"}))
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
err = inst.Upgrade(system)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
// Nothing should be there anymore (files, packagedb entry)
|
||||||
|
Expect(helpers.Exists(filepath.Join(fakeroot, "test5"))).ToNot(BeTrue())
|
||||||
|
Expect(helpers.Exists(filepath.Join(fakeroot, "test6"))).ToNot(BeTrue())
|
||||||
|
|
||||||
|
// New version - new files
|
||||||
|
Expect(helpers.Exists(filepath.Join(fakeroot, "newc"))).To(BeTrue())
|
||||||
|
_, err = system.Database.GetPackageFiles(&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"})
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
_, err = system.Database.FindPackage(&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"})
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
|
||||||
|
// New package should be there
|
||||||
|
_, err = system.Database.FindPackage(&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.1"})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
type Installer interface {
|
type Installer interface {
|
||||||
Install([]pkg.Package, *System) error
|
Install([]pkg.Package, *System) error
|
||||||
Uninstall(pkg.Package, *System) error
|
Uninstall(pkg.Package, *System) error
|
||||||
|
Upgrade(s *System) error
|
||||||
Repositories([]Repository)
|
Repositories([]Repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ type PackageSolver interface {
|
|||||||
ConflictsWithInstalled(p pkg.Package) (bool, error)
|
ConflictsWithInstalled(p pkg.Package) (bool, error)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Solver is the default solver for luet
|
// Solver is the default solver for luet
|
||||||
@ -193,6 +194,55 @@ func (s *Solver) ConflictsWithInstalled(p pkg.Package) (bool, error) {
|
|||||||
return s.ConflictsWith(p, s.Installed())
|
return s.ConflictsWith(p, s.Installed())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Solver) Upgrade() ([]pkg.Package, PackagesAssertions, error) {
|
||||||
|
|
||||||
|
// First get candidates that needs to be upgraded..
|
||||||
|
|
||||||
|
toUninstall := []pkg.Package{}
|
||||||
|
toInstall := []pkg.Package{}
|
||||||
|
|
||||||
|
availableCache := map[string][]pkg.Package{}
|
||||||
|
for _, p := range s.DefinitionDatabase.World() {
|
||||||
|
// Each one, should be expanded
|
||||||
|
availableCache[p.GetName()+p.GetCategory()] = append(availableCache[p.GetName()+p.GetCategory()], p)
|
||||||
|
}
|
||||||
|
|
||||||
|
installedcopy := pkg.NewInMemoryDatabase(false)
|
||||||
|
|
||||||
|
for _, p := range s.InstalledDatabase.World() {
|
||||||
|
installedcopy.CreatePackage(p)
|
||||||
|
packages, ok := availableCache[p.GetName()+p.GetCategory()]
|
||||||
|
if ok && len(packages) != 0 {
|
||||||
|
best := pkg.Best(packages)
|
||||||
|
if best.GetVersion() != p.GetVersion() {
|
||||||
|
toUninstall = append(toUninstall, p)
|
||||||
|
toInstall = append(toInstall, best)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s2 := NewSolver(installedcopy, s.DefinitionDatabase, pkg.NewInMemoryDatabase(false))
|
||||||
|
// Then try to uninstall the versions in the system, and store that tree
|
||||||
|
for _, p := range toUninstall {
|
||||||
|
r, err := s.Uninstall(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't uninstall selected candidate "+p.GetFingerPrint())
|
||||||
|
}
|
||||||
|
for _, z := range r {
|
||||||
|
err = installedcopy.RemovePackage(z)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't remove copy of package targetted for removal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
r, e := s2.Install(toInstall)
|
||||||
|
return toUninstall, r, e
|
||||||
|
// To that tree, ask to install the versions that should be upgraded, and try to solve
|
||||||
|
// Return the solution
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Uninstall takes a candidate package and return a list of packages that would be removed
|
// Uninstall takes a candidate package and return a list of packages that would be removed
|
||||||
// in order to purge the candidate. Returns error if unsat.
|
// in order to purge the candidate. Returns error if unsat.
|
||||||
func (s *Solver) Uninstall(c pkg.Package) ([]pkg.Package, error) {
|
func (s *Solver) Uninstall(c pkg.Package) ([]pkg.Package, error) {
|
||||||
|
@ -678,5 +678,39 @@ var _ = Describe("Solver", func() {
|
|||||||
Expect(p).To(Equal(a03))
|
Expect(p).To(Equal(a03))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Context("Upgrades", func() {
|
||||||
|
|
||||||
|
C := pkg.NewPackage("c", "1.5", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "a", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
|
||||||
|
C.SetCategory("test")
|
||||||
|
B := pkg.NewPackage("b", "1.0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
B.SetCategory("test")
|
||||||
|
A := pkg.NewPackage("a", "1.1", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "b", Version: "1.0", Category: "test"}}, []*pkg.DefaultPackage{})
|
||||||
|
A.SetCategory("test")
|
||||||
|
A1 := pkg.NewPackage("a", "1.2", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "b", Version: "1.0", Category: "test"}}, []*pkg.DefaultPackage{})
|
||||||
|
A1.SetCategory("test")
|
||||||
|
|
||||||
|
It("upgrades correctly", func() {
|
||||||
|
for _, p := range []pkg.Package{A1, B, C} {
|
||||||
|
_, err := dbDefinitions.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{A, B} {
|
||||||
|
_, err := dbInstalled.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
uninstall, solution, err := s.Upgrade()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(len(uninstall)).To(Equal(1))
|
||||||
|
Expect(uninstall[0].GetName()).To(Equal("a"))
|
||||||
|
Expect(uninstall[0].GetVersion()).To(Equal("1.1"))
|
||||||
|
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: A1, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: false}))
|
||||||
|
Expect(len(solution)).To(Equal(3))
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
10
tests/fixtures/upgrade/c/build.yaml
vendored
Normal file
10
tests/fixtures/upgrade/c/build.yaml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
prelude:
|
||||||
|
- echo foo > /test
|
||||||
|
- echo bar > /test2
|
||||||
|
steps:
|
||||||
|
- echo c > /c
|
||||||
|
- echo c > /cd
|
||||||
|
requires:
|
||||||
|
- category: "test"
|
||||||
|
name: "a"
|
||||||
|
version: ">=1.0"
|
3
tests/fixtures/upgrade/c/definition.yaml
vendored
Normal file
3
tests/fixtures/upgrade/c/definition.yaml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
category: "test"
|
||||||
|
name: "c"
|
||||||
|
version: "1.0"
|
11
tests/fixtures/upgrade/cat/a/a/build.yaml
vendored
Normal file
11
tests/fixtures/upgrade/cat/a/a/build.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
image: "alpine"
|
||||||
|
prelude:
|
||||||
|
- echo foo > /test
|
||||||
|
- echo bar > /test2
|
||||||
|
steps:
|
||||||
|
- echo artifact3 > /test3
|
||||||
|
- echo artifact4 > /test4
|
||||||
|
requires:
|
||||||
|
- category: "test"
|
||||||
|
name: "b"
|
||||||
|
version: "1.0"
|
8
tests/fixtures/upgrade/cat/a/a/definition.yaml
vendored
Normal file
8
tests/fixtures/upgrade/cat/a/a/definition.yaml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
category: "test"
|
||||||
|
name: "a"
|
||||||
|
version: "1.1"
|
||||||
|
requires:
|
||||||
|
- category: "test2"
|
||||||
|
name: "b"
|
||||||
|
version: "1.0"
|
||||||
|
|
9
tests/fixtures/upgrade/cat/b-1.1/build.yaml
vendored
Normal file
9
tests/fixtures/upgrade/cat/b-1.1/build.yaml
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
image: "alpine"
|
||||||
|
prelude:
|
||||||
|
- echo foo > /test
|
||||||
|
- echo bar > /test2
|
||||||
|
- chmod +x generate.sh
|
||||||
|
steps:
|
||||||
|
- echo artifact5 > /newc
|
||||||
|
- echo artifact6 > /newnewc
|
||||||
|
- ./generate.sh
|
3
tests/fixtures/upgrade/cat/b-1.1/definition.yaml
vendored
Normal file
3
tests/fixtures/upgrade/cat/b-1.1/definition.yaml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
category: "test"
|
||||||
|
name: "b"
|
||||||
|
version: "1.1"
|
1
tests/fixtures/upgrade/cat/b-1.1/generate.sh
vendored
Normal file
1
tests/fixtures/upgrade/cat/b-1.1/generate.sh
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
echo generated > /sonewc
|
9
tests/fixtures/upgrade/cat/b/build.yaml
vendored
Normal file
9
tests/fixtures/upgrade/cat/b/build.yaml
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
image: "alpine"
|
||||||
|
prelude:
|
||||||
|
- echo foo > /test
|
||||||
|
- echo bar > /test2
|
||||||
|
- chmod +x generate.sh
|
||||||
|
steps:
|
||||||
|
- echo artifact5 > /test5
|
||||||
|
- echo artifact6 > /test6
|
||||||
|
- ./generate.sh
|
3
tests/fixtures/upgrade/cat/b/definition.yaml
vendored
Normal file
3
tests/fixtures/upgrade/cat/b/definition.yaml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
category: "test"
|
||||||
|
name: "b"
|
||||||
|
version: "1.0"
|
1
tests/fixtures/upgrade/cat/b/generate.sh
vendored
Normal file
1
tests/fixtures/upgrade/cat/b/generate.sh
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
echo generated > /artifact42
|
Loading…
Reference in New Issue
Block a user