Compare commits

..

5 Commits

Author SHA1 Message Date
Ettore Di Giacinto
288ac086ec 🎨 Cleanups 2022-06-06 12:38:43 +02:00
mudler
45a28112ef 🎨 Add test-integration-docker target 2022-06-05 23:47:23 +02:00
mudler
2765f817be 🎨 Refactor, use ensureoder where necessary 2022-06-05 23:47:08 +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
17 changed files with 210 additions and 73 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

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

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

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

@@ -638,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
@@ -816,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

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