Compare commits

...

14 Commits

Author SHA1 Message Date
Ettore Di Giacinto
d472dee19b Tag 0.9.14 2020-12-09 19:33:11 +01:00
Ettore Di Giacinto
b5990b5333 Generate changes from CompilerBackendOptions and pass by image name so img can unpack images 2020-12-09 00:27:37 +01:00
Ettore Di Giacinto
767488327b Update README 2020-12-08 17:48:21 +01:00
Ettore Di Giacinto
be3933998b Update links in README 2020-12-08 17:47:33 +01:00
Ettore Di Giacinto
fa9dc9da53 Tag 0.9.13 2020-12-08 15:49:40 +01:00
Ettore Di Giacinto
9911888d18 Stabilize test 2020-12-08 14:56:51 +01:00
Ettore Di Giacinto
2906180c43 Upgrade universe test expectations are changed 2020-12-08 14:15:52 +01:00
Ettore Di Giacinto
cf5e4e1305 Detect removed also when availables aren't found 2020-12-08 12:28:20 +01:00
Ettore Di Giacinto
519586f6bc Search for removed in Def DB 2020-12-08 12:07:28 +01:00
Ettore Di Giacinto
6dbc422b8f Apply solver change to UpgradeUniverse also to the parallel variant and adapt tests
Similarly, we want just to consider what is being uninstalled and the
new rules of the package that is going to be upgraded
2020-12-08 11:43:38 +01:00
Ettore Di Giacinto
a3cfebf438 Create BuildFormula from installed with InstallDatabase
Instead of using the DefinitionDB which supposedly contains only the
relations present in the online repositories. In this way the solver its
more consistent and tries to solve with only the internal definitions.

This also fixes quirks with luet upgrade --universe
2020-12-08 10:58:08 +01:00
Ettore Di Giacinto
24201b25ef Apply solver change also to the parallel variant 2020-12-08 10:41:03 +01:00
Ettore Di Giacinto
7c53296530 Adapt tests 2020-12-08 10:39:15 +01:00
Ettore Di Giacinto
a3cb0ed17f When attempting to uninstall, do it from the internal db so it can resolve the current versions 2020-12-08 02:04:54 +01:00
15 changed files with 281 additions and 70 deletions

View File

@@ -8,7 +8,7 @@
Luet is a multi-platform Package Manager based off from containers - it uses Docker (and others) to build packages. It has zero dependencies and it is well suitable for "from scratch" environments. It can also version entire rootfs and enables delivery of OTA-alike updates, making it a perfect fit for the Edge computing era and IoT embedded devices.
It offers a simple [specfile format](https://luet-lab.github.io/docs/docs/concepts/specfile/) in YAML notation to define both packages and rootfs. As it is based on containers, it can be also used to build stages for Linux From Scratch installations and it can build and track updates for those systems.
It offers a simple [specfile format](https://luet-lab.github.io/docs/docs/concepts/packages/specfile/) in YAML notation to define both [packages](https://luet-lab.github.io/docs/docs/concepts/packages/) and [rootfs](https://luet-lab.github.io/docs/docs/concepts/packages/#package-layers). As it is based on containers, it can be also used to build stages for Linux From Scratch installations and it can build and track updates for those systems.
It is written entirely in Golang and where used as package manager, it can run in from scratch environment, with zero dependencies.
@@ -20,8 +20,8 @@ It is written entirely in Golang and where used as package manager, it can run i
- You can install it aside also with your current distro package manager, and start building and distributing your packages
- Support for packages as "layers"
- [It uses SAT solving techniques to solve the deptree](https://luet-lab.github.io/docs/docs/concepts/constraints/) ( Inspired by [OPIUM](https://ranjitjhala.github.io/static/opium.pdf) )
- Support for collections and templated package definitions
- [Can be extended with Plugins and Extensions](https://luet-lab.github.io/docs/docs/plugins-and-extensions/)
- Support for [collections](https://luet-lab.github.io/docs/docs/concepts/packages/collections/) and [templated package definitions](https://luet-lab.github.io/docs/docs/concepts/packages/templates/)
- [Can be extended with Plugins and Extensions](https://luet-lab.github.io/docs/docs/concepts/plugins-and-extensions/)
## Install

View File

@@ -40,7 +40,7 @@ var Verbose bool
var LockedCommands = []string{"install", "uninstall", "upgrade"}
const (
LuetCLIVersion = "0.9.12"
LuetCLIVersion = "0.9.14"
LuetEnvPrefix = "LUET"
)

View File

@@ -102,15 +102,15 @@ ENV PACKAGE_VERSION=1.4.0
ENV PACKAGE_CATEGORY=app-admin
RUN echo foo > /test
RUN echo bar > /test2`))
opts = CompilerBackendOptions{
opts2 := CompilerBackendOptions{
ImageName: "test",
SourcePath: tmpdir,
DockerFileName: "LuetDockerfile",
Destination: filepath.Join(tmpdir, "output2.tar"),
}
Expect(b.ImageDefinitionToTar(opts)).ToNot(HaveOccurred())
Expect(b.ImageDefinitionToTar(opts2)).ToNot(HaveOccurred())
Expect(helpers.Exists(filepath.Join(tmpdir, "output2.tar"))).To(BeTrue())
diffs, err := b.Changes(filepath.Join(tmpdir2, "output1.tar"), filepath.Join(tmpdir, "output2.tar"))
diffs, err := b.Changes(opts, opts2)
Expect(err).ToNot(HaveOccurred())
artifacts := []ArtifactNode{}

View File

@@ -52,7 +52,10 @@ import (
// }
// }
// ]
func GenerateChanges(b compiler.CompilerBackend, srcImage, dstImage string) ([]compiler.ArtifactLayer, error) {
func GenerateChanges(b compiler.CompilerBackend, fromImage, toImage compiler.CompilerBackendOptions) ([]compiler.ArtifactLayer, error) {
srcImage := fromImage.Destination
dstImage := toImage.Destination
res := compiler.ArtifactLayer{FromImage: srcImage, ToImage: dstImage}
@@ -95,6 +98,7 @@ func GenerateChanges(b compiler.CompilerBackend, srcImage, dstImage string) ([]c
srcImageExtract := compiler.CompilerBackendOptions{
SourcePath: srcImage,
ImageName: fromImage.ImageName,
Destination: srcRootFS,
}
err = b.ExtractRootfs(srcImageExtract, false) // No need to keep permissions as we just collect file diffs
@@ -123,6 +127,7 @@ func GenerateChanges(b compiler.CompilerBackend, srcImage, dstImage string) ([]c
dstImageExtract := compiler.CompilerBackendOptions{
SourcePath: dstImage,
ImageName: toImage.ImageName,
Destination: dstRootFS,
}
err = b.ExtractRootfs(dstImageExtract, false)

View File

@@ -32,12 +32,14 @@ var _ = Describe("Docker image diffs", func() {
Context("Generate diffs from docker images", func() {
It("Detect no changes", func() {
err := b.DownloadImage(compiler.CompilerBackendOptions{
ImageName: "alpine:latest",
})
opts := compiler.CompilerBackendOptions{
ImageName: "alpine:latest",
Destination: "alpine:latest",
}
err := b.DownloadImage(opts)
Expect(err).ToNot(HaveOccurred())
layers, err := GenerateChanges(b, "alpine:latest", "alpine:latest")
layers, err := GenerateChanges(b, opts, opts)
Expect(err).ToNot(HaveOccurred())
Expect(len(layers)).To(Equal(1))
Expect(len(layers[0].Diffs.Additions)).To(Equal(0))
@@ -55,7 +57,13 @@ var _ = Describe("Docker image diffs", func() {
})
Expect(err).ToNot(HaveOccurred())
layers, err := GenerateChanges(b, "quay.io/mocaccino/micro", "quay.io/mocaccino/extra")
layers, err := GenerateChanges(b, compiler.CompilerBackendOptions{
ImageName: "quay.io/mocaccino/micro",
Destination: "quay.io/mocaccino/micro",
}, compiler.CompilerBackendOptions{
ImageName: "quay.io/mocaccino/extra",
Destination: "quay.io/mocaccino/extra",
})
Expect(err).ToNot(HaveOccurred())
Expect(len(layers)).To(Equal(1))

View File

@@ -239,7 +239,7 @@ func (*SimpleDocker) ExtractRootfs(opts compiler.CompilerBackendOptions, keepPer
}
// Changes retrieves changes between image layers
func (d *SimpleDocker) Changes(fromImage, toImage string) ([]compiler.ArtifactLayer, error) {
func (d *SimpleDocker) Changes(fromImage, toImage compiler.CompilerBackendOptions) ([]compiler.ArtifactLayer, error) {
diffs, err := GenerateChanges(d, fromImage, toImage)
if config.LuetCfg.GetGeneral().Debug {

View File

@@ -93,13 +93,13 @@ ENV PACKAGE_VERSION=1.4.0
ENV PACKAGE_CATEGORY=app-admin
RUN echo foo > /test
RUN echo bar > /test2`))
opts = CompilerBackendOptions{
opts2 := CompilerBackendOptions{
ImageName: "test",
SourcePath: tmpdir,
DockerFileName: "LuetDockerfile",
Destination: filepath.Join(tmpdir, "output2.tar"),
}
Expect(b.ImageDefinitionToTar(opts)).ToNot(HaveOccurred())
Expect(b.ImageDefinitionToTar(opts2)).ToNot(HaveOccurred())
Expect(helpers.Exists(filepath.Join(tmpdir, "output2.tar"))).To(BeTrue())
artifacts := []ArtifactNode{}
@@ -109,7 +109,7 @@ RUN echo bar > /test2`))
artifacts = append(artifacts, ArtifactNode{Name: "/test", Size: 4})
artifacts = append(artifacts, ArtifactNode{Name: "/test2", Size: 4})
Expect(b.Changes(filepath.Join(tmpdir2, "output1.tar"), filepath.Join(tmpdir, "output2.tar"))).To(Equal(
Expect(b.Changes(opts, opts2)).To(Equal(
[]ArtifactLayer{{
FromImage: filepath.Join(tmpdir2, "output1.tar"),
ToImage: filepath.Join(tmpdir, "output2.tar"),

View File

@@ -149,7 +149,7 @@ func (*SimpleImg) ExtractRootfs(opts compiler.CompilerBackendOptions, keepPerms
// TODO: Use container-diff (https://github.com/GoogleContainerTools/container-diff) for checking out layer diffs
// Changes uses container-diff (https://github.com/GoogleContainerTools/container-diff) for retrieving out layer diffs
func (i *SimpleImg) Changes(fromImage, toImage string) ([]compiler.ArtifactLayer, error) {
func (i *SimpleImg) Changes(fromImage, toImage compiler.CompilerBackendOptions) ([]compiler.ArtifactLayer, error) {
return GenerateChanges(i, fromImage, toImage)
}

View File

@@ -272,7 +272,7 @@ func (cs *LuetCompiler) unpackDelta(rootfs string, concurrency int, keepPermissi
defer os.Remove(builderOpts.Destination)
}
Info(pkgTag, ":hammer: Generating delta")
diffs, err := cs.Backend.Changes(builderOpts.Destination, runnerOpts.Destination)
diffs, err := cs.Backend.Changes(builderOpts, runnerOpts)
if err != nil {
return nil, errors.Wrap(err, "Could not generate changes from layers")
}

View File

@@ -79,7 +79,7 @@ type CompilerBackend interface {
BuildImage(CompilerBackendOptions) error
ExportImage(CompilerBackendOptions) error
RemoveImage(CompilerBackendOptions) error
Changes(fromImage, toImage string) ([]ArtifactLayer, error)
Changes(fromImage, toImage CompilerBackendOptions) ([]ArtifactLayer, error)
ImageDefinitionToTar(CompilerBackendOptions) error
ExtractRootfs(opts CompilerBackendOptions, keepPerms bool) error

View File

@@ -76,7 +76,7 @@ func (s *Parallel) noRulesInstalled() bool {
return true
}
func (s *Parallel) buildParallelFormula(formulas []bf.Formula, packages pkg.Packages) (bf.Formula, error) {
func (s *Parallel) buildParallelFormula(db pkg.PackageDatabase, formulas []bf.Formula, packages pkg.Packages) (bf.Formula, error) {
var wg = new(sync.WaitGroup)
var wg2 = new(sync.WaitGroup)
@@ -87,7 +87,7 @@ func (s *Parallel) buildParallelFormula(formulas []bf.Formula, packages pkg.Pack
go func(wg *sync.WaitGroup, c <-chan pkg.Package) {
defer wg.Done()
for p := range c {
solvable, err := p.BuildFormula(s.DefinitionDatabase, s.ParallelDatabase)
solvable, err := p.BuildFormula(db, s.ParallelDatabase)
if err != nil {
panic(err)
}
@@ -126,13 +126,13 @@ func (s *Parallel) BuildInstalled() (bf.Formula, error) {
var packages pkg.Packages
for _, p := range s.Installed() {
packages = append(packages, p)
for _, dep := range p.Related(s.DefinitionDatabase) {
for _, dep := range p.Related(s.InstalledDatabase) {
packages = append(packages, dep)
}
}
return s.buildParallelFormula(formulas, packages)
return s.buildParallelFormula(s.InstalledDatabase, formulas, packages)
}
// BuildWorld builds the formula which olds the requirements from the package definitions
@@ -148,7 +148,7 @@ func (s *Parallel) BuildWorld(includeInstalled bool) (bf.Formula, error) {
//f = bf.And(f, solvable)
formulas = append(formulas, solvable)
}
return s.buildParallelFormula(formulas, s.World())
return s.buildParallelFormula(s.DefinitionDatabase, formulas, s.World())
}
// BuildWorld builds the formula which olds the requirements from the package definitions
@@ -200,7 +200,7 @@ func (s *Parallel) BuildPartialWorld(includeInstalled bool) (bf.Formula, error)
close(results)
wg2.Wait()
return s.buildParallelFormula(formulas, packages)
return s.buildParallelFormula(s.DefinitionDatabase, formulas, packages)
//return s.buildParallelFormula(formulas, s.World())
}
@@ -471,23 +471,22 @@ func (s *Parallel) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAsse
go func(wg *sync.WaitGroup, c <-chan pkg.Package) {
defer wg.Done()
for p := range c {
available, err := universe.FindPackageVersions(p)
if err != nil {
removed = append(removed, p) /// FIXME: Racy
}
if len(available) == 0 {
available, err := s.DefinitionDatabase.FindPackageVersions(p)
if len(available) == 0 || err != nil {
removed = append(removed, p)
continue
}
bestmatch := available.Best(nil)
// Found a better version available
if !bestmatch.Matches(p) {
encodedP, _ := p.Encode(universe)
P := bf.Var(encodedP)
results <- bf.And(bf.Not(P), r)
encodedP, _ = bestmatch.Encode(universe)
P = bf.Var(encodedP)
results <- bf.And(P, r)
oldP, _ := p.Encode(universe)
toreplaceP := bf.Var(oldP)
best, _ := bestmatch.Encode(universe)
toUpgrade := bf.Var(best)
solvablenew, _ := bestmatch.BuildFormula(s.DefinitionDatabase, s.ParallelDatabase)
results <- bf.And(bf.Not(toreplaceP), bf.And(append(solvablenew, toUpgrade)...))
}
}
}(wg, all)
@@ -514,8 +513,7 @@ func (s *Parallel) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAsse
// Treat removed packages from universe as marked for deletion
if dropremoved {
// SAT encode the clauses against the world
for _, p := range removed {
for _, p := range removed.Unique() {
encodedP, err := p.Encode(universe)
if err != nil {
return nil, nil, errors.Wrap(err, "couldn't encode package")
@@ -699,7 +697,7 @@ func (s *Parallel) Uninstall(checkconflicts, full bool, packs ...pkg.Package) (p
}
}
s2 := &Parallel{Concurrency: s.Concurrency, InstalledDatabase: pkg.NewInMemoryDatabase(false), DefinitionDatabase: s.DefinitionDatabase, ParallelDatabase: pkg.NewInMemoryDatabase(false)}
s2 := &Parallel{Concurrency: s.Concurrency, InstalledDatabase: pkg.NewInMemoryDatabase(false), DefinitionDatabase: s.InstalledDatabase, ParallelDatabase: pkg.NewInMemoryDatabase(false)}
s2.SetResolver(s.Resolver)
// Get the requirements to install the candidate
asserts, err := s2.Install(toRemove)

View File

@@ -671,8 +671,9 @@ var _ = Describe("Parallel", func() {
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A))
Expect(solution).To(ContainElement(B))
Expect(len(solution)).To(Equal(1))
Expect(len(solution)).To(Equal(2))
})
It("Uninstalls complex packages correctly, even if shared deps are required by system packages", func() {
@@ -1285,12 +1286,172 @@ var _ = Describe("Parallel", func() {
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(solution).To(ContainElement(PackageAssert{Package: A, Value: false}))
Expect(solution).ToNot(ContainElement(PackageAssert{Package: C, Value: true}))
Expect(solution).ToNot(ContainElement(PackageAssert{Package: A, Value: true}))
Expect(len(solution)).To(Equal(4))
Expect(len(solution)).To(Equal(3))
})
It("UpgradeUniverse upgrades correctly", func() {
D := pkg.NewPackage("d", "1.5", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "a", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
D.SetCategory("test")
C = pkg.NewPackage("c", "1.5", []*pkg.DefaultPackage{
&pkg.DefaultPackage{Name: "a", Version: ">=1.0", Category: "test"},
&pkg.DefaultPackage{Name: "d", Version: ">=1.0", Category: "test"},
}, []*pkg.DefaultPackage{})
C.SetCategory("test")
C1 := pkg.NewPackage("c", "1.6", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "a", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
C1.SetCategory("test")
B = pkg.NewPackage("b", "1.0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B.SetCategory("test")
B1 := pkg.NewPackage("b", "1.1", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "c", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
B1.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")
for _, p := range []pkg.Package{A1, B, B1, C, C1, D} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{A, B, D} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
uninstall, solution, err := s.UpgradeUniverse(false)
Expect(err).ToNot(HaveOccurred())
Expect(len(uninstall)).To(Equal(2))
Expect(uninstall).To(ContainElement(A))
Expect(uninstall).To(ContainElement(B))
Expect(solution).To(ContainElement(PackageAssert{Package: A1, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: B1, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: C1, Value: true}))
Expect(len(solution)).To(Equal(6))
})
It("UpgradeUniverse upgrades correctly", func() {
D := pkg.NewPackage("d", "1.5", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "a", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
D.SetCategory("test")
D1 := pkg.NewPackage("d", "1.6", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "a", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
D1.SetCategory("test")
C = pkg.NewPackage("c", "1.5", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
C.SetCategory("test")
B = pkg.NewPackage("b", "1.0", []*pkg.DefaultPackage{
&pkg.DefaultPackage{Name: "c", Version: ">=1.0", Category: "test"},
}, []*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")
for _, p := range []pkg.Package{A, B, C, D, D1} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{A, B, C, D} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
uninstall, solution, err := s.UpgradeUniverse(false)
Expect(err).ToNot(HaveOccurred())
Expect(len(uninstall)).To(Equal(1))
Expect(uninstall[0].GetName()).To(Equal("d"))
Expect(uninstall[0].GetVersion()).To(Equal("1.5"))
Expect(solution).To(ContainElement(PackageAssert{Package: D1, Value: true}))
Expect(len(solution)).To(Equal(3))
})
It("Upgrade upgrades correctly", func() {
D := pkg.NewPackage("d", "1.5", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "a", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
D.SetCategory("test")
C = pkg.NewPackage("c", "1.5", []*pkg.DefaultPackage{
&pkg.DefaultPackage{Name: "a", Version: ">=1.0", Category: "test"},
&pkg.DefaultPackage{Name: "d", Version: ">=1.0", Category: "test"},
}, []*pkg.DefaultPackage{})
C.SetCategory("test")
C1 := pkg.NewPackage("c", "1.6", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "a", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
C1.SetCategory("test")
B = pkg.NewPackage("b", "1.0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B.SetCategory("test")
B1 := pkg.NewPackage("b", "1.1", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "c", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
B1.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")
for _, p := range []pkg.Package{A1, B, B1, C, C1, D} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{A, B, D} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
uninstall, solution, err := s.Upgrade(false, false)
Expect(err).ToNot(HaveOccurred())
Expect(len(uninstall)).To(Equal(2))
Expect(uninstall).To(ContainElement(A))
Expect(uninstall).To(ContainElement(B))
Expect(solution).To(ContainElement(PackageAssert{Package: A1, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: B1, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: C1, Value: true}))
Expect(len(solution)).To(Equal(6))
})
It("Upgrade upgrades correctly", func() {
D := pkg.NewPackage("d", "1.5", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "a", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
D.SetCategory("test")
D1 := pkg.NewPackage("d", "1.6", []*pkg.DefaultPackage{&pkg.DefaultPackage{Name: "a", Version: ">=1.0", Category: "test"}}, []*pkg.DefaultPackage{})
D1.SetCategory("test")
C = pkg.NewPackage("c", "1.5", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
C.SetCategory("test")
B = pkg.NewPackage("b", "1.0", []*pkg.DefaultPackage{
&pkg.DefaultPackage{Name: "c", Version: ">=1.0", Category: "test"},
}, []*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")
for _, p := range []pkg.Package{A, B, C, D, D1} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{A, B, C, D} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
uninstall, solution, err := s.Upgrade(false, false)
Expect(err).ToNot(HaveOccurred())
Expect(len(uninstall)).To(Equal(1))
Expect(uninstall[0].GetName()).To(Equal("d"))
Expect(uninstall[0].GetVersion()).To(Equal("1.5"))
Expect(solution).To(ContainElement(PackageAssert{Package: D1, Value: true}))
Expect(len(solution)).To(Equal(5))
})
})
})

View File

@@ -134,13 +134,13 @@ func (s *Solver) BuildInstalled() (bf.Formula, error) {
var packages pkg.Packages
for _, p := range s.Installed() {
packages = append(packages, p)
for _, dep := range p.Related(s.DefinitionDatabase) {
for _, dep := range p.Related(s.InstalledDatabase) {
packages = append(packages, dep)
}
}
for _, p := range packages {
solvable, err := p.BuildFormula(s.DefinitionDatabase, s.SolverDatabase)
solvable, err := p.BuildFormula(s.InstalledDatabase, s.SolverDatabase)
if err != nil {
return nil, err
}
@@ -399,6 +399,7 @@ func (s *Solver) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAssert
notUptodate := pkg.Packages{}
removed := pkg.Packages{}
toUpgrade := pkg.Packages{}
replacements := map[pkg.Package]pkg.Package{}
// TODO: this is memory expensive, we need to optimize this
universe := pkg.NewInMemoryDatabase(false)
@@ -411,11 +412,9 @@ func (s *Solver) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAssert
// Grab all the installed ones, see if they are eligible for update
for _, p := range s.Installed() {
available, err := universe.FindPackageVersions(p)
if err != nil {
available, err := s.DefinitionDatabase.FindPackageVersions(p)
if len(available) == 0 || err != nil {
removed = append(removed, p)
}
if len(available) == 0 {
continue
}
@@ -424,6 +423,7 @@ func (s *Solver) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAssert
if !bestmatch.Matches(p) {
notUptodate = append(notUptodate, p)
toUpgrade = append(toUpgrade, bestmatch)
replacements[p] = bestmatch
}
}
@@ -437,28 +437,37 @@ func (s *Solver) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAssert
// Treat removed packages from universe as marked for deletion
if dropremoved {
notUptodate = append(notUptodate, removed...)
// SAT encode the clauses against the world
for _, p := range removed.Unique() {
encodedP, err := p.Encode(universe)
if err != nil {
return nil, nil, errors.Wrap(err, "couldn't encode package")
}
P := bf.Var(encodedP)
formulas = append(formulas, bf.And(bf.Not(P), r))
}
}
// SAT encode the clauses against the world
for _, p := range notUptodate.Unique() {
encodedP, err := p.Encode(universe)
for old, new := range replacements {
oldP, err := old.Encode(universe)
if err != nil {
return nil, nil, errors.Wrap(err, "couldn't encode package")
}
P := bf.Var(encodedP)
formulas = append(formulas, bf.And(bf.Not(P), r))
}
for _, p := range toUpgrade {
encodedP, err := p.Encode(universe)
oldencodedP := bf.Var(oldP)
newP, err := new.Encode(universe)
if err != nil {
return nil, nil, errors.Wrap(err, "couldn't encode package")
}
P := bf.Var(encodedP)
formulas = append(formulas, bf.And(P, r))
newEncodedP := bf.Var(newP)
//solvable, err := old.BuildFormula(s.DefinitionDatabase, s.SolverDatabase)
solvablenew, err := new.BuildFormula(s.DefinitionDatabase, s.SolverDatabase)
formulas = append(formulas, bf.And(bf.Not(oldencodedP), bf.And(append(solvablenew, newEncodedP)...)))
}
//formulas = append(formulas, r)
markedForRemoval := pkg.Packages{}
if len(formulas) == 0 {
@@ -607,7 +616,7 @@ func (s *Solver) Uninstall(checkconflicts, full bool, packs ...pkg.Package) (pkg
}
}
s2 := NewSolver(Options{Type: SingleCoreSimple}, pkg.NewInMemoryDatabase(false), s.DefinitionDatabase, pkg.NewInMemoryDatabase(false))
s2 := NewSolver(Options{Type: SingleCoreSimple}, pkg.NewInMemoryDatabase(false), s.InstalledDatabase, pkg.NewInMemoryDatabase(false))
s2.SetResolver(s.Resolver)
// Get the requirements to install the candidate

View File

@@ -671,8 +671,9 @@ var _ = Describe("Solver", func() {
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A))
Expect(solution).To(ContainElement(B))
Expect(len(solution)).To(Equal(1))
Expect(len(solution)).To(Equal(2))
})
It("Uninstalls complex packages correctly, even if shared deps are required by system packages", func() {
@@ -1209,6 +1210,8 @@ var _ = Describe("Solver", func() {
})
})
Context("Upgrades", func() {
E := pkg.NewPackage("e", "1.5", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
E.SetCategory("test")
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{})
@@ -1296,10 +1299,37 @@ var _ = Describe("Solver", func() {
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(solution).To(ContainElement(PackageAssert{Package: A, Value: false}))
Expect(len(solution)).To(Equal(4))
Expect(solution).ToNot(ContainElement(PackageAssert{Package: C, Value: true}))
Expect(solution).ToNot(ContainElement(PackageAssert{Package: A, Value: true}))
Expect(len(solution)).To(Equal(3))
})
It("Suggests to remove untracked packages", func() {
for _, p := range []pkg.Package{E} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{A, B, C, E} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
uninstall, solution, err := s.UpgradeUniverse(true)
Expect(err).ToNot(HaveOccurred())
Expect(len(uninstall)).To(Equal(3))
Expect(uninstall).To(ContainElement(B))
Expect(uninstall).To(ContainElement(A))
Expect(uninstall).To(ContainElement(C))
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: A, Value: false}))
Expect(len(solution)).To(Equal(3))
})
})
})

View File

@@ -119,8 +119,8 @@ testUpgrade() {
assertTrue 'package installed new A' "[ -e '$tmpdir/testrootfs/testlatest' ]"
# It does remove C as well, no other package depends on it.
assertContains 'does contain test/c-1.0' "$upgrade" 'test/c-1.0'
assertNotContains 'does not attempt to download test/c-1.0' "$upgrade" 'test/c-1.0 downloaded'
#assertContains 'does contain test/c-1.0' "$upgrade" 'test/c-1.0'
#assertNotContains 'does not attempt to download test/c-1.0' "$upgrade" 'test/c-1.0 downloaded'
}
# Load shUnit2.