Compare commits

...

11 Commits

Author SHA1 Message Date
Ettore Di Giacinto
bac9bac25f ⬆️ Tag 0.32.2 2022-06-06 17:39:58 +02:00
Ettore Di Giacinto
ce95b3ada4 🎨 Cleanups 2022-06-06 17:39:16 +02:00
mudler
49d8c2b972 🎨 Add test-integration-docker target 2022-06-06 17:39:16 +02:00
mudler
2b3a1555f0 🎨 Refactor, use ensureoder where necessary 2022-06-06 17:39:16 +02:00
Itxaka
03e72653c7 Fill referenceID on repo sync (#300)
Signed-off-by: Itxaka <igarcia@suse.com>
2022-06-06 17:36:19 +02:00
Ettore Di Giacinto
264bf53fe7 🎨 Accept types.Logger in WithLogger 2022-05-26 13:09:37 +00:00
Ettore Di Giacinto
edd2275bf5 🎨 Allow to pass by a logger interface to context 2022-05-26 13:06:47 +00:00
Ettore Di Giacinto
d6ae727d79 🐛 Fix handlelock panic
Somehow this slipped in, as we should check the lock only if we have enough args.

Fixes #297
2022-05-25 10:07:25 +00:00
Ettore Di Giacinto
fea872aba0 ⬆️ Tag 0.32.1 2022-05-24 23:05:35 +02:00
Ettore Di Giacinto
1006be9271 🐛 Bail out when no packages are found with default solvers (#296)
* 🐛 Bail out when no packages are found with default solvers

Checking packages is more tricky when a resolver is set. Resolvers
are capable of mutating the user request and remove part of the
constraints in order to resolve a specific solution.

This had the countereffect on a normal solver to not detect correctly
packages when missing from the wanted set and not proposed during
installation.

This should fix all the cases above taking into consideration of
resolvers and adding specific test-cases for it.

* ⚙️ Pin to tag for test image
2022-05-24 23:01:56 +02:00
Ettore Di Giacinto
b5da2fa7b4 ⚙️ Fixup corner case when templating requires
Adds also specific tests to cover that area
2022-04-28 12:57:36 +02:00
37 changed files with 442 additions and 102 deletions

View File

@@ -81,6 +81,12 @@ test-docker:
--workdir /go/src/github.com/mudler/luet -ti golang:latest \
bash -c "make test"
.PHONY: test-integration-docker
test-integration-docker:
docker run -v $(ROOT_DIR):/go/src/github.com/mudler/luet -v /var/run/docker.sock:/var/run/docker.sock \
--workdir /go/src/github.com/mudler/luet -ti golang:latest \
bash -c "apt-get update && apt-get install docker.io && make test-integration"
multiarch-build:
goreleaser build --snapshot --rm-dist

View File

@@ -30,7 +30,7 @@ var cfgFile string
var Verbose bool
const (
LuetCLIVersion = "0.32.0"
LuetCLIVersion = "0.32.2"
LuetEnvPrefix = "LUET"
)

View File

@@ -55,11 +55,7 @@ func TemplateFolders(ctx *context.Context, i installer.BuildTreeResult, treePath
}
func HandleLock() {
if os.Getenv("LUET_NOLOCK") == "true" {
return
}
if len(os.Args) == 0 {
if os.Getenv("LUET_NOLOCK") == "true" || len(os.Args) < 2 {
return
}

1
go.mod
View File

@@ -53,6 +53,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/pterm/pterm v0.12.32-0.20211002183613-ada9ef6790c3
github.com/rancher-sandbox/gofilecache v0.0.0-20210330135715-becdeff5df15
github.com/scrohde/go-toposort v0.0.0-20170629203416-a9a902e065a3 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.8.1

2
go.sum
View File

@@ -1300,6 +1300,8 @@ github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
github.com/scrohde/go-toposort v0.0.0-20170629203416-a9a902e065a3 h1:i8yAzeysv+UgLJ0O8usGQHvtyM0AbPHlI03l5OnWD4A=
github.com/scrohde/go-toposort v0.0.0-20170629203416-a9a902e065a3/go.mod h1:FzYqW619hxj8ogwgAr2ENoIELLwatZvaBnWdTquP99U=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=

View File

@@ -30,7 +30,7 @@ import (
)
type Context struct {
*logger.Logger
types.Logger
context.Context
types.GarbageCollector
Config *types.LuetConfig
@@ -51,7 +51,7 @@ func (c *Context) GetAnnotation(s string) interface{} {
type ContextOption func(c *Context) error
// WithLogger sets the logger
func WithLogger(l *logger.Logger) ContextOption {
func WithLogger(l types.Logger) ContextOption {
return func(c *Context) error {
c.Logger = l
return nil
@@ -122,13 +122,14 @@ func (c *Context) WithLoggingContext(name string) types.Context {
ctxCopy.Config = &configCopy
ctxCopy.annotations = ctx.annotations
ctxCopy.Logger, _ = c.Logger.Copy(logger.WithContext(name))
ctxCopy.Logger, _ = c.Logger.Copy()
ctxCopy.Logger.SetContext(name)
return ctxCopy
}
// Copy returns a context copy with a reset logging context
func (c *Context) Copy() types.Context {
func (c *Context) Clone() types.Context {
return c.WithLoggingContext("")
}

View File

@@ -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() {

View File

@@ -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")
}

View File

@@ -25,6 +25,7 @@ import (
log "github.com/ipfs/go-log/v2"
"github.com/kyokomi/emoji"
"github.com/mudler/luet/pkg/api/core/types"
"github.com/pterm/pterm"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@@ -114,18 +115,19 @@ func New(opts ...LoggerOptions) (*Logger, error) {
return l, nil
}
func (l *Logger) Copy(opts ...LoggerOptions) (*Logger, error) {
// Copy returns a copy of the logger
func (l *Logger) Copy() (types.Logger, error) {
c := *l
copy := &c
for _, o := range opts {
if err := o(copy); err != nil {
return nil, err
}
}
return copy, nil
}
// SetContext sets the logger context, used to prefix log lines
func (l *Logger) SetContext(name string) {
l.context = name
}
func joinMsg(args ...interface{}) (message string) {
for _, m := range args {
message += " " + fmt.Sprintf("%v", m)

View File

@@ -21,7 +21,6 @@ import (
"os"
"github.com/gookit/color"
"github.com/mudler/luet/pkg/api/core/logger"
. "github.com/mudler/luet/pkg/api/core/logger"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -84,11 +83,12 @@ var _ = Describe("Context and logging", func() {
It("returns copies with logged context", func() {
l, err := New(WithLevel("debug"))
l, _ = l.Copy(logger.WithContext("bazzz"))
l2, _ := l.Copy()
l2.SetContext("bazzz")
Expect(err).ToNot(HaveOccurred())
Expect(captureStdout(func(w io.Writer) {
l.Debug("bar")
l2.Debug("bar")
})).To(ContainSubstring("(bazzz) bar"))
})

View File

@@ -6,9 +6,10 @@ import (
"sort"
"unicode"
"github.com/mudler/luet/pkg/helpers"
"github.com/mudler/topsort"
"github.com/philopon/go-toposort"
"github.com/pkg/errors"
toposort "github.com/scrohde/go-toposort"
)
// PackageAssert represent a package assertion.
@@ -30,51 +31,48 @@ func (a *PackageAssert) String() string {
return fmt.Sprintf("%s/%s %s %s", a.Package.GetCategory(), a.Package.GetName(), a.Package.GetVersion(), msg)
}
func (assertions PackagesAssertions) EnsureOrder() PackagesAssertions {
func (assertions PackagesAssertions) EnsureOrder(definitiondb PackageDatabase) (PackagesAssertions, error) {
allAssertions := assertions
orderedAssertions := PackagesAssertions{}
unorderedAssertions := PackagesAssertions{}
fingerprints := []string{}
tmpMap := map[string]PackageAssert{}
for _, a := range assertions {
tmpMap[a.Package.GetFingerPrint()] = a
fingerprints = append(fingerprints, a.Package.GetFingerPrint())
unorderedAssertions = append(unorderedAssertions, a) // Build a list of the ones that must be ordered
graph := toposort.NewGraph(len(allAssertions))
if a.Value {
unorderedAssertions = append(unorderedAssertions, a) // Build a list of the ones that must be ordered
} else {
orderedAssertions = append(orderedAssertions, a) // Keep last the ones which are not meant to be installed
}
for _, a := range assertions {
tmpMap[a.Package.GetPackageName()] = a
graph.AddNode(a.Package.GetPackageName())
}
sort.Sort(unorderedAssertions)
edges := map[string]string{}
// Build a topological graph
graph := toposort.NewGraph(len(unorderedAssertions))
graph.AddNodes(fingerprints...)
for _, a := range unorderedAssertions {
for _, a := range allAssertions {
for _, req := range a.Package.GetRequires() {
graph.AddEdge(a.Package.GetFingerPrint(), req.GetFingerPrint())
if def, err := definitiondb.FindPackage(req); err == nil { // Provides: Get a chance of being override here
req = def
}
edges[a.Package.GetPackageName()] = req.GetPackageName()
graph.AddNode(req.GetPackageName())
}
}
result, ok := graph.Toposort()
for k, v := range edges {
graph.AddEdge(k, v)
}
result, ok := graph.ToposortStable()
if !ok {
panic("Cycle found")
return nil, fmt.Errorf("cycle found")
}
for _, res := range result {
a, ok := tmpMap[res]
if !ok {
panic("fail")
// continue
if ok {
orderedAssertions = append(orderedAssertions, a)
}
orderedAssertions = append(orderedAssertions, a)
// orderedAssertions = append(PackagesAssertions{a}, orderedAssertions...) // push upfront
}
//helpers.ReverseAny(orderedAssertions)
return orderedAssertions
helpers.ReverseAny(orderedAssertions)
return orderedAssertions, nil
}
// SearchByName searches a string matching a package in the assetion list

View File

@@ -0,0 +1,86 @@
// Copyright © 2021 Ettore Di Giacinto <mudler@mocaccino.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 types_test
import (
types "github.com/mudler/luet/pkg/api/core/types"
"github.com/mudler/luet/pkg/database"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Assertions", func() {
Context("Ordering", func() {
It("orders them correctly", func() {
foo := &types.Package{Name: "foo", PackageRequires: []*types.Package{{Name: "bar"}}}
assertions := types.PackagesAssertions{
{Package: foo},
{Package: &types.Package{Name: "baz", PackageRequires: []*types.Package{{Name: "bar"}}}},
{Package: &types.Package{Name: "bar", PackageRequires: []*types.Package{{}}}},
}
ordered_old, err := assertions.Order(database.NewInMemoryDatabase(false), foo.GetFingerPrint())
Expect(err).ShouldNot(HaveOccurred())
Expect(ordered_old[0].Package.Name).To(Equal("bar"))
ordered, err := assertions.EnsureOrder(database.NewInMemoryDatabase(false))
Expect(err).ShouldNot(HaveOccurred())
Expect(len(ordered)).To(Equal(3))
Expect(ordered[0].Package.Name).To(Equal("bar"))
})
It("errors on cycles", func() {
foo := &types.Package{Name: "foo", PackageRequires: []*types.Package{{Name: "bar"}}}
assertions := types.PackagesAssertions{
{Package: foo},
{Package: &types.Package{Name: "baz", PackageRequires: []*types.Package{{Name: "bar"}}}},
{Package: &types.Package{Name: "bar", PackageRequires: []*types.Package{{Name: "baz"}}}},
}
_, err := assertions.Order(database.NewInMemoryDatabase(false), foo.GetFingerPrint())
Expect(err).Should(HaveOccurred())
_, err = assertions.EnsureOrder(database.NewInMemoryDatabase(false))
Expect(err).Should(HaveOccurred())
})
It("orders them correctly", func() {
foo := &types.Package{Name: "foo", PackageRequires: []*types.Package{{Name: "bar"}}}
assertions := types.PackagesAssertions{
{Package: foo},
{Package: &types.Package{Name: "baz2", PackageRequires: []*types.Package{{Name: "foobaz"}}}},
{Package: &types.Package{Name: "baz", PackageRequires: []*types.Package{{Name: "bar"}}}},
{Package: &types.Package{Name: "bar", PackageRequires: []*types.Package{{}}}},
{Package: &types.Package{Name: "foobaz", PackageRequires: []*types.Package{{}}}},
}
ordered_old, err := assertions.Order(database.NewInMemoryDatabase(false), foo.GetFingerPrint())
Expect(err).ShouldNot(HaveOccurred())
Expect(ordered_old[0].Package.Name).To(Equal("bar"))
Expect(ordered_old[1].Package.Name).ToNot(Equal("foobaz"))
ordered, err := assertions.EnsureOrder(database.NewInMemoryDatabase(false))
Expect(err).ShouldNot(HaveOccurred())
Expect(len(ordered)).To(Equal(5))
Expect(ordered[0].Package.Name).To(Equal("bar"))
Expect(ordered[1].Package.Name).To(Equal("foobaz"))
})
})
})

View File

@@ -19,7 +19,7 @@ type Context interface {
Logger
GarbageCollector
GetConfig() LuetConfig
Copy() Context
Clone() Context
// SetAnnotation sets generic annotations to hold in a context
SetAnnotation(s string, i interface{})

View File

@@ -33,7 +33,8 @@ type Logger interface {
Fatalf(string, ...interface{})
Panicf(string, ...interface{})
Tracef(string, ...interface{})
Copy() (Logger, error)
SetContext(string)
SpinnerStop()
Spinner()
Ask() bool

View File

@@ -146,9 +146,12 @@ func PackageFromYaml(yml []byte) (Package, error) {
type rawPackages []map[string]interface{}
func (r rawPackages) Find(name, category, version string) map[string]interface{} {
func (r rawPackages) Find(wanted Package) map[string]interface{} {
for _, v := range r {
if v["name"] == name && v["category"] == category && v["version"] == version {
p := &Package{}
dat, _ := json.Marshal(v)
json.Unmarshal(dat, p)
if wanted.Matches(p) {
return v
}
}
@@ -288,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 {

View File

@@ -1461,7 +1461,7 @@ func (cs *LuetCompiler) templatePackage(vals []map[string]interface{}, pack *typ
return nil, errors.Wrap(err, "getting raw packages")
}
raw := packsRaw.Find(pack.GetName(), pack.GetCategory(), pack.GetVersion())
raw := packsRaw.Find(*pack)
td := templatedata{}
if len(vals) > 0 {
for _, bv := range vals {

View File

@@ -22,6 +22,8 @@ import (
"github.com/ghodss/yaml"
"github.com/mudler/luet/pkg/api/core/types"
box "github.com/mudler/luet/pkg/box"
fileHelper "github.com/mudler/luet/pkg/helpers/file"
"github.com/mudler/luet/pkg/tree"
"github.com/pkg/errors"
)
@@ -90,3 +92,47 @@ func NewLuetFinalizerFromYaml(data []byte) (*LuetFinalizer, error) {
}
return &p, err
}
func OrderFinalizers(allRepos types.PackageDatabase, toInstall map[string]ArtifactMatch, solution types.PackagesAssertions) ([]*types.Package, error) {
var toFinalize []*types.Package
if len(toInstall) == 1 {
for _, w := range toInstall {
if fileHelper.Exists(w.Package.Rel(tree.FinalizerFile)) {
// Finalizers needs to run in order and in sequence.
ordered, err := solution.Order(allRepos, w.Package.GetFingerPrint())
if err != nil {
return toFinalize, errors.Wrap(err, "While order a solution for "+w.Package.HumanReadableString())
}
ORDER:
for _, ass := range ordered {
if ass.Value {
installed, ok := toInstall[ass.Package.GetFingerPrint()]
if !ok {
// 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)
if err != nil {
return toFinalize, errors.Wrap(err, "Error getting package "+ass.Package.HumanReadableString())
}
toFinalize = append(toFinalize, treePackage)
}
}
}
}
} else {
assertions, err := solution.EnsureOrder(allRepos)
if err != nil {
return toFinalize, err
}
for _, o := range assertions {
if o.Value {
toFinalize = append(toFinalize, o.Package)
}
}
}
return toFinalize, nil
}

View File

@@ -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)
@@ -622,7 +638,7 @@ func (l *LuetInstaller) download(syncedRepos Repositories, toDownload map[string
var wg = new(sync.WaitGroup)
ctx := l.Options.Context.Copy()
ctx := l.Options.Context.Clone()
// Check if the terminal is big enough to display a progress bar
// https://github.com/pterm/pterm/blob/4c725e56bfd9eb38e1c7b9dec187b50b93baa8bd/progressbar_printer.go#L190
@@ -800,33 +816,7 @@ func (l *LuetInstaller) computeInstall(o Option, syncedRepos Repositories, cp ty
func (l *LuetInstaller) getFinalizers(allRepos types.PackageDatabase, solution types.PackagesAssertions, toInstall map[string]ArtifactMatch, nodeps bool) ([]*types.Package, error) {
var toFinalize []*types.Package
if !nodeps {
// TODO: Lower those errors as l.Options.Context.Warning
for _, w := range toInstall {
if !fileHelper.Exists(w.Package.Rel(tree.FinalizerFile)) {
continue
}
// Finalizers needs to run in order and in sequence.
ordered, err := solution.Order(allRepos, w.Package.GetFingerPrint())
if err != nil {
return toFinalize, errors.Wrap(err, "While order a solution for "+w.Package.HumanReadableString())
}
ORDER:
for _, ass := range ordered {
if ass.Value {
installed, ok := toInstall[ass.Package.GetFingerPrint()]
if !ok {
// 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)
if err != nil {
return toFinalize, errors.Wrap(err, "Error getting package "+ass.Package.HumanReadableString())
}
toFinalize = append(toFinalize, treePackage)
}
}
}
return OrderFinalizers(allRepos, toInstall, solution)
} else {
for _, c := range toInstall {
if !fileHelper.Exists(c.Package.Rel(tree.FinalizerFile)) {

View File

@@ -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())

View File

@@ -457,6 +457,14 @@ func (r *LuetSystemRepository) SetVerify(p bool) {
r.LuetRepository.Verify = p
}
func (r *LuetSystemRepository) GetReferenceID() string {
return r.LuetRepository.ReferenceID
}
func (r *LuetSystemRepository) SetReferenceID(ref string) {
r.LuetRepository.ReferenceID = ref
}
func (r *LuetSystemRepository) GetBackend() compiler.CompilerBackend {
return r.Backend
}
@@ -1051,6 +1059,7 @@ func (r *LuetSystemRepository) fill(r2 *LuetSystemRepository) {
r2.SetPriority(r.GetPriority())
r2.SetName(r.GetName())
r2.SetVerify(r.GetVerify())
r2.SetReferenceID(r.GetReferenceID())
}
func (r *LuetSystemRepository) Serialize() (*LuetSystemRepositoryMetadata, LuetSystemRepository) {

View File

@@ -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 {

View File

@@ -54,7 +54,7 @@ func BuildCollectionParser(srcDir, currentpath, name string, templates []string,
compileDefPath := pack.Rel(CompilerDefinitionFile)
if fileHelper.Exists(compileDefPath) {
raw := packsRaw.Find(pack.GetName(), pack.GetCategory(), pack.GetVersion())
raw := packsRaw.Find(pack)
buildyaml, err := ioutil.ReadFile(compileDefPath)
if err != nil {
return errors.Wrap(err, "Error reading file "+currentpath)
@@ -113,7 +113,7 @@ func RuntimeCollectionParser(srcDir, currentpath, name string, templates []strin
compileDefPath := p.Rel(CompilerDefinitionFile)
if fileHelper.Exists(compileDefPath) {
raw := packsRaw.Find(p.GetName(), p.GetCategory(), p.GetVersion())
raw := packsRaw.Find(p)
buildyaml, err := ioutil.ReadFile(compileDefPath)
if err != nil {
return errors.Wrap(err, "Error reading file "+currentpath)

View File

@@ -0,0 +1,75 @@
// 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/>.
// Recipe is a builder imeplementation.
// It reads a Tree and spit it in human readable form (YAML), called recipe,
// It also loads a tree (recipe) from a YAML (to a db, e.g. BoltDB), allowing to query it
// with the solver, using the package object.
package tree_test
import (
"io/ioutil"
"os"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/mudler/luet/pkg/api/core/types"
pkg "github.com/mudler/luet/pkg/database"
. "github.com/mudler/luet/pkg/tree"
)
var _ = Describe("Templated tree", func() {
Context("Resolves correctly dependencies", func() {
It("interpolates correctly templated requires", func() {
db := pkg.NewInMemoryDatabase(false)
generalRecipe := NewCompilerRecipe(db)
tmpdir, err := ioutil.TempDir("", "package")
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(tmpdir) // clean up
err = generalRecipe.Load("../../tests/fixtures/template_requires")
Expect(err).ToNot(HaveOccurred())
Expect(len(generalRecipe.GetDatabase().World())).To(Equal(7))
foo, err := generalRecipe.GetDatabase().FindPackage(&types.Package{Name: "foo"})
Expect(err).ToNot(HaveOccurred())
Expect(len(foo.GetRequires())).To(Equal(1))
Expect(foo.GetRequires()[0].Name).To(Equal("bar"))
baz, err := generalRecipe.GetDatabase().FindPackage(&types.Package{Name: "baz"})
Expect(err).ToNot(HaveOccurred())
Expect(len(baz.GetRequires())).To(Equal(1))
Expect(baz.GetRequires()[0].Name).To(Equal("foobar"))
bazbaz, err := generalRecipe.GetDatabase().FindPackage(&types.Package{Name: "bazbaz"})
Expect(err).ToNot(HaveOccurred())
Expect(len(bazbaz.GetRequires())).To(Equal(1))
Expect(bazbaz.GetRequires()[0].Name).To(Equal("foobar"))
foo, err = generalRecipe.GetDatabase().FindPackage(&types.Package{Name: "foo", Category: "test"})
Expect(err).ToNot(HaveOccurred())
Expect(len(foo.GetRequires())).To(Equal(1))
Expect(foo.GetRequires()[0].Name).To(Equal("bar"))
baz, err = generalRecipe.GetDatabase().FindPackage(&types.Package{Name: "baz", Category: "test"})
Expect(err).ToNot(HaveOccurred())
Expect(len(baz.GetRequires())).To(Equal(1))
Expect(baz.GetRequires()[0].Name).To(Equal("foobar"))
})
})
})

View File

@@ -0,0 +1,7 @@
requires:
{{ if eq .Values.value "bar" }}
- name: "bar"
{{ end }}
{{ if eq .Values.value "foobar" }}
- name: "foobar"
{{ end }}

View File

@@ -0,0 +1,5 @@
packages:
- name: "foo"
value: "bar"
- name: "baz"
value: "foobar"

View File

@@ -0,0 +1,7 @@
requires:
{{ if eq .Values.value "bar" }}
- name: "bar"
{{ end }}
{{ if eq .Values.value "foobar" }}
- name: "foobar"
{{ end }}

View File

@@ -0,0 +1,7 @@
packages:
- name: "foo"
category: "test"
value: "bar"
- name: "baz"
value: "foobar"
category: "test"

View File

@@ -0,0 +1,7 @@
requires:
{{ if eq .Values.value "bar" }}
- name: "bar"
{{ end }}
{{ if eq .Values.value "foobar" }}
- name: "foobar"
{{ end }}

View File

@@ -0,0 +1,2 @@
name: "bazbaz"
value: "foobar"

View File

@@ -0,0 +1 @@
image: alpine

View File

@@ -0,0 +1,3 @@
packages:
- name: "foobar"
- name: "bar"

View File

@@ -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() {

View File

@@ -1,5 +1,7 @@
package toposort
import "sort"
type Graph struct {
nodes []string
outputs map[string]map[string]int
@@ -59,7 +61,7 @@ func (g *Graph) RemoveEdge(from, to string) bool {
return true
}
func (g *Graph) Toposort() ([]string, bool) {
func (g *Graph) toposort(stable bool) ([]string, bool) {
L := make([]string, 0, len(g.nodes))
S := make([]string, 0, len(g.nodes))
@@ -69,6 +71,10 @@ func (g *Graph) Toposort() ([]string, bool) {
}
}
if stable {
sort.Strings(S)
}
for len(S) > 0 {
var n string
n, S = S[0], S[1:]
@@ -79,6 +85,10 @@ func (g *Graph) Toposort() ([]string, bool) {
ms[i-1] = m
}
if stable {
sort.Strings(ms)
}
for _, m := range ms {
g.unsafeRemoveEdge(n, m)
@@ -99,3 +109,11 @@ func (g *Graph) Toposort() ([]string, bool) {
return L, true
}
func (g *Graph) Toposort() ([]string, bool) {
return g.toposort(false)
}
func (g *Graph) ToposortStable() ([]string, bool) {
return g.toposort(true)
}

4
vendor/modules.txt vendored
View File

@@ -370,7 +370,6 @@ github.com/pelletier/go-toml
github.com/peterbourgon/diskv
# github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f
## explicit
github.com/philopon/go-toposort
# github.com/pkg/errors v0.9.1
## explicit
github.com/pkg/errors
@@ -402,6 +401,9 @@ github.com/rogpeppe/go-internal/internal/syscall/windows
github.com/rogpeppe/go-internal/internal/syscall/windows/sysdll
github.com/rogpeppe/go-internal/lockedfile
github.com/rogpeppe/go-internal/lockedfile/internal/filelock
# github.com/scrohde/go-toposort v0.0.0-20170629203416-a9a902e065a3
## explicit
github.com/scrohde/go-toposort
# github.com/shopspring/decimal v1.2.0
github.com/shopspring/decimal
# github.com/sirupsen/logrus v1.8.1