diff --git a/pkg/api/core/image/delta_test.go b/pkg/api/core/image/delta_test.go index b47ce73c..6825af61 100644 --- a/pkg/api/core/image/delta_test.go +++ b/pkg/api/core/image/delta_test.go @@ -53,17 +53,18 @@ var _ = Describe("Delta", func() { var img, img2 v1.Image var err error - ref, _ = name.ParseReference("alpine") - ref2, _ = name.ParseReference("golang:alpine") - img, _ = daemon.Image(ref) - img2, _ = daemon.Image(ref2) - BeforeEach(func() { ctx = context.NewContext() + ctx.Config.General.Debug = true tmpfile, err = ioutil.TempFile("", "delta") Expect(err).ToNot(HaveOccurred()) defer os.RemoveAll(tmpfile.Name()) // clean up + + ref, _ = name.ParseReference("alpine") + ref2, _ = name.ParseReference("golang:1.16-alpine3.14") + img, _ = daemon.Image(ref) + img2, _ = daemon.Image(ref2) }) It("Extract all deltas", func() { diff --git a/pkg/api/core/image/mutator_suite_test.go b/pkg/api/core/image/mutator_suite_test.go index 0fa3c343..a15f38c1 100644 --- a/pkg/api/core/image/mutator_suite_test.go +++ b/pkg/api/core/image/mutator_suite_test.go @@ -29,6 +29,6 @@ func TestImageApi(t *testing.T) { b := backend.NewSimpleDockerBackend(context.NewContext()) b.DownloadImage(backend.Options{ImageName: "alpine"}) b.DownloadImage(backend.Options{ImageName: "golang:alpine"}) - + b.DownloadImage(backend.Options{ImageName: "golang:1.16-alpine3.14"}) RunSpecs(t, "Image API Suite") } diff --git a/pkg/api/core/types/package.go b/pkg/api/core/types/package.go index a4389e8f..32905d56 100644 --- a/pkg/api/core/types/package.go +++ b/pkg/api/core/types/package.go @@ -291,6 +291,11 @@ func (p *Package) String() string { return fmt.Sprintf("%s", string(b)) } +// HasVersionDefined returns true when a specific version of a package is implied +func (p *Package) HasVersionDefined() bool { + return p.Version != ">=0" +} + // GetFingerPrint returns a UUID of the package. // FIXME: this needs to be unique, now just name is generalized func (p *Package) GetFingerPrint() string { diff --git a/pkg/installer/installer.go b/pkg/installer/installer.go index 17475db7..c16a9077 100644 --- a/pkg/installer/installer.go +++ b/pkg/installer/installer.go @@ -552,21 +552,28 @@ func (l *LuetInstaller) Install(cp types.Packages, s *System) error { return err } - // Check if we have to process something, or return to the user an error - if len(match) == 0 { - l.Options.Context.Info("No packages to install") - return nil - } + allInstalled := true + // Resolvers might decide to remove some packages from being installed - if l.Options.SolverOptions.Type != solver.QLearningResolverType { + if !solver.IsRelaxedResolver(l.Options.SolverOptions) { for _, p := range cp { found := false - vers, _ := s.Database.FindPackageVersions(p) // If was installed, it is found, as it was filtered - if len(vers) >= 1 { - found = true - continue + + if p.HasVersionDefined() { + f, err := s.Database.FindPackage(p) + if f != nil && err == nil { + found = true + continue + } + } else { + vers, _ := s.Database.FindPackageVersions(p) // If was installed, it is found, as it was filtered + if len(vers) >= 1 { + found = true + continue + } } + allInstalled = false for _, m := range match { if m.Package.GetName() == p.GetName() { found = true @@ -583,8 +590,17 @@ func (l *LuetInstaller) Install(cp types.Packages, s *System) error { } } } + + // Check if we have to process something, or return to the user an error + if len(match) == 0 { + l.Options.Context.Info("No packages to install") + if !solver.IsRelaxedResolver(l.Options.SolverOptions) && !allInstalled { + return fmt.Errorf("could not find packages to install from the repositories in the system") + } + return nil + } + l.Options.Context.Info("Packages that are going to be installed in the system:") - //l.Options.Context.Info("Packages that are going to be installed in the system: \n ", Green(matchesToList(match)).BgBlack().String()) printMatches(match) diff --git a/pkg/installer/installer_test.go b/pkg/installer/installer_test.go index be72bde2..26a28387 100644 --- a/pkg/installer/installer_test.go +++ b/pkg/installer/installer_test.go @@ -26,6 +26,7 @@ import ( compiler "github.com/mudler/luet/pkg/compiler" backend "github.com/mudler/luet/pkg/compiler/backend" fileHelper "github.com/mudler/luet/pkg/helpers/file" + "github.com/mudler/luet/pkg/solver" pkg "github.com/mudler/luet/pkg/database" . "github.com/mudler/luet/pkg/installer" @@ -1161,6 +1162,7 @@ urls: Expect(err).ToNot(HaveOccurred()) inst := NewLuetInstaller(LuetInstallerOptions{ + Relaxed: true, Concurrency: 1, Context: ctx, PackageRepositories: types.LuetRepositories{*repo2.LuetRepository}, }) @@ -1174,6 +1176,28 @@ urls: systemDB := pkg.NewBoltDatabase(filepath.Join(bolt, "db.db")) system := &System{Database: systemDB, Target: fakeroot} + + err = inst.Install([]*types.Package{&types.Package{Name: "b", Category: "test", Version: "1.0"}}, system) + Expect(err).ToNot(HaveOccurred()) + + err = inst.Install([]*types.Package{&types.Package{Name: "b", Category: "foo", Version: "1.0"}}, system) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("where are definitions coming from")) + + err = inst.Install([]*types.Package{&types.Package{Name: "b", Category: "foo", Version: ">=0"}}, system) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("package 'foo/b->=0' not found")) + + inst2 := NewLuetInstaller(LuetInstallerOptions{ + SolverOptions: types.LuetSolverOptions{Type: solver.QLearningResolverType}, + Relaxed: true, + Concurrency: 1, Context: ctx, + PackageRepositories: types.LuetRepositories{*repo2.LuetRepository}, + }) + err = inst2.Install([]*types.Package{&types.Package{Name: "b"}, &types.Package{Name: "b", Category: "test", Version: ">=0"}}, system) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("where are definitions coming from")) + err = inst.Install([]*types.Package{&types.Package{Name: "b", Category: "test", Version: "1.0"}}, system) Expect(err).ToNot(HaveOccurred()) diff --git a/pkg/solver/solver.go b/pkg/solver/solver.go index d306b94c..f88fe73b 100644 --- a/pkg/solver/solver.go +++ b/pkg/solver/solver.go @@ -40,6 +40,13 @@ type Solver struct { Resolver types.PackageResolver } +// IsRelaxedResolver returns true wether a solver might +// take action on user side, by removing some installation constraints +// or taking automated actions (e.g. qlearning) +func IsRelaxedResolver(t types.LuetSolverOptions) bool { + return t.Type == QLearningResolverType +} + // NewSolver accepts as argument two lists of packages, the first is the initial set, // the second represent all the known packages. func NewSolver(t types.SolverOptions, installed types.PackageDatabase, definitiondb types.PackageDatabase, solverdb types.PackageDatabase) types.PackageSolver { diff --git a/tests/integration/01_simple.sh b/tests/integration/01_simple.sh index 54cfb5b4..766c6ce6 100755 --- a/tests/integration/01_simple.sh +++ b/tests/integration/01_simple.sh @@ -57,11 +57,39 @@ EOF } testInstall() { + luet install -y --config $tmpdir/luet.yaml test/foobar + installst=$? + assertEquals 'install test fails' "$installst" "2" + + luet install -y --config $tmpdir/luet.yaml test/foobar test/c + installst=$? + assertEquals 'install test fails' "$installst" "2" + + luet install -y --config $tmpdir/luet.yaml test/foobar@1.0 + installst=$? + assertEquals 'install test fails' "$installst" "2" + + luet install -y --config $tmpdir/luet.yaml test/foobar@1.0 test/c@1.0 + installst=$? + assertEquals 'install test fails' "$installst" "2" + + luet install -y --config $tmpdir/luet.yaml test/foobar@1.0 test/c + installst=$? + assertEquals 'install test fails' "$installst" "2" + luet install -y --config $tmpdir/luet.yaml test/c - #luet install -y --config $tmpdir/luet.yaml test/c@1.0 > /dev/null installst=$? assertEquals 'install test successfully' "$installst" "0" assertTrue 'package installed' "[ -e '$tmpdir/testrootfs/c' ]" + + luet install -y --config $tmpdir/luet.yaml test/foobar test/c + installst=$? + assertEquals 'install test fails' "$installst" "2" + + # Already installed + luet install -y --config $tmpdir/luet.yaml test/c@1.0 + installst=$? + assertEquals 'install test fails' "$installst" "0" } testReInstall() {