Compare commits

...

7 Commits
0.6.2 ... 0.6.3

Author SHA1 Message Date
Ettore Di Giacinto
575079bb77 Prepare for 0.6.3 tag 2020-02-27 18:57:13 +01:00
Ettore Di Giacinto
5bcc8d112a Enforce solver constraints
- Don't sign installed packages during finalizer execution
- Enforce solver constraints: build ALO and AMO rules taking into account
  that the current package might not be selected at all.
- Force uninstalls on upgrade
- Enable option to tell uninstall to ignore conflict with the analized system state,
  as we don't want any conflict with the installed to raise during the upgrade.
  In this way we both force uninstalls and we avoid to check with conflicts
  against the current system state which is pending to deletion.
  This is due to the fact that now the solver enforces the constraints
  and explictly denies two packages of the same version installed.
- Adapt test as now we generate more constraints, which makes the solver more
  noisy on the package that are explictly selected or not
2020-02-27 18:38:31 +01:00
Ettore Di Giacinto
65b17f5283 Add simple upgrade integration test 2020-02-26 23:59:48 +01:00
Ettore Di Giacinto
a0618107a8 Adapt tests 2020-02-26 23:56:21 +01:00
Ettore Di Giacinto
25f2abf103 Do conflict with same package versions while building formula 2020-02-26 23:01:08 +01:00
Ettore Di Giacinto
e3ebfd6bfe Respect ImageRepository with build --nodeps 2020-02-26 19:00:22 +01:00
Ettore Di Giacinto
48df98bcae Add dev version tag 2020-02-23 22:42:02 +01:00
23 changed files with 336 additions and 87 deletions

View File

@@ -37,7 +37,7 @@ var cfgFile string
var Verbose bool
const (
LuetCLIVersion = "0.6.2"
LuetCLIVersion = "0.6.3"
LuetEnvPrefix = "LUET"
)

View File

@@ -631,7 +631,7 @@ func (cs *LuetCompiler) compile(concurrency int, keepPermissions bool, p Compila
}
} else if len(dependencies) > 0 {
lastHash = dependencies[len(dependencies)-1].Hash.PackageHash
lastHash = cs.ImageRepository + ":" + dependencies[len(dependencies)-1].Hash.PackageHash
}
if !cs.Options.OnlyDeps {

View File

@@ -106,23 +106,39 @@ func (l *LuetInstaller) Upgrade(s *System) error {
return err
}
// First match packages against repositories by priority
// matches := syncedRepos.PackageMatches(p)
allRepos := pkg.NewInMemoryDatabase(false)
syncedRepos.SyncDatabase(allRepos)
// compute a "big" world
solv := solver.NewResolver(s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
uninstall, solution, err := solv.Upgrade()
uninstall, solution, err := solv.Upgrade(false)
if err != nil {
return errors.Wrap(err, "Failed solving solution for upgrade")
}
// We don't want any conflict with the installed to raise during the upgrade.
// In this way we both force uninstalls and we avoid to check with conflicts
// against the current system state which is pending to deletion
// E.g. you can't check for conflicts for an upgrade of a new version of A
// if the old A results installed in the system. This is due to the fact that
// now the solver enforces the constraints and explictly denies two packages
// of the same version installed.
forced := false
if l.Options.Force {
forced = true
}
l.Options.Force = true
for _, u := range uninstall {
Info(":package: Marked for deletion", u.HumanReadableString())
err := l.Uninstall(u, s)
if err != nil && !l.Options.Force {
Error("Failed uninstall for ", u.HumanReadableString())
return errors.Wrap(err, "uninstalling "+u.HumanReadableString())
}
}
l.Options.Force = forced
toInstall := []pkg.Package{}
for _, assertion := range solution {
@@ -249,29 +265,27 @@ func (l *LuetInstaller) Install(cp []pkg.Package, s *System) error {
close(all)
wg.Wait()
for _, c := range toInstall {
// Annotate to the system that the package was installed
_, err := s.Database.CreatePackage(c.Package)
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Failed creating package")
}
}
executedFinalizer := map[string]bool{}
// TODO: Lower those errors as warning
for _, w := range p {
// Finalizers needs to run in order and in sequence.
ordered := solution.Order(allRepos, w.GetFingerPrint())
ORDER:
for _, ass := range ordered {
if ass.Value {
// Annotate to the system that the package was installed
if _, err := s.Database.FindPackage(ass.Package); err == nil {
err := s.Database.UpdatePackage(ass.Package)
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Failed updating package")
}
} else {
_, err := s.Database.CreatePackage(ass.Package)
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Failed creating package")
}
}
installed, ok := toInstall[ass.Package.GetFingerPrint()]
if !ok {
return errors.New("Couldn't find ArtifactMatch for " + ass.Package.HumanReadableString())
// It was a dep already installed in the system, so we can skip it safely
continue ORDER
}
treePackage, err := installed.Repository.GetTree().GetDatabase().FindPackage(ass.Package)
@@ -381,12 +395,17 @@ 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) TODO - mark the uninstallation in db
// Get installed definition
checkConflicts := true
if l.Options.Force == true {
checkConflicts = false
}
if !l.Options.NoDeps {
solv := solver.NewResolver(s.Database, s.Database, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
solution, err := solv.Uninstall(p)
solution, err := solv.Uninstall(p, checkConflicts)
if err != nil && !l.Options.Force {
return errors.Wrap(err, "Could not solve the uninstall constraints. Tip: try with --solver-type qlearning or with --force, or by removing packages excluding their dependencies with --nodeps")
}

View File

@@ -468,6 +468,22 @@ func (pack *DefaultPackage) BuildFormula(definitiondb PackageDatabase, db Packag
A := bf.Var(encodedA)
var formulas []bf.Formula
// Do conflict with other packages versions (if A is selected, then conflict with other versions of A)
packages, _ := definitiondb.FindPackageVersions(p)
if len(packages) > 0 {
for _, cp := range packages {
encodedB, err := cp.Encode(db)
if err != nil {
return nil, err
}
B := bf.Var(encodedB)
if !p.Matches(cp) {
formulas = append(formulas, bf.Or(bf.Not(A), bf.Or(bf.Not(A), bf.Not(B))))
}
}
}
for _, requiredDef := range p.GetRequires() {
required, err := definitiondb.FindPackage(requiredDef)
if err != nil || requiredDef.IsSelector() {
@@ -479,62 +495,60 @@ func (pack *DefaultPackage) BuildFormula(definitiondb PackageDatabase, db Packag
if err != nil || len(packages) == 0 {
required = requiredDef
} else {
if len(packages) == 1 {
required = packages[0]
} else {
var ALO, priorityConstraints, priorityALO []bf.Formula
// Try to prio best match
// Force the solver to consider first our candidate (if does exists).
// Then builds ALO and AMO for the requires.
c, candidateErr := definitiondb.FindPackageCandidate(requiredDef)
var C bf.Formula
if candidateErr == nil {
// We have a desired candidate, try to look a solution with that included first
for _, o := range packages {
encodedB, err := o.Encode(db)
if err != nil {
return nil, err
}
B := bf.Var(encodedB)
if !o.Matches(c) {
priorityConstraints = append(priorityConstraints, bf.Not(B))
priorityALO = append(priorityALO, B)
}
}
encodedC, err := c.Encode(db)
if err != nil {
return nil, err
}
C = bf.Var(encodedC)
// Or the Candidate is true, or all the others might be not true
// This forces the CDCL sat implementation to look first at a solution with C=true
formulas = append(formulas, bf.Or(bf.Or(C, bf.Or(priorityConstraints...)), bf.Or(bf.Not(C), bf.Or(priorityALO...))))
}
var ALO, priorityConstraints, priorityALO []bf.Formula
// AMO - At most one
// Try to prio best match
// Force the solver to consider first our candidate (if does exists).
// Then builds ALO and AMO for the requires.
c, candidateErr := definitiondb.FindPackageCandidate(requiredDef)
var C bf.Formula
if candidateErr == nil {
// We have a desired candidate, try to look a solution with that included first
for _, o := range packages {
encodedB, err := o.Encode(db)
if err != nil {
return nil, err
}
B := bf.Var(encodedB)
ALO = append(ALO, B)
for _, i := range packages {
encodedI, err := i.Encode(db)
if err != nil {
return nil, err
}
I := bf.Var(encodedI)
if !o.Matches(i) {
formulas = append(formulas, bf.Or(bf.Not(I), bf.Not(B)))
}
if !o.Matches(c) {
priorityConstraints = append(priorityConstraints, bf.Not(B))
priorityALO = append(priorityALO, B)
}
}
formulas = append(formulas, bf.Or(ALO...)) // ALO - At least one
continue
encodedC, err := c.Encode(db)
if err != nil {
return nil, err
}
C = bf.Var(encodedC)
// Or the Candidate is true, or all the others might be not true
// This forces the CDCL sat implementation to look first at a solution with C=true
formulas = append(formulas, bf.Or(bf.Not(A), bf.Or(bf.Or(C, bf.Or(priorityConstraints...)), bf.Or(bf.Not(C), bf.Or(priorityALO...)))))
}
// AMO - At most one
for _, o := range packages {
encodedB, err := o.Encode(db)
if err != nil {
return nil, err
}
B := bf.Var(encodedB)
ALO = append(ALO, B)
for _, i := range packages {
encodedI, err := i.Encode(db)
if err != nil {
return nil, err
}
I := bf.Var(encodedI)
if !o.Matches(i) {
formulas = append(formulas, bf.Or(bf.Not(A), bf.Or(bf.Not(I), bf.Not(B))))
}
}
}
formulas = append(formulas, bf.Or(bf.Not(A), bf.Or(ALO...))) // ALO - At least one
continue
}
}
encodedB, err := required.Encode(db)
@@ -600,6 +614,7 @@ func (pack *DefaultPackage) BuildFormula(definitiondb PackageDatabase, db Packag
formulas = append(formulas, f...)
}
return formulas, nil
}

View File

@@ -211,7 +211,7 @@ var _ = Describe("Package", func() {
f, err := a1.BuildFormula(definitions, db)
Expect(err).ToNot(HaveOccurred())
Expect(len(f)).To(Equal(2))
Expect(len(f)).To(Equal(8))
// Expect(f[0].String()).To(Equal("or(not(c31f5842), a4910f77)"))
// Expect(f[1].String()).To(Equal("or(not(c31f5842), not(a97670be))"))
})

View File

@@ -28,11 +28,11 @@ import (
type PackageSolver interface {
SetDefinitionDatabase(pkg.PackageDatabase)
Install(p []pkg.Package) (PackagesAssertions, error)
Uninstall(candidate pkg.Package) ([]pkg.Package, error)
Uninstall(candidate pkg.Package, checkconflicts bool) ([]pkg.Package, error)
ConflictsWithInstalled(p pkg.Package) (bool, error)
ConflictsWith(p pkg.Package, ls []pkg.Package) (bool, error)
World() []pkg.Package
Upgrade() ([]pkg.Package, PackagesAssertions, error)
Upgrade(checkconflicts bool) ([]pkg.Package, PackagesAssertions, error)
SetResolver(PackageResolver)
@@ -210,7 +210,7 @@ func (s *Solver) ConflictsWithInstalled(p pkg.Package) (bool, error) {
return s.ConflictsWith(p, s.Installed())
}
func (s *Solver) Upgrade() ([]pkg.Package, PackagesAssertions, error) {
func (s *Solver) Upgrade(checkconflicts bool) ([]pkg.Package, PackagesAssertions, error) {
// First get candidates that needs to be upgraded..
@@ -236,12 +236,11 @@ func (s *Solver) Upgrade() ([]pkg.Package, PackagesAssertions, error) {
}
}
}
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
for _, p := range toUninstall {
r, err := s.Uninstall(p)
r, err := s.Uninstall(p, checkconflicts)
if err != nil {
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't uninstall selected candidate "+p.GetFingerPrint())
}
@@ -262,7 +261,7 @@ func (s *Solver) Upgrade() ([]pkg.Package, PackagesAssertions, error) {
// 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.
func (s *Solver) Uninstall(c pkg.Package) ([]pkg.Package, error) {
func (s *Solver) Uninstall(c pkg.Package, checkconflicts bool) ([]pkg.Package, error) {
var res []pkg.Package
candidate, err := s.InstalledDatabase.FindPackage(c)
if err != nil {
@@ -294,17 +293,19 @@ func (s *Solver) Uninstall(c pkg.Package) ([]pkg.Package, error) {
}
}
s2 := NewSolver(pkg.NewInMemoryDatabase(false), s.DefinitionDatabase, pkg.NewInMemoryDatabase(false))
s2.SetResolver(s.Resolver)
// Get the requirements to install the candidate
saved := s.InstalledDatabase
s.InstalledDatabase = pkg.NewInMemoryDatabase(false)
asserts, err := s.Install([]pkg.Package{candidate})
asserts, err := s2.Install([]pkg.Package{candidate})
if err != nil {
return nil, err
}
s.InstalledDatabase = saved
for _, a := range asserts {
if a.Value {
if !checkconflicts {
res = append(res, a.Package.IsFlagged(false))
continue
}
c, err := s.ConflictsWithInstalled(a.Package)
if err != nil {

View File

@@ -429,7 +429,7 @@ var _ = Describe("Solver", func() {
Expect(solution).To(ContainElement(PackageAssert{Package: D1, Value: false}))
Expect(solution).ToNot(ContainElement(PackageAssert{Package: E, Value: true}))
Expect(len(solution)).To(Equal(5))
Expect(len(solution)).To(Equal(6))
Expect(err).ToNot(HaveOccurred())
})
@@ -470,7 +470,7 @@ var _ = Describe("Solver", func() {
Expect(solution).To(ContainElement(PackageAssert{Package: D1, Value: false}))
Expect(solution).ToNot(ContainElement(PackageAssert{Package: E, Value: true}))
Expect(len(solution)).To(Equal(4))
Expect(len(solution)).To(Equal(6))
Expect(err).ToNot(HaveOccurred())
})
@@ -511,7 +511,7 @@ var _ = Describe("Solver", func() {
Expect(solution).To(ContainElement(PackageAssert{Package: D1, Value: false}))
Expect(solution).ToNot(ContainElement(PackageAssert{Package: E, Value: true}))
Expect(len(solution)).To(Equal(4))
Expect(len(solution)).To(Equal(6))
Expect(err).ToNot(HaveOccurred())
})
@@ -533,7 +533,7 @@ var _ = Describe("Solver", func() {
}
s = NewSolver(dbInstalled, dbDefinitions, db)
solution, err := s.Uninstall(A)
solution, err := s.Uninstall(A, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -559,7 +559,7 @@ var _ = Describe("Solver", func() {
}
s = NewSolver(dbInstalled, dbDefinitions, db)
solution, err := s.Uninstall(&pkg.DefaultPackage{Name: "A", Version: ">1.0"})
solution, err := s.Uninstall(&pkg.DefaultPackage{Name: "A", Version: ">1.0"}, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -674,7 +674,7 @@ var _ = Describe("Solver", func() {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Uninstall(A)
solution, err := s.Uninstall(A, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -698,7 +698,7 @@ var _ = Describe("Solver", func() {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Uninstall(A)
solution, err := s.Uninstall(A, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -721,7 +721,7 @@ var _ = Describe("Solver", func() {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Uninstall(A)
solution, err := s.Uninstall(A, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -746,7 +746,7 @@ var _ = Describe("Solver", func() {
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Uninstall(A)
solution, err := s.Uninstall(A, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -772,7 +772,7 @@ var _ = Describe("Solver", func() {
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Uninstall(A)
solution, err := s.Uninstall(A, true)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A.IsFlagged(false)))
@@ -893,7 +893,7 @@ var _ = Describe("Solver", func() {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
uninstall, solution, err := s.Upgrade()
uninstall, solution, err := s.Upgrade(true)
Expect(err).ToNot(HaveOccurred())
Expect(len(uninstall)).To(Equal(1))

View 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"

View File

@@ -0,0 +1,9 @@
category: "test"
name: "c"
version: "1.0"
# Boom?
requires:
- category: "test"
name: "a"
version: ">=0.1"

View 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"

View File

@@ -0,0 +1,8 @@
category: "test"
name: "a"
version: "1.1"
requires:
- category: "test"
name: "b"
version: ">=0.1"

View File

@@ -0,0 +1,11 @@
image: "alpine"
prelude:
- echo foo > /test
- echo bar > /test2
steps:
- echo artifact3 > /testaa
- echo artifact4 > /testaa2
requires:
- category: "test"
name: "b"
version: "1.0"

View File

@@ -0,0 +1,3 @@
category: "test"
name: "a"
version: "1.0"

View File

@@ -0,0 +1,11 @@
image: "alpine"
prelude:
- echo foo > /test
- echo bar > /test2
steps:
- echo artifact3 > /testlatest
- echo artifact4 > /testlatest2
requires:
- category: "test"
name: "b"
version: "1.0"

View File

@@ -0,0 +1,3 @@
category: "test"
name: "a"
version: "1.2"

View 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

View File

@@ -0,0 +1,3 @@
category: "test"
name: "b"
version: "1.1"

View File

@@ -0,0 +1 @@
echo generated > /sonewc

View 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

View File

@@ -0,0 +1,3 @@
category: "test"
name: "b"
version: "1.0"

View File

@@ -0,0 +1 @@
echo generated > /artifact42

View File

@@ -12,7 +12,7 @@ oneTimeTearDown() {
testBuild() {
mkdir $tmpdir/testbuild
luet build --all --tree "$ROOT_DIR/tests/fixtures/qlearning" --destination $tmpdir/testbuild --compression gzip
luet build --all --concurrency 1 --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' ]"

122
tests/integration/05_upgrade.sh Executable file
View File

@@ -0,0 +1,122 @@
#!/bin/bash
export LUET_NOLOCK=true
oneTimeSetUp() {
export tmpdir="$(mktemp -d)"
}
oneTimeTearDown() {
rm -rf "$tmpdir"
}
testBuild() {
mkdir $tmpdir/testbuild
luet build --tree "$ROOT_DIR/tests/fixtures/upgrade_integration" --destination $tmpdir/testbuild --compression gzip test/b-1.0
buildst=$?
assertTrue 'create package B 1.0' "[ -e '$tmpdir/testbuild/b-test-1.0.package.tar.gz' ]"
assertEquals 'builds successfully' "$buildst" "0"
luet build --tree "$ROOT_DIR/tests/fixtures/upgrade_integration" --destination $tmpdir/testbuild --compression gzip test/b-1.1
buildst=$?
assertEquals 'builds successfully' "$buildst" "0"
assertTrue 'create package B 1.1' "[ -e '$tmpdir/testbuild/b-test-1.1.package.tar.gz' ]"
luet build --tree "$ROOT_DIR/tests/fixtures/upgrade_integration" --destination $tmpdir/testbuild --compression gzip test/a-1.0
buildst=$?
assertEquals 'builds successfully' "$buildst" "0"
assertTrue 'create package A 1.0' "[ -e '$tmpdir/testbuild/a-test-1.0.package.tar.gz' ]"
luet build --tree "$ROOT_DIR/tests/fixtures/upgrade_integration" --destination $tmpdir/testbuild --compression gzip test/a-1.1
buildst=$?
assertEquals 'builds successfully' "$buildst" "0"
assertTrue 'create package A 1.1' "[ -e '$tmpdir/testbuild/a-test-1.1.package.tar.gz' ]"
luet build --tree "$ROOT_DIR/tests/fixtures/upgrade_integration" --destination $tmpdir/testbuild --compression gzip test/a-1.2
buildst=$?
assertEquals 'builds successfully' "$buildst" "0"
assertTrue 'create package A 1.2' "[ -e '$tmpdir/testbuild/a-test-1.2.package.tar.gz' ]"
luet build --tree "$ROOT_DIR/tests/fixtures/upgrade_integration" --destination $tmpdir/testbuild --compression gzip test/c-1.0
buildst=$?
assertEquals 'builds successfully' "$buildst" "0"
assertTrue 'create package C 1.0' "[ -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/upgrade_integration" \
--output $tmpdir/testbuild \
--packages $tmpdir/testbuild \
--name "test" \
--descr "Test Repo" \
--urls $tmpdir/testrootfs \
--type disk
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/b-1.0
installst=$?
assertEquals 'install test successfully' "$installst" "0"
assertTrue 'package installed B' "[ -e '$tmpdir/testrootfs/test5' ]"
luet install --config $tmpdir/luet.yaml test/a-1.0
assertTrue 'package installed A' "[ -e '$tmpdir/testrootfs/testaa' ]"
installst=$?
assertEquals 'install test successfully' "$installst" "0"
luet install --config $tmpdir/luet.yaml test/a-1.1
assertTrue 'package installed A' "[ -e '$tmpdir/testrootfs/testaa' ]"
installst=$?
assertEquals 'install test successfully' "$installst" "0"
assertTrue 'package keeps old A' "[ -e '$tmpdir/testrootfs/testaa' ]"
assertTrue 'package new A was not installed' "[ ! -e '$tmpdir/testrootfs/testlatest' ]"
luet install --config $tmpdir/luet.yaml test/c-1.0
installst=$?
assertEquals 'install test successfully' "$installst" "0"
assertTrue 'package installed C' "[ -e '$tmpdir/testrootfs/c' ]"
}
testUpgrade() {
luet --config $tmpdir/luet.yaml upgrade
installst=$?
assertEquals 'install test successfully' "$installst" "0"
assertTrue 'package uninstalled B' "[ ! -e '$tmpdir/testrootfs/test5' ]"
assertTrue 'package installed B' "[ -e '$tmpdir/testrootfs/newc' ]"
assertTrue 'package uninstalled A' "[ ! -e '$tmpdir/testrootfs/testaa' ]"
assertTrue 'package installed new A' "[ -e '$tmpdir/testrootfs/testlatest' ]"
}
# Load shUnit2.
. "$ROOT_DIR/tests/integration/shunit2"/shunit2