mirror of
https://github.com/mudler/luet.git
synced 2025-09-16 15:19:24 +00:00
🎨 Introduce contextualized logging
This commit is multi-fold as it also refactors internally context and logger as interfaces so it is easier to plug luet as a library externally. Introduces a garbage collector (related to #227) but doesn't handle yet parallelism. Closes #265
This commit is contained in:
@@ -90,7 +90,7 @@ type Bus struct {
|
||||
*pluggable.Manager
|
||||
}
|
||||
|
||||
func (b *Bus) Initialize(ctx *types.Context, plugin ...string) {
|
||||
func (b *Bus) Initialize(ctx types.Context, plugin ...string) {
|
||||
b.Manager.Load(plugin...).Register()
|
||||
|
||||
for _, e := range b.Manager.Events {
|
||||
|
@@ -18,7 +18,7 @@ package config_test
|
||||
|
||||
import (
|
||||
config "github.com/mudler/luet/pkg/api/core/config"
|
||||
"github.com/mudler/luet/pkg/api/core/types"
|
||||
"github.com/mudler/luet/pkg/api/core/context"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -29,7 +29,7 @@ var _ = Describe("Config", func() {
|
||||
Context("Test config protect", func() {
|
||||
|
||||
It("Protect1", func() {
|
||||
ctx := types.NewContext()
|
||||
ctx := context.NewContext()
|
||||
files := []string{
|
||||
"etc/foo/my.conf",
|
||||
"usr/bin/foo",
|
||||
@@ -59,7 +59,7 @@ var _ = Describe("Config", func() {
|
||||
})
|
||||
|
||||
It("Protect2", func() {
|
||||
ctx := types.NewContext()
|
||||
ctx := context.NewContext()
|
||||
|
||||
files := []string{
|
||||
"etc/foo/my.conf",
|
||||
@@ -86,7 +86,7 @@ var _ = Describe("Config", func() {
|
||||
})
|
||||
|
||||
It("Protect3: Annotation dir without initial slash", func() {
|
||||
ctx := types.NewContext()
|
||||
ctx := context.NewContext()
|
||||
|
||||
files := []string{
|
||||
"etc/foo/my.conf",
|
||||
|
28
pkg/api/core/context/contest_suite_test.go
Normal file
28
pkg/api/core/context/contest_suite_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// 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 context_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestContext(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Context Suite")
|
||||
}
|
159
pkg/api/core/context/context.go
Normal file
159
pkg/api/core/context/context.go
Normal file
@@ -0,0 +1,159 @@
|
||||
// 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 context
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
fileHelper "github.com/mudler/luet/pkg/helpers/file"
|
||||
|
||||
gc "github.com/mudler/luet/pkg/api/core/garbagecollector"
|
||||
"github.com/mudler/luet/pkg/api/core/logger"
|
||||
"github.com/mudler/luet/pkg/api/core/types"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
*logger.Logger
|
||||
context.Context
|
||||
types.GarbageCollector
|
||||
Config *types.LuetConfig
|
||||
NoSpinner bool
|
||||
annotations map[string]interface{}
|
||||
}
|
||||
|
||||
// SetAnnotation sets generic annotations to hold in a context
|
||||
func (c *Context) SetAnnotation(s string, i interface{}) {
|
||||
c.annotations[s] = i
|
||||
}
|
||||
|
||||
// GetAnnotation gets generic annotations to hold in a context
|
||||
func (c *Context) GetAnnotation(s string) interface{} {
|
||||
return c.annotations[s]
|
||||
}
|
||||
|
||||
type ContextOption func(c *Context) error
|
||||
|
||||
// WithLogger sets the logger
|
||||
func WithLogger(l *logger.Logger) ContextOption {
|
||||
return func(c *Context) error {
|
||||
c.Logger = l
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithConfig sets the luet config
|
||||
func WithConfig(cc *types.LuetConfig) ContextOption {
|
||||
return func(c *Context) error {
|
||||
c.Config = cc
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: GC needs to be instantiated when a new context is created from system TmpDirBase
|
||||
|
||||
// WithGarbageCollector sets the Garbage collector for the given context
|
||||
func WithGarbageCollector(l types.GarbageCollector) ContextOption {
|
||||
return func(c *Context) error {
|
||||
if !filepath.IsAbs(l.String()) {
|
||||
abs, err := fileHelper.Rel2Abs(l.String())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while converting relative path to absolute path")
|
||||
}
|
||||
l = gc.GarbageCollector(abs)
|
||||
}
|
||||
|
||||
c.GarbageCollector = l
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewContext returns a new context.
|
||||
// It accepts a Garbage collector, a config and a logger as an option
|
||||
func NewContext(opts ...ContextOption) *Context {
|
||||
l, _ := logger.New()
|
||||
d := &Context{
|
||||
annotations: make(map[string]interface{}),
|
||||
Logger: l,
|
||||
GarbageCollector: gc.GarbageCollector(filepath.Join(os.TempDir(), "tmpluet")),
|
||||
Config: &types.LuetConfig{
|
||||
ConfigFromHost: true,
|
||||
Logging: types.LuetLoggingConfig{},
|
||||
General: types.LuetGeneralConfig{},
|
||||
System: types.LuetSystemConfig{
|
||||
DatabasePath: filepath.Join("var", "db"),
|
||||
PkgsCachePath: filepath.Join("var", "db", "packages"),
|
||||
},
|
||||
Solver: types.LuetSolverOptions{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(d)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// WithLoggingContext returns a copy of the context with a contextualized logger
|
||||
func (c *Context) WithLoggingContext(name string) types.Context {
|
||||
configCopy := *c.Config
|
||||
configCopy.System = c.Config.System
|
||||
configCopy.General = c.Config.General
|
||||
configCopy.Logging = c.Config.Logging
|
||||
|
||||
ctx := *c
|
||||
ctxCopy := &ctx
|
||||
ctxCopy.Config = &configCopy
|
||||
ctxCopy.annotations = ctx.annotations
|
||||
|
||||
ctxCopy.Logger, _ = c.Logger.Copy(logger.WithContext(name))
|
||||
|
||||
return ctxCopy
|
||||
}
|
||||
|
||||
// Copy returns a context copy with a reset logging context
|
||||
func (c *Context) Copy() types.Context {
|
||||
return c.WithLoggingContext("")
|
||||
}
|
||||
|
||||
func (c *Context) Warning(mess ...interface{}) {
|
||||
c.Logger.Warn(mess...)
|
||||
if c.Config.General.FatalWarns {
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) Warn(mess ...interface{}) {
|
||||
c.Warning(mess...)
|
||||
}
|
||||
|
||||
func (c *Context) Warnf(t string, mess ...interface{}) {
|
||||
c.Logger.Warnf(t, mess...)
|
||||
if c.Config.General.FatalWarns {
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) Warningf(t string, mess ...interface{}) {
|
||||
c.Warnf(t, mess...)
|
||||
}
|
||||
|
||||
func (c *Context) GetConfig() types.LuetConfig {
|
||||
return *c.Config
|
||||
}
|
59
pkg/api/core/garbagecollector/garbagecollector.go
Normal file
59
pkg/api/core/garbagecollector/garbagecollector.go
Normal file
@@ -0,0 +1,59 @@
|
||||
// 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 gc
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
type GarbageCollector string
|
||||
|
||||
func (c GarbageCollector) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
func (c GarbageCollector) init() error {
|
||||
if _, err := os.Stat(string(c)); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(string(c), os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c GarbageCollector) Clean() error {
|
||||
return os.RemoveAll(string(c))
|
||||
}
|
||||
|
||||
func (c GarbageCollector) TempDir(pattern string) (string, error) {
|
||||
err := c.init()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ioutil.TempDir(string(c), pattern)
|
||||
}
|
||||
|
||||
func (c GarbageCollector) TempFile(s string) (*os.File, error) {
|
||||
err := c.init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.TempFile(string(c), s)
|
||||
}
|
@@ -18,8 +18,8 @@ package image_test
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/mudler/luet/pkg/api/core/context"
|
||||
. "github.com/mudler/luet/pkg/api/core/image"
|
||||
"github.com/mudler/luet/pkg/api/core/types"
|
||||
"github.com/mudler/luet/pkg/helpers/file"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -27,16 +27,16 @@ import (
|
||||
|
||||
var _ = Describe("Cache", func() {
|
||||
|
||||
ctx := types.NewContext()
|
||||
ctx := context.NewContext()
|
||||
Context("used as k/v store", func() {
|
||||
|
||||
cache := &Cache{}
|
||||
var dir string
|
||||
|
||||
BeforeEach(func() {
|
||||
ctx = types.NewContext()
|
||||
ctx = context.NewContext()
|
||||
var err error
|
||||
dir, err = ctx.Config.System.TempDir("foo")
|
||||
dir, err = ctx.TempDir("foo")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
cache = NewCache(dir, 10*1024*1024, 1) // 10MB Cache when upgrading to files. Max volatile memory of 1 row.
|
||||
})
|
||||
|
@@ -20,8 +20,8 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/mudler/luet/pkg/api/core/context"
|
||||
. "github.com/mudler/luet/pkg/api/core/image"
|
||||
"github.com/mudler/luet/pkg/api/core/types"
|
||||
"github.com/mudler/luet/pkg/api/core/types/artifact"
|
||||
"github.com/mudler/luet/pkg/compiler/backend"
|
||||
"github.com/mudler/luet/pkg/helpers/file"
|
||||
@@ -32,12 +32,12 @@ import (
|
||||
var _ = Describe("Create", func() {
|
||||
Context("Creates an OCI image from a standard tar", func() {
|
||||
It("creates an image which is loadable", func() {
|
||||
ctx := types.NewContext()
|
||||
ctx := context.NewContext()
|
||||
|
||||
dst, err := ctx.Config.System.TempFile("dst")
|
||||
dst, err := ctx.TempFile("dst")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(dst.Name())
|
||||
srcTar, err := ctx.Config.System.TempFile("srcTar")
|
||||
srcTar, err := ctx.TempFile("srcTar")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(srcTar.Name())
|
||||
|
||||
|
@@ -23,8 +23,8 @@ import (
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
daemon "github.com/google/go-containerregistry/pkg/v1/daemon"
|
||||
"github.com/mudler/luet/pkg/api/core/context"
|
||||
. "github.com/mudler/luet/pkg/api/core/image"
|
||||
"github.com/mudler/luet/pkg/api/core/types"
|
||||
"github.com/mudler/luet/pkg/helpers/file"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -47,7 +47,7 @@ var _ = Describe("Delta", func() {
|
||||
})
|
||||
|
||||
Context("ExtractDeltaFiles", func() {
|
||||
ctx := types.NewContext()
|
||||
ctx := context.NewContext()
|
||||
var tmpfile *os.File
|
||||
var ref, ref2 name.Reference
|
||||
var img, img2 v1.Image
|
||||
@@ -59,7 +59,7 @@ var _ = Describe("Delta", func() {
|
||||
img2, _ = daemon.Image(ref2)
|
||||
|
||||
BeforeEach(func() {
|
||||
ctx = types.NewContext()
|
||||
ctx = context.NewContext()
|
||||
|
||||
tmpfile, err = ioutil.TempFile("", "delta")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
@@ -35,7 +35,7 @@ import (
|
||||
// considering the added files only, and applies a filter on them based on the regexes
|
||||
// in the lists.
|
||||
func ExtractDeltaAdditionsFiles(
|
||||
ctx *types.Context,
|
||||
ctx types.Context,
|
||||
srcimg v1.Image,
|
||||
includes []string, excludes []string,
|
||||
) (func(h *tar.Header) (bool, error), error) {
|
||||
@@ -43,7 +43,7 @@ func ExtractDeltaAdditionsFiles(
|
||||
includeRegexp := compileRegexes(includes)
|
||||
excludeRegexp := compileRegexes(excludes)
|
||||
|
||||
srcfilesd, err := ctx.Config.System.TempDir("srcfiles")
|
||||
srcfilesd, err := ctx.TempDir("srcfiles")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -128,7 +128,7 @@ func ExtractDeltaAdditionsFiles(
|
||||
// It then filters files by an include and exclude list.
|
||||
// The list can be regexes
|
||||
func ExtractFiles(
|
||||
ctx *types.Context,
|
||||
ctx types.Context,
|
||||
prefixPath string,
|
||||
includes []string, excludes []string,
|
||||
) func(h *tar.Header) (bool, error) {
|
||||
@@ -193,7 +193,7 @@ func ExtractFiles(
|
||||
// ExtractReader perform the extracting action over the io.ReadCloser
|
||||
// it extracts the files over output. Accepts a filter as an option
|
||||
// and additional containerd Options
|
||||
func ExtractReader(ctx *types.Context, reader io.ReadCloser, output string, filter func(h *tar.Header) (bool, error), opts ...containerdarchive.ApplyOpt) (int64, string, error) {
|
||||
func ExtractReader(ctx types.Context, reader io.ReadCloser, output string, filter func(h *tar.Header) (bool, error), opts ...containerdarchive.ApplyOpt) (int64, string, error) {
|
||||
defer reader.Close()
|
||||
|
||||
// If no filter is specified, grab all.
|
||||
@@ -213,8 +213,8 @@ func ExtractReader(ctx *types.Context, reader io.ReadCloser, output string, filt
|
||||
}
|
||||
|
||||
// Extract is just syntax sugar around ExtractReader. It extracts an image into a dir
|
||||
func Extract(ctx *types.Context, img v1.Image, filter func(h *tar.Header) (bool, error), opts ...containerdarchive.ApplyOpt) (int64, string, error) {
|
||||
tmpdiffs, err := ctx.Config.GetSystem().TempDir("extraction")
|
||||
func Extract(ctx types.Context, img v1.Image, filter func(h *tar.Header) (bool, error), opts ...containerdarchive.ApplyOpt) (int64, string, error) {
|
||||
tmpdiffs, err := ctx.TempDir("extraction")
|
||||
if err != nil {
|
||||
return 0, "", errors.Wrap(err, "Error met while creating tempdir for rootfs")
|
||||
}
|
||||
@@ -222,6 +222,6 @@ func Extract(ctx *types.Context, img v1.Image, filter func(h *tar.Header) (bool,
|
||||
}
|
||||
|
||||
// ExtractTo is just syntax sugar around ExtractReader
|
||||
func ExtractTo(ctx *types.Context, img v1.Image, output string, filter func(h *tar.Header) (bool, error), opts ...containerdarchive.ApplyOpt) (int64, string, error) {
|
||||
func ExtractTo(ctx types.Context, img v1.Image, output string, filter func(h *tar.Header) (bool, error), opts ...containerdarchive.ApplyOpt) (int64, string, error) {
|
||||
return ExtractReader(ctx, mutate.Extract(img), output, filter, opts...)
|
||||
}
|
||||
|
@@ -23,8 +23,8 @@ import (
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
daemon "github.com/google/go-containerregistry/pkg/v1/daemon"
|
||||
"github.com/mudler/luet/pkg/api/core/context"
|
||||
. "github.com/mudler/luet/pkg/api/core/image"
|
||||
"github.com/mudler/luet/pkg/api/core/types"
|
||||
"github.com/mudler/luet/pkg/helpers/file"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -34,14 +34,14 @@ var _ = Describe("Extract", func() {
|
||||
|
||||
Context("extract files from images", func() {
|
||||
Context("ExtractFiles", func() {
|
||||
ctx := types.NewContext()
|
||||
ctx := context.NewContext()
|
||||
var tmpfile *os.File
|
||||
var ref name.Reference
|
||||
var img v1.Image
|
||||
var err error
|
||||
|
||||
BeforeEach(func() {
|
||||
ctx = types.NewContext()
|
||||
ctx = context.NewContext()
|
||||
|
||||
tmpfile, err = ioutil.TempFile("", "extract")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
@@ -18,7 +18,7 @@ package image_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mudler/luet/pkg/api/core/types"
|
||||
"github.com/mudler/luet/pkg/api/core/context"
|
||||
"github.com/mudler/luet/pkg/compiler/backend"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
|
||||
func TestMutator(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
b := backend.NewSimpleDockerBackend(types.NewContext())
|
||||
b := backend.NewSimpleDockerBackend(context.NewContext())
|
||||
b.DownloadImage(backend.Options{ImageName: "alpine"})
|
||||
b.DownloadImage(backend.Options{ImageName: "golang:alpine"})
|
||||
|
||||
|
28
pkg/api/core/logger/config_suite_test.go
Normal file
28
pkg/api/core/logger/config_suite_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// 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 logger_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestAPITypes(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Types Suite")
|
||||
}
|
367
pkg/api/core/logger/logger.go
Normal file
367
pkg/api/core/logger/logger.go
Normal file
@@ -0,0 +1,367 @@
|
||||
// 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 logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
log "github.com/ipfs/go-log/v2"
|
||||
"github.com/kyokomi/emoji"
|
||||
"github.com/pterm/pterm"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// Logger is the default logger
|
||||
type Logger struct {
|
||||
level log.LogLevel
|
||||
emoji bool
|
||||
logToFile bool
|
||||
noSpinner bool
|
||||
fileLogger *zap.Logger
|
||||
context string
|
||||
spinnerLock sync.Mutex
|
||||
s *pterm.SpinnerPrinter
|
||||
}
|
||||
|
||||
// LogLevel represents a log severity level. Use the package variables as an
|
||||
// enum.
|
||||
type LogLevel zapcore.Level
|
||||
|
||||
type LoggerOptions func(*Logger) error
|
||||
|
||||
var NoSpinner LoggerOptions = func(l *Logger) error {
|
||||
l.noSpinner = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func WithLevel(level string) LoggerOptions {
|
||||
return func(l *Logger) error {
|
||||
lvl, _ := log.LevelFromString(level) // Defaults to Info
|
||||
l.level = lvl
|
||||
if l.level == log.LevelDebug {
|
||||
pterm.EnableDebugMessages()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithContext(c string) LoggerOptions {
|
||||
return func(l *Logger) error {
|
||||
l.context = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithFileLogging(p, encoding string) LoggerOptions {
|
||||
return func(l *Logger) error {
|
||||
if encoding == "" {
|
||||
encoding = "console"
|
||||
}
|
||||
l.logToFile = true
|
||||
var err error
|
||||
cfg := zap.NewProductionConfig()
|
||||
cfg.OutputPaths = []string{p}
|
||||
cfg.Level = zap.NewAtomicLevelAt(zapcore.Level(l.level))
|
||||
cfg.ErrorOutputPaths = []string{}
|
||||
cfg.Encoding = encoding
|
||||
cfg.DisableCaller = true
|
||||
cfg.DisableStacktrace = true
|
||||
cfg.EncoderConfig.TimeKey = "time"
|
||||
cfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
|
||||
l.fileLogger, err = cfg.Build()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var EnableEmoji = func() LoggerOptions {
|
||||
return func(l *Logger) error {
|
||||
l.emoji = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func New(opts ...LoggerOptions) (*Logger, error) {
|
||||
l := &Logger{
|
||||
level: log.LevelDebug,
|
||||
s: pterm.DefaultSpinner.WithShowTimer(false).WithRemoveWhenDone(true),
|
||||
}
|
||||
for _, o := range opts {
|
||||
if err := o(l); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (l *Logger) Copy(opts ...LoggerOptions) (*Logger, error) {
|
||||
c := *l
|
||||
copy := &c
|
||||
for _, o := range opts {
|
||||
if err := o(copy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return copy, nil
|
||||
}
|
||||
|
||||
func joinMsg(args ...interface{}) (message string) {
|
||||
for _, m := range args {
|
||||
message += " " + fmt.Sprintf("%v", m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (l *Logger) enabled(lvl log.LogLevel) bool {
|
||||
return lvl >= l.level
|
||||
}
|
||||
|
||||
var emojiStrip = regexp.MustCompile(`[:][\w]+[:]`)
|
||||
|
||||
func (l *Logger) transform(args ...interface{}) (sanitized []interface{}) {
|
||||
for _, a := range args {
|
||||
var aString string
|
||||
|
||||
// Strip emoji if needed
|
||||
if l.emoji {
|
||||
aString = emoji.Sprint(a)
|
||||
} else {
|
||||
aString = emojiStrip.ReplaceAllString(joinMsg(a), "")
|
||||
}
|
||||
|
||||
sanitized = append(sanitized, aString)
|
||||
}
|
||||
|
||||
if l.context != "" {
|
||||
sanitized = append([]interface{}{fmt.Sprintf("(%s)", l.context)}, sanitized...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func prefixCodeLine(args ...interface{}) []interface{} {
|
||||
pc, file, line, ok := runtime.Caller(3)
|
||||
if ok {
|
||||
args = append([]interface{}{fmt.Sprintf("(%s:#%d:%v)",
|
||||
path.Base(file), line, runtime.FuncForPC(pc).Name())}, args...)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func (l *Logger) send(ll log.LogLevel, f string, args ...interface{}) {
|
||||
if !l.enabled(ll) {
|
||||
return
|
||||
}
|
||||
|
||||
sanitizedArgs := joinMsg(l.transform(args...)...)
|
||||
sanitizedF := joinMsg(l.transform(f)...)
|
||||
formatDefined := f != ""
|
||||
|
||||
switch {
|
||||
case log.LevelDebug == ll && !formatDefined:
|
||||
pterm.Debug.Println(prefixCodeLine(sanitizedArgs)...)
|
||||
if l.logToFile {
|
||||
l.fileLogger.Debug(joinMsg(prefixCodeLine(sanitizedArgs)...))
|
||||
}
|
||||
case log.LevelDebug == ll && formatDefined:
|
||||
pterm.Debug.Printfln(sanitizedF, prefixCodeLine(args...)...)
|
||||
if l.logToFile {
|
||||
l.fileLogger.Sugar().Debugf(sanitizedF, prefixCodeLine(args...)...)
|
||||
}
|
||||
case log.LevelError == ll && !formatDefined:
|
||||
pterm.Error.Println(pterm.LightRed(sanitizedArgs))
|
||||
if l.logToFile {
|
||||
l.fileLogger.Error(sanitizedArgs)
|
||||
}
|
||||
case log.LevelError == ll && formatDefined:
|
||||
pterm.Error.Printfln(pterm.LightRed(sanitizedF), args...)
|
||||
if l.logToFile {
|
||||
l.fileLogger.Sugar().Errorf(sanitizedF, args...)
|
||||
}
|
||||
|
||||
case log.LevelFatal == ll && !formatDefined:
|
||||
pterm.Error.Println(sanitizedArgs)
|
||||
if l.logToFile {
|
||||
l.fileLogger.Error(sanitizedArgs)
|
||||
}
|
||||
case log.LevelFatal == ll && formatDefined:
|
||||
pterm.Error.Printfln(sanitizedF, args...)
|
||||
if l.logToFile {
|
||||
l.fileLogger.Sugar().Errorf(sanitizedF, args...)
|
||||
}
|
||||
//INFO
|
||||
case log.LevelInfo == ll && !formatDefined:
|
||||
pterm.Info.Println(sanitizedArgs)
|
||||
if l.logToFile {
|
||||
l.fileLogger.Info(sanitizedArgs)
|
||||
}
|
||||
case log.LevelInfo == ll && formatDefined:
|
||||
pterm.Info.Printfln(sanitizedF, args...)
|
||||
if l.logToFile {
|
||||
l.fileLogger.Sugar().Infof(sanitizedF, args...)
|
||||
}
|
||||
//WARN
|
||||
case log.LevelWarn == ll && !formatDefined:
|
||||
pterm.Warning.Println(sanitizedArgs)
|
||||
if l.logToFile {
|
||||
l.fileLogger.Warn(sanitizedArgs)
|
||||
}
|
||||
case log.LevelWarn == ll && formatDefined:
|
||||
pterm.Warning.Printfln(sanitizedF, args...)
|
||||
if l.logToFile {
|
||||
l.fileLogger.Sugar().Warnf(sanitizedF, args...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Debug(args ...interface{}) {
|
||||
l.send(log.LevelDebug, "", args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Error(args ...interface{}) {
|
||||
l.send(log.LevelError, "", args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Trace(args ...interface{}) {
|
||||
l.send(log.LevelDebug, "", args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Tracef(t string, args ...interface{}) {
|
||||
l.send(log.LevelDebug, t, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Fatal(args ...interface{}) {
|
||||
l.send(log.LevelFatal, "", args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (l *Logger) Info(args ...interface{}) {
|
||||
l.send(log.LevelInfo, "", args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Success(args ...interface{}) {
|
||||
l.Info(append([]interface{}{"SUCCESS"}, args...)...)
|
||||
}
|
||||
|
||||
func (l *Logger) Panic(args ...interface{}) {
|
||||
l.Fatal(args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Warn(args ...interface{}) {
|
||||
l.send(log.LevelWarn, "", args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Warning(args ...interface{}) {
|
||||
l.Warn(args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Debugf(f string, args ...interface{}) {
|
||||
l.send(log.LevelDebug, f, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Errorf(f string, args ...interface{}) {
|
||||
l.send(log.LevelError, f, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Fatalf(f string, args ...interface{}) {
|
||||
l.send(log.LevelFatal, f, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Infof(f string, args ...interface{}) {
|
||||
l.send(log.LevelInfo, f, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Panicf(f string, args ...interface{}) {
|
||||
l.Fatalf(joinMsg(f), args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Warnf(f string, args ...interface{}) {
|
||||
l.send(log.LevelWarn, f, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Warningf(f string, args ...interface{}) {
|
||||
l.Warnf(f, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Ask() bool {
|
||||
var input string
|
||||
|
||||
l.Info("Do you want to continue with this operation? [y/N]: ")
|
||||
_, err := fmt.Scanln(&input)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
input = strings.ToLower(input)
|
||||
|
||||
if input == "y" || input == "yes" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Spinner starts the spinner
|
||||
func (l *Logger) Spinner() {
|
||||
if !IsTerminal() || l.noSpinner {
|
||||
return
|
||||
}
|
||||
|
||||
l.spinnerLock.Lock()
|
||||
defer l.spinnerLock.Unlock()
|
||||
|
||||
if l.s != nil && !l.s.IsActive {
|
||||
l.s, _ = l.s.Start()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Screen(text string) {
|
||||
pterm.DefaultHeader.WithBackgroundStyle(pterm.NewStyle(pterm.BgLightBlue)).WithMargin(2).Println(text)
|
||||
}
|
||||
|
||||
func (l *Logger) SpinnerText(suffix, prefix string) {
|
||||
if !IsTerminal() || l.noSpinner {
|
||||
return
|
||||
}
|
||||
l.spinnerLock.Lock()
|
||||
defer l.spinnerLock.Unlock()
|
||||
|
||||
if l.level == log.LevelDebug {
|
||||
fmt.Printf("%s %s\n",
|
||||
suffix, prefix,
|
||||
)
|
||||
} else {
|
||||
l.s.UpdateText(suffix + prefix)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) SpinnerStop() {
|
||||
if !IsTerminal() || l.noSpinner {
|
||||
return
|
||||
}
|
||||
l.spinnerLock.Lock()
|
||||
defer l.spinnerLock.Unlock()
|
||||
|
||||
if l.s != nil {
|
||||
l.s.Success()
|
||||
}
|
||||
}
|
@@ -13,7 +13,7 @@
|
||||
// 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
|
||||
package logger_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
@@ -21,7 +21,8 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/gookit/color"
|
||||
types "github.com/mudler/luet/pkg/api/core/types"
|
||||
"github.com/mudler/luet/pkg/api/core/logger"
|
||||
. "github.com/mudler/luet/pkg/api/core/logger"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
@@ -44,30 +45,11 @@ func captureStdout(f func(w io.Writer)) string {
|
||||
}
|
||||
|
||||
var _ = Describe("Context and logging", func() {
|
||||
ctx := types.NewContext()
|
||||
|
||||
BeforeEach(func() {
|
||||
ctx = types.NewContext()
|
||||
})
|
||||
|
||||
Context("LogLevel", func() {
|
||||
It("converts it correctly to number and zaplog", func() {
|
||||
Expect(types.ErrorLevel.ToNumber()).To(Equal(0))
|
||||
Expect(types.InfoLevel.ToNumber()).To(Equal(2))
|
||||
Expect(types.WarningLevel.ToNumber()).To(Equal(1))
|
||||
Expect(types.LogLevel("foo").ToNumber()).To(Equal(3))
|
||||
Expect(types.WarningLevel.ZapLevel().String()).To(Equal("warn"))
|
||||
Expect(types.InfoLevel.ZapLevel().String()).To(Equal("info"))
|
||||
Expect(types.ErrorLevel.ZapLevel().String()).To(Equal("error"))
|
||||
Expect(types.FatalLevel.ZapLevel().String()).To(Equal("fatal"))
|
||||
Expect(types.LogLevel("foo").ZapLevel().String()).To(Equal("debug"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Context", func() {
|
||||
It("detect if is a terminal", func() {
|
||||
Expect(captureStdout(func(w io.Writer) {
|
||||
_, _, err := ctx.GetTerminalSize()
|
||||
_, _, err := GetTerminalSize()
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(Equal("size not detectable"))
|
||||
os.Stdout.Write([]byte(err.Error()))
|
||||
@@ -75,47 +57,71 @@ var _ = Describe("Context and logging", func() {
|
||||
})
|
||||
|
||||
It("respects loglevel", func() {
|
||||
ctx.Config.GetGeneral().Debug = false
|
||||
|
||||
l, err := New(WithLevel("info"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(captureStdout(func(w io.Writer) {
|
||||
ctx.Debug("")
|
||||
l.Debug("")
|
||||
})).To(Equal(""))
|
||||
|
||||
ctx.Config.GetGeneral().Debug = true
|
||||
l, err = New(WithLevel("debug"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(captureStdout(func(w io.Writer) {
|
||||
ctx.Debug("foo")
|
||||
l.Debug("foo")
|
||||
})).To(ContainSubstring("foo"))
|
||||
})
|
||||
|
||||
It("logs with context", func() {
|
||||
l, err := New(WithLevel("debug"), WithContext("foo"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(captureStdout(func(w io.Writer) {
|
||||
l.Debug("bar")
|
||||
})).To(ContainSubstring("(foo) bar"))
|
||||
})
|
||||
|
||||
It("returns copies with logged context", func() {
|
||||
l, err := New(WithLevel("debug"))
|
||||
l, _ = l.Copy(logger.WithContext("bazzz"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(captureStdout(func(w io.Writer) {
|
||||
l.Debug("bar")
|
||||
})).To(ContainSubstring("(bazzz) bar"))
|
||||
})
|
||||
|
||||
It("logs to file", func() {
|
||||
ctx.NoColor()
|
||||
|
||||
t, err := ioutil.TempFile("", "tree")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(t.Name()) // clean up
|
||||
ctx.Config.GetLogging().EnableLogFile = true
|
||||
ctx.Config.GetLogging().Path = t.Name()
|
||||
|
||||
ctx.Init()
|
||||
l, err := New(WithLevel("debug"), WithFileLogging(t.Name(), ""))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// ctx.Init()
|
||||
|
||||
Expect(captureStdout(func(w io.Writer) {
|
||||
ctx.Info("foot")
|
||||
l.Info("foot")
|
||||
})).To(And(ContainSubstring("INFO"), ContainSubstring("foot")))
|
||||
|
||||
Expect(captureStdout(func(w io.Writer) {
|
||||
ctx.Success("test")
|
||||
l.Success("test")
|
||||
})).To(And(ContainSubstring("SUCCESS"), ContainSubstring("test")))
|
||||
|
||||
Expect(captureStdout(func(w io.Writer) {
|
||||
ctx.Error("foobar")
|
||||
l.Error("foobar")
|
||||
})).To(And(ContainSubstring("ERROR"), ContainSubstring("foobar")))
|
||||
|
||||
Expect(captureStdout(func(w io.Writer) {
|
||||
ctx.Warning("foowarn")
|
||||
l.Warning("foowarn")
|
||||
})).To(And(ContainSubstring("WARNING"), ContainSubstring("foowarn")))
|
||||
|
||||
l, err := ioutil.ReadFile(t.Name())
|
||||
ll, err := ioutil.ReadFile(t.Name())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
logs := string(l)
|
||||
logs := string(ll)
|
||||
Expect(logs).To(ContainSubstring("foot"))
|
||||
Expect(logs).To(ContainSubstring("test"))
|
||||
Expect(logs).To(ContainSubstring("foowarn"))
|
43
pkg/api/core/logger/terminal.go
Normal file
43
pkg/api/core/logger/terminal.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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 logger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/mattn/go-isatty"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func IsTerminal() bool {
|
||||
return isatty.IsTerminal(os.Stdout.Fd())
|
||||
}
|
||||
|
||||
// GetTerminalSize returns the width and the height of the active terminal.
|
||||
func GetTerminalSize() (width, height int, err error) {
|
||||
w, h, err := term.GetSize(int(os.Stdout.Fd()))
|
||||
if w <= 0 {
|
||||
w = 0
|
||||
}
|
||||
if h <= 0 {
|
||||
h = 0
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.New("size not detectable")
|
||||
}
|
||||
return w, h, err
|
||||
}
|
@@ -41,7 +41,7 @@ import (
|
||||
bus "github.com/mudler/luet/pkg/api/core/bus"
|
||||
config "github.com/mudler/luet/pkg/api/core/config"
|
||||
"github.com/mudler/luet/pkg/api/core/image"
|
||||
types "github.com/mudler/luet/pkg/api/core/types"
|
||||
"github.com/mudler/luet/pkg/api/core/types"
|
||||
backend "github.com/mudler/luet/pkg/compiler/backend"
|
||||
compression "github.com/mudler/luet/pkg/compiler/types/compression"
|
||||
compilerspec "github.com/mudler/luet/pkg/compiler/types/spec"
|
||||
@@ -70,7 +70,7 @@ type PackageArtifact struct {
|
||||
Runtime *pkg.DefaultPackage `json:"runtime,omitempty"`
|
||||
}
|
||||
|
||||
func ImageToArtifact(ctx *types.Context, img v1.Image, t compression.Implementation, output string, filter func(h *tar.Header) (bool, error)) (*PackageArtifact, error) {
|
||||
func ImageToArtifact(ctx types.Context, img v1.Image, t compression.Implementation, output string, filter func(h *tar.Header) (bool, error)) (*PackageArtifact, error) {
|
||||
_, tmpdiffs, err := image.Extract(ctx, img, filter)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error met while creating tempdir for rootfs")
|
||||
@@ -176,12 +176,12 @@ func (a *PackageArtifact) GetFileName() string {
|
||||
}
|
||||
|
||||
// CreateArtifactForFile creates a new artifact from the given file
|
||||
func CreateArtifactForFile(ctx *types.Context, s string, opts ...func(*PackageArtifact)) (*PackageArtifact, error) {
|
||||
func CreateArtifactForFile(ctx types.Context, s string, opts ...func(*PackageArtifact)) (*PackageArtifact, error) {
|
||||
if _, err := os.Stat(s); os.IsNotExist(err) {
|
||||
return nil, errors.Wrap(err, "artifact path doesn't exist")
|
||||
}
|
||||
fileName := path.Base(s)
|
||||
archive, err := ctx.Config.GetSystem().TempDir("archive")
|
||||
archive, err := ctx.TempDir("archive")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error met while creating tempdir for "+s)
|
||||
}
|
||||
@@ -191,7 +191,7 @@ func CreateArtifactForFile(ctx *types.Context, s string, opts ...func(*PackageAr
|
||||
return nil, errors.Wrapf(err, "error while copying %s to %s", s, dst)
|
||||
}
|
||||
|
||||
artifact, err := ctx.Config.GetSystem().TempDir("artifact")
|
||||
artifact, err := ctx.TempDir("artifact")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error met while creating tempdir for "+s)
|
||||
}
|
||||
@@ -210,9 +210,9 @@ type ImageBuilder interface {
|
||||
}
|
||||
|
||||
// GenerateFinalImage takes an artifact and builds a Docker image with its content
|
||||
func (a *PackageArtifact) GenerateFinalImage(ctx *types.Context, imageName string, b ImageBuilder, keepPerms bool) error {
|
||||
func (a *PackageArtifact) GenerateFinalImage(ctx types.Context, imageName string, b ImageBuilder, keepPerms bool) error {
|
||||
|
||||
tempimage, err := ctx.Config.GetSystem().TempFile("tempimage")
|
||||
tempimage, err := ctx.TempFile("tempimage")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error met while creating tempdir for "+a.Path)
|
||||
}
|
||||
@@ -428,7 +428,7 @@ func replaceFileTarWrapper(dst string, inputTarStream io.ReadCloser, mods []stri
|
||||
return pipeReader
|
||||
}
|
||||
|
||||
func tarModifierWrapperFunc(ctx *types.Context) func(dst, path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
|
||||
func tarModifierWrapperFunc(ctx types.Context) func(dst, path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
|
||||
return func(dst, path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
|
||||
// If the destination path already exists I rename target file name with postfix.
|
||||
var destPath string
|
||||
@@ -495,10 +495,10 @@ func tarModifierWrapperFunc(ctx *types.Context) func(dst, path string, header *t
|
||||
}
|
||||
}
|
||||
|
||||
func (a *PackageArtifact) GetProtectFiles(ctx *types.Context) (res []string) {
|
||||
func (a *PackageArtifact) GetProtectFiles(ctx types.Context) (res []string) {
|
||||
annotationDir := ""
|
||||
|
||||
if !ctx.Config.ConfigProtectSkip {
|
||||
if !ctx.GetConfig().ConfigProtectSkip {
|
||||
|
||||
// a.CompileSpec could be nil when artifact.Unpack is used for tree tarball
|
||||
if a.CompileSpec != nil &&
|
||||
@@ -511,7 +511,7 @@ func (a *PackageArtifact) GetProtectFiles(ctx *types.Context) (res []string) {
|
||||
// TODO: check if skip this if we have a.CompileSpec nil
|
||||
|
||||
cp := config.NewConfigProtect(annotationDir)
|
||||
cp.Map(a.Files, ctx.Config.GetConfigProtectConfFiles())
|
||||
cp.Map(a.Files, ctx.GetConfig().ConfigProtectConfFiles)
|
||||
|
||||
// NOTE: for unpack we need files path without initial /
|
||||
res = cp.GetProtectFiles(false)
|
||||
@@ -521,7 +521,7 @@ func (a *PackageArtifact) GetProtectFiles(ctx *types.Context) (res []string) {
|
||||
}
|
||||
|
||||
// Unpack Untar and decompress (TODO) to the given path
|
||||
func (a *PackageArtifact) Unpack(ctx *types.Context, dst string, keepPerms bool) error {
|
||||
func (a *PackageArtifact) Unpack(ctx types.Context, dst string, keepPerms bool) error {
|
||||
|
||||
if !strings.HasPrefix(dst, string(os.PathSeparator)) {
|
||||
return errors.New("destination must be an absolute path")
|
||||
|
@@ -20,10 +20,9 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/mudler/luet/pkg/api/core/context"
|
||||
"github.com/mudler/luet/pkg/api/core/image"
|
||||
"github.com/mudler/luet/pkg/api/core/types"
|
||||
. "github.com/mudler/luet/pkg/api/core/types/artifact"
|
||||
. "github.com/mudler/luet/pkg/compiler/backend"
|
||||
backend "github.com/mudler/luet/pkg/compiler/backend"
|
||||
compression "github.com/mudler/luet/pkg/compiler/types/compression"
|
||||
"github.com/mudler/luet/pkg/compiler/types/options"
|
||||
@@ -39,7 +38,7 @@ import (
|
||||
|
||||
var _ = Describe("Artifact", func() {
|
||||
Context("Simple package build definition", func() {
|
||||
ctx := types.NewContext()
|
||||
ctx := context.NewContext()
|
||||
It("Generates a verified delta", func() {
|
||||
|
||||
generalRecipe := tree.NewGeneralRecipe(pkg.NewInMemoryDatabase(false))
|
||||
@@ -49,7 +48,7 @@ var _ = Describe("Artifact", func() {
|
||||
|
||||
Expect(len(generalRecipe.GetDatabase().GetPackages())).To(Equal(1))
|
||||
|
||||
cc := NewLuetCompiler(nil, generalRecipe.GetDatabase(), options.WithContext(types.NewContext()))
|
||||
cc := NewLuetCompiler(nil, generalRecipe.GetDatabase(), options.WithContext(context.NewContext()))
|
||||
lspec, err := cc.FromPackage(&pkg.DefaultPackage{Name: "enman", Category: "app-admin", Version: "1.4.0"})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
@@ -83,7 +82,7 @@ WORKDIR /luetbuild
|
||||
ENV PACKAGE_NAME=enman
|
||||
ENV PACKAGE_VERSION=1.4.0
|
||||
ENV PACKAGE_CATEGORY=app-admin`))
|
||||
b := NewSimpleDockerBackend(ctx)
|
||||
b := backend.NewSimpleDockerBackend(ctx)
|
||||
opts := backend.Options{
|
||||
ImageName: "luet/base",
|
||||
SourcePath: tmpdir,
|
||||
@@ -120,7 +119,7 @@ RUN echo bar > /test2`))
|
||||
})
|
||||
|
||||
It("Generates packages images", func() {
|
||||
b := NewSimpleDockerBackend(ctx)
|
||||
b := backend.NewSimpleDockerBackend(ctx)
|
||||
imageprefix := "foo/"
|
||||
testString := []byte(`funky test data`)
|
||||
|
||||
@@ -177,7 +176,7 @@ RUN echo bar > /test2`))
|
||||
})
|
||||
|
||||
It("Generates empty packages images", func() {
|
||||
b := NewSimpleDockerBackend(ctx)
|
||||
b := backend.NewSimpleDockerBackend(ctx)
|
||||
imageprefix := "foo/"
|
||||
|
||||
tmpdir, err := ioutil.TempDir(os.TempDir(), "artifact")
|
||||
|
@@ -20,7 +20,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
types "github.com/mudler/luet/pkg/api/core/types"
|
||||
"github.com/mudler/luet/pkg/api/core/context"
|
||||
. "github.com/mudler/luet/pkg/api/core/types/artifact"
|
||||
compilerspec "github.com/mudler/luet/pkg/compiler/types/spec"
|
||||
fileHelper "github.com/mudler/luet/pkg/helpers/file"
|
||||
@@ -59,7 +59,7 @@ var _ = Describe("Cache", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
b := NewPackageArtifact(path)
|
||||
ctx := types.NewContext()
|
||||
ctx := context.NewContext()
|
||||
err = b.Unpack(ctx, tmpdir, false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
|
@@ -45,7 +45,7 @@ type HashOptions struct {
|
||||
|
||||
func (c Checksums) List() (res [][]string) {
|
||||
keys := make([]string, 0)
|
||||
for k, _ := range c {
|
||||
for k := range c {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
@@ -1,6 +1,4 @@
|
||||
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
|
||||
// Daniele Rondina <geaaru@sabayonlinux.org>
|
||||
// 2021 Ettore Di Giacinto <mudler@mocaccino.org>
|
||||
// Copyright © 2019-2021 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
|
||||
@@ -39,19 +37,22 @@ var AvailableResolvers = strings.Join([]string{solver.QLearningResolverType}, "
|
||||
|
||||
type LuetLoggingConfig struct {
|
||||
// Path of the logfile
|
||||
Path string `mapstructure:"path"`
|
||||
Path string `yaml:"path" mapstructure:"path"`
|
||||
// Enable/Disable logging to file
|
||||
EnableLogFile bool `mapstructure:"enable_logfile"`
|
||||
EnableLogFile bool `yaml:"enable_logfile" mapstructure:"enable_logfile"`
|
||||
// Enable JSON format logging in file
|
||||
JsonFormat bool `mapstructure:"json_format"`
|
||||
JsonFormat bool `yaml:"json_format" mapstructure:"json_format"`
|
||||
|
||||
// Log level
|
||||
Level LogLevel `mapstructure:"level"`
|
||||
Level string `yaml:"level" mapstructure:"level"`
|
||||
|
||||
// Enable emoji
|
||||
EnableEmoji bool `mapstructure:"enable_emoji"`
|
||||
EnableEmoji bool `yaml:"enable_emoji" mapstructure:"enable_emoji"`
|
||||
// Enable/Disable color in logging
|
||||
Color bool `mapstructure:"color"`
|
||||
Color bool `yaml:"color" mapstructure:"color"`
|
||||
|
||||
// NoSpinner disable spinner
|
||||
NoSpinner bool `yaml:"no_spinner" mapstructure:"no_spinner"`
|
||||
}
|
||||
|
||||
type LuetGeneralConfig struct {
|
||||
@@ -108,8 +109,42 @@ type LuetSystemConfig struct {
|
||||
TmpDirBase string `yaml:"tmpdir_base" mapstructure:"tmpdir_base"`
|
||||
}
|
||||
|
||||
func (s *LuetSystemConfig) SetRootFS(path string) error {
|
||||
p, err := fileHelper.Rel2Abs(path)
|
||||
// Init reads the config and replace user-defined paths with
|
||||
// absolute paths where necessary, and construct the paths for the cache
|
||||
// and database on the real system
|
||||
func (c *LuetConfig) Init() error {
|
||||
if err := c.System.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.loadConfigProtect(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load repositories
|
||||
if err := c.loadRepositories(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LuetSystemConfig) init() error {
|
||||
if err := s.setRootfs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.setDBPath(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.setCachePath()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LuetSystemConfig) setRootfs() error {
|
||||
p, err := fileHelper.Rel2Abs(s.Rootfs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -118,9 +153,8 @@ func (s *LuetSystemConfig) SetRootFS(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sc *LuetSystemConfig) GetRepoDatabaseDirPath(name string) string {
|
||||
dbpath := filepath.Join(sc.Rootfs, sc.DatabasePath)
|
||||
dbpath = filepath.Join(dbpath, "repos/"+name)
|
||||
func (sc LuetSystemConfig) GetRepoDatabaseDirPath(name string) string {
|
||||
dbpath := filepath.Join(sc.DatabasePath, "repos/"+name)
|
||||
err := os.MkdirAll(dbpath, os.ModePerm)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -128,45 +162,48 @@ func (sc *LuetSystemConfig) GetRepoDatabaseDirPath(name string) string {
|
||||
return dbpath
|
||||
}
|
||||
|
||||
func (sc *LuetSystemConfig) GetSystemRepoDatabaseDirPath() string {
|
||||
func (sc *LuetSystemConfig) setDBPath() error {
|
||||
dbpath := filepath.Join(sc.Rootfs,
|
||||
sc.DatabasePath)
|
||||
err := os.MkdirAll(dbpath, os.ModePerm)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
return dbpath
|
||||
sc.DatabasePath = dbpath
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sc *LuetSystemConfig) GetSystemPkgsCacheDirPath() (p string) {
|
||||
func (sc *LuetSystemConfig) setCachePath() {
|
||||
var cachepath string
|
||||
if sc.PkgsCachePath != "" {
|
||||
cachepath = sc.PkgsCachePath
|
||||
if !filepath.IsAbs(cachepath) {
|
||||
cachepath = filepath.Join(sc.DatabasePath, sc.PkgsCachePath)
|
||||
os.MkdirAll(cachepath, os.ModePerm)
|
||||
} else {
|
||||
cachepath = sc.PkgsCachePath
|
||||
}
|
||||
} else {
|
||||
// Create dynamic cache for test suites
|
||||
cachepath, _ = ioutil.TempDir(os.TempDir(), "cachepkgs")
|
||||
}
|
||||
|
||||
if filepath.IsAbs(cachepath) {
|
||||
p = cachepath
|
||||
} else {
|
||||
p = filepath.Join(sc.GetSystemRepoDatabaseDirPath(), cachepath)
|
||||
}
|
||||
|
||||
sc.PkgsCachePath = cachepath // Be consistent with the path we set
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (sc *LuetSystemConfig) GetRootFsAbs() (string, error) {
|
||||
return filepath.Abs(sc.Rootfs)
|
||||
}
|
||||
|
||||
type LuetKV struct {
|
||||
type FinalizerEnv struct {
|
||||
Key string `json:"key" yaml:"key" mapstructure:"key"`
|
||||
Value string `json:"value" yaml:"value" mapstructure:"value"`
|
||||
}
|
||||
|
||||
type Finalizers []FinalizerEnv
|
||||
|
||||
func (f Finalizers) Slice() (sl []string) {
|
||||
for _, kv := range f {
|
||||
sl = append(sl, fmt.Sprintf("%s=%s", kv.Key, kv.Value))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type LuetConfig struct {
|
||||
Logging LuetLoggingConfig `yaml:"logging,omitempty" mapstructure:"logging"`
|
||||
General LuetGeneralConfig `yaml:"general,omitempty" mapstructure:"general"`
|
||||
@@ -179,16 +216,16 @@ type LuetConfig struct {
|
||||
ConfigFromHost bool `yaml:"config_from_host,omitempty" mapstructure:"config_from_host"`
|
||||
SystemRepositories LuetRepositories `yaml:"repositories,omitempty" mapstructure:"repositories"`
|
||||
|
||||
FinalizerEnvs []LuetKV `json:"finalizer_envs,omitempty" yaml:"finalizer_envs,omitempty" mapstructure:"finalizer_envs,omitempty"`
|
||||
FinalizerEnvs Finalizers `json:"finalizer_envs,omitempty" yaml:"finalizer_envs,omitempty" mapstructure:"finalizer_envs,omitempty"`
|
||||
|
||||
ConfigProtectConfFiles []config.ConfigProtectConfFile `yaml:"-" mapstructure:"-"`
|
||||
}
|
||||
|
||||
func (c *LuetConfig) GetSystemDB() pkg.PackageDatabase {
|
||||
switch c.GetSystem().DatabaseEngine {
|
||||
switch c.System.DatabaseEngine {
|
||||
case "boltdb":
|
||||
return pkg.NewBoltDatabase(
|
||||
filepath.Join(c.GetSystem().GetSystemRepoDatabaseDirPath(), "luet.db"))
|
||||
filepath.Join(c.System.DatabasePath, "luet.db"))
|
||||
default:
|
||||
return pkg.NewInMemoryDatabase(true)
|
||||
}
|
||||
@@ -198,83 +235,30 @@ func (c *LuetConfig) AddSystemRepository(r LuetRepository) {
|
||||
c.SystemRepositories = append(c.SystemRepositories, r)
|
||||
}
|
||||
|
||||
func (c *LuetConfig) GetFinalizerEnvsMap() map[string]string {
|
||||
ans := make(map[string]string)
|
||||
|
||||
for _, kv := range c.FinalizerEnvs {
|
||||
ans[kv.Key] = kv.Value
|
||||
}
|
||||
return ans
|
||||
}
|
||||
|
||||
func (c *LuetConfig) SetFinalizerEnv(k, v string) {
|
||||
keyPresent := false
|
||||
envs := []LuetKV{}
|
||||
envs := []FinalizerEnv{}
|
||||
|
||||
for _, kv := range c.FinalizerEnvs {
|
||||
if kv.Key == k {
|
||||
keyPresent = true
|
||||
envs = append(envs, LuetKV{Key: kv.Key, Value: v})
|
||||
envs = append(envs, FinalizerEnv{Key: kv.Key, Value: v})
|
||||
} else {
|
||||
envs = append(envs, kv)
|
||||
}
|
||||
}
|
||||
if !keyPresent {
|
||||
envs = append(envs, LuetKV{Key: k, Value: v})
|
||||
envs = append(envs, FinalizerEnv{Key: k, Value: v})
|
||||
}
|
||||
|
||||
c.FinalizerEnvs = envs
|
||||
}
|
||||
|
||||
func (c *LuetConfig) GetFinalizerEnvs() []string {
|
||||
ans := []string{}
|
||||
for _, kv := range c.FinalizerEnvs {
|
||||
ans = append(ans, fmt.Sprintf("%s=%s", kv.Key, kv.Value))
|
||||
}
|
||||
return ans
|
||||
}
|
||||
|
||||
func (c *LuetConfig) GetFinalizerEnv(k string) (string, error) {
|
||||
keyNotPresent := true
|
||||
ans := ""
|
||||
for _, kv := range c.FinalizerEnvs {
|
||||
if kv.Key == k {
|
||||
keyNotPresent = false
|
||||
ans = kv.Value
|
||||
}
|
||||
}
|
||||
|
||||
if keyNotPresent {
|
||||
return "", errors.New("Finalizer key " + k + " not found")
|
||||
}
|
||||
return ans, nil
|
||||
}
|
||||
|
||||
func (c *LuetConfig) GetLogging() *LuetLoggingConfig {
|
||||
return &c.Logging
|
||||
}
|
||||
|
||||
func (c *LuetConfig) GetGeneral() *LuetGeneralConfig {
|
||||
return &c.General
|
||||
}
|
||||
|
||||
func (c *LuetConfig) GetSystem() *LuetSystemConfig {
|
||||
return &c.System
|
||||
}
|
||||
|
||||
func (c *LuetConfig) GetSolverOptions() *LuetSolverOptions {
|
||||
return &c.Solver
|
||||
}
|
||||
|
||||
func (c *LuetConfig) YAML() ([]byte, error) {
|
||||
return yaml.Marshal(c)
|
||||
}
|
||||
|
||||
func (c *LuetConfig) GetConfigProtectConfFiles() []config.ConfigProtectConfFile {
|
||||
return c.ConfigProtectConfFiles
|
||||
}
|
||||
|
||||
func (c *LuetConfig) AddConfigProtectConfFile(file *config.ConfigProtectConfFile) {
|
||||
func (c *LuetConfig) addProtectFile(file *config.ConfigProtectConfFile) {
|
||||
if c.ConfigProtectConfFiles == nil {
|
||||
c.ConfigProtectConfFiles = []config.ConfigProtectConfFile{*file}
|
||||
} else {
|
||||
@@ -282,28 +266,21 @@ func (c *LuetConfig) AddConfigProtectConfFile(file *config.ConfigProtectConfFile
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LuetConfig) LoadRepositories(ctx *Context) error {
|
||||
func (c *LuetConfig) loadRepositories() error {
|
||||
var regexRepo = regexp.MustCompile(`.yml$|.yaml$`)
|
||||
var err error
|
||||
rootfs := ""
|
||||
|
||||
// Respect the rootfs param on read repositories
|
||||
if !c.ConfigFromHost {
|
||||
rootfs, err = c.GetSystem().GetRootFsAbs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rootfs = c.System.Rootfs
|
||||
}
|
||||
|
||||
for _, rdir := range c.RepositoriesConfDir {
|
||||
|
||||
rdir = filepath.Join(rootfs, rdir)
|
||||
|
||||
ctx.Debug("Parsing Repository Directory", rdir, "...")
|
||||
|
||||
files, err := ioutil.ReadDir(rdir)
|
||||
if err != nil {
|
||||
ctx.Debug("Skip dir", rdir, ":", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -313,27 +290,20 @@ func (c *LuetConfig) LoadRepositories(ctx *Context) error {
|
||||
}
|
||||
|
||||
if !regexRepo.MatchString(file.Name()) {
|
||||
ctx.Debug("File", file.Name(), "skipped.")
|
||||
continue
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(path.Join(rdir, file.Name()))
|
||||
if err != nil {
|
||||
ctx.Warning("On read file", file.Name(), ":", err.Error())
|
||||
ctx.Warning("File", file.Name(), "skipped.")
|
||||
continue
|
||||
}
|
||||
|
||||
r, err := LoadRepository(content)
|
||||
if err != nil {
|
||||
ctx.Warning("On parse file", file.Name(), ":", err.Error())
|
||||
ctx.Warning("File", file.Name(), "skipped.")
|
||||
continue
|
||||
}
|
||||
|
||||
if r.Name == "" || len(r.Urls) == 0 || r.Type == "" {
|
||||
ctx.Warning("Invalid repository ", file.Name())
|
||||
ctx.Warning("File", file.Name(), "skipped.")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -359,28 +329,20 @@ func (c *LuetConfig) GetSystemRepository(name string) (*LuetRepository, error) {
|
||||
return ans, nil
|
||||
}
|
||||
|
||||
func (c *LuetConfig) LoadConfigProtect(ctx *Context) error {
|
||||
func (c *LuetConfig) loadConfigProtect() error {
|
||||
var regexConfs = regexp.MustCompile(`.yml$`)
|
||||
var err error
|
||||
|
||||
rootfs := ""
|
||||
|
||||
// Respect the rootfs param on read repositories
|
||||
if !c.ConfigFromHost {
|
||||
rootfs, err = c.GetSystem().GetRootFsAbs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rootfs = c.System.Rootfs
|
||||
}
|
||||
|
||||
for _, cdir := range c.ConfigProtectConfDir {
|
||||
cdir = filepath.Join(rootfs, cdir)
|
||||
|
||||
ctx.Debug("Parsing Config Protect Directory", cdir, "...")
|
||||
|
||||
files, err := ioutil.ReadDir(cdir)
|
||||
if err != nil {
|
||||
ctx.Debug("Skip dir", cdir, ":", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -390,38 +352,31 @@ func (c *LuetConfig) LoadConfigProtect(ctx *Context) error {
|
||||
}
|
||||
|
||||
if !regexConfs.MatchString(file.Name()) {
|
||||
ctx.Debug("File", file.Name(), "skipped.")
|
||||
continue
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(path.Join(cdir, file.Name()))
|
||||
if err != nil {
|
||||
ctx.Warning("On read file", file.Name(), ":", err.Error())
|
||||
ctx.Warning("File", file.Name(), "skipped.")
|
||||
continue
|
||||
}
|
||||
|
||||
r, err := loadConfigProtectConFile(file.Name(), content)
|
||||
r, err := loadConfigProtectConfFile(file.Name(), content)
|
||||
if err != nil {
|
||||
ctx.Warning("On parse file", file.Name(), ":", err.Error())
|
||||
ctx.Warning("File", file.Name(), "skipped.")
|
||||
continue
|
||||
}
|
||||
|
||||
if r.Name == "" || len(r.Directories) == 0 {
|
||||
ctx.Warning("Invalid config protect file", file.Name())
|
||||
ctx.Warning("File", file.Name(), "skipped.")
|
||||
continue
|
||||
}
|
||||
|
||||
c.AddConfigProtectConfFile(r)
|
||||
c.addProtectFile(r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func loadConfigProtectConFile(filename string, data []byte) (*config.ConfigProtectConfFile, error) {
|
||||
func loadConfigProtectConfFile(filename string, data []byte) (*config.ConfigProtectConfFile, error) {
|
||||
ans := config.NewConfigProtectConfFile(filename)
|
||||
err := yaml.Unmarshal(data, &ans)
|
||||
if err != nil {
|
||||
@@ -429,47 +384,3 @@ func loadConfigProtectConFile(filename string, data []byte) (*config.ConfigProte
|
||||
}
|
||||
return ans, nil
|
||||
}
|
||||
|
||||
func (c *LuetLoggingConfig) SetLogLevel(s LogLevel) {
|
||||
c.Level = s
|
||||
}
|
||||
|
||||
func (c *LuetSystemConfig) InitTmpDir() error {
|
||||
if !filepath.IsAbs(c.TmpDirBase) {
|
||||
abs, err := fileHelper.Rel2Abs(c.TmpDirBase)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while converting relative path to absolute path")
|
||||
}
|
||||
c.TmpDirBase = abs
|
||||
}
|
||||
|
||||
if _, err := os.Stat(c.TmpDirBase); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(c.TmpDirBase, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *LuetSystemConfig) CleanupTmpDir() error {
|
||||
return os.RemoveAll(c.TmpDirBase)
|
||||
}
|
||||
|
||||
func (c *LuetSystemConfig) TempDir(pattern string) (string, error) {
|
||||
err := c.InitTmpDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ioutil.TempDir(c.TmpDirBase, pattern)
|
||||
}
|
||||
|
||||
func (c *LuetSystemConfig) TempFile(pattern string) (*os.File, error) {
|
||||
err := c.InitTmpDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.TempFile(c.TmpDirBase, pattern)
|
||||
}
|
||||
|
@@ -17,53 +17,86 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
types "github.com/mudler/luet/pkg/api/core/types"
|
||||
"github.com/mudler/luet/pkg/api/core/context"
|
||||
"github.com/mudler/luet/pkg/api/core/types"
|
||||
fileHelper "github.com/mudler/luet/pkg/helpers/file"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Config", func() {
|
||||
Context("Load Repository1", func() {
|
||||
ctx := types.NewContext()
|
||||
|
||||
ctx.Config.RepositoriesConfDir = []string{
|
||||
"../../../../tests/fixtures/repos.conf.d",
|
||||
}
|
||||
err := ctx.Config.LoadRepositories(ctx)
|
||||
Context("Inits paths", func() {
|
||||
t, _ := ioutil.TempDir("", "tests")
|
||||
defer os.RemoveAll(t)
|
||||
c := &types.LuetConfig{
|
||||
System: types.LuetSystemConfig{
|
||||
Rootfs: t,
|
||||
PkgsCachePath: "foo",
|
||||
DatabasePath: "baz",
|
||||
}}
|
||||
It("sets default", func() {
|
||||
err := c.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(c.System.Rootfs).To(Equal(t))
|
||||
Expect(c.System.PkgsCachePath).To(Equal(filepath.Join(t, "baz", "foo")))
|
||||
Expect(c.System.DatabasePath).To(Equal(filepath.Join(t, "baz")))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Load Repository1", func() {
|
||||
var ctx *context.Context
|
||||
BeforeEach(func() {
|
||||
ctx = context.NewContext(context.WithConfig(&types.LuetConfig{
|
||||
RepositoriesConfDir: []string{
|
||||
"../../../../tests/fixtures/repos.conf.d",
|
||||
},
|
||||
}))
|
||||
ctx.Config.Init()
|
||||
})
|
||||
|
||||
It("Check Load Repository 1", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(ctx.Config.SystemRepositories)).Should(Equal(2))
|
||||
Expect(ctx.Config.SystemRepositories[0].Name).Should(Equal("test1"))
|
||||
Expect(ctx.Config.SystemRepositories[0].Priority).Should(Equal(999))
|
||||
Expect(ctx.Config.SystemRepositories[0].Type).Should(Equal("disk"))
|
||||
Expect(len(ctx.Config.SystemRepositories[0].Urls)).Should(Equal(1))
|
||||
Expect(ctx.Config.SystemRepositories[0].Urls[0]).Should(Equal("tests/repos/test1"))
|
||||
Expect(len(ctx.GetConfig().SystemRepositories)).Should(Equal(2))
|
||||
Expect(ctx.GetConfig().SystemRepositories[0].Name).Should(Equal("test1"))
|
||||
Expect(ctx.GetConfig().SystemRepositories[0].Priority).Should(Equal(999))
|
||||
Expect(ctx.GetConfig().SystemRepositories[0].Type).Should(Equal("disk"))
|
||||
Expect(len(ctx.GetConfig().SystemRepositories[0].Urls)).Should(Equal(1))
|
||||
Expect(ctx.GetConfig().SystemRepositories[0].Urls[0]).Should(Equal("tests/repos/test1"))
|
||||
})
|
||||
|
||||
It("Chec Load Repository 2", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(ctx.Config.SystemRepositories)).Should(Equal(2))
|
||||
Expect(ctx.Config.SystemRepositories[1].Name).Should(Equal("test2"))
|
||||
Expect(ctx.Config.SystemRepositories[1].Priority).Should(Equal(1000))
|
||||
Expect(ctx.Config.SystemRepositories[1].Type).Should(Equal("disk"))
|
||||
Expect(len(ctx.Config.SystemRepositories[1].Urls)).Should(Equal(1))
|
||||
Expect(ctx.Config.SystemRepositories[1].Urls[0]).Should(Equal("tests/repos/test2"))
|
||||
Expect(len(ctx.GetConfig().SystemRepositories)).Should(Equal(2))
|
||||
Expect(ctx.GetConfig().SystemRepositories[1].Name).Should(Equal("test2"))
|
||||
Expect(ctx.GetConfig().SystemRepositories[1].Priority).Should(Equal(1000))
|
||||
Expect(ctx.GetConfig().SystemRepositories[1].Type).Should(Equal("disk"))
|
||||
Expect(len(ctx.GetConfig().SystemRepositories[1].Urls)).Should(Equal(1))
|
||||
Expect(ctx.GetConfig().SystemRepositories[1].Urls[0]).Should(Equal("tests/repos/test2"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Simple temporary directory creation", func() {
|
||||
ctx := context.NewContext(context.WithConfig(&types.LuetConfig{
|
||||
System: types.LuetSystemConfig{
|
||||
TmpDirBase: os.TempDir() + "/tmpluet",
|
||||
},
|
||||
}))
|
||||
|
||||
BeforeEach(func() {
|
||||
ctx = context.NewContext(context.WithConfig(&types.LuetConfig{
|
||||
System: types.LuetSystemConfig{
|
||||
TmpDirBase: os.TempDir() + "/tmpluet",
|
||||
},
|
||||
}))
|
||||
|
||||
})
|
||||
|
||||
It("Create Temporary directory", func() {
|
||||
ctx := types.NewContext()
|
||||
|
||||
ctx.Config.GetSystem().TmpDirBase = os.TempDir() + "/tmpluet"
|
||||
|
||||
tmpDir, err := ctx.Config.GetSystem().TempDir("test1")
|
||||
tmpDir, err := ctx.TempDir("test1")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(strings.HasPrefix(tmpDir, filepath.Join(os.TempDir(), "tmpluet"))).To(BeTrue())
|
||||
Expect(fileHelper.Exists(tmpDir)).To(BeTrue())
|
||||
@@ -72,11 +105,7 @@ var _ = Describe("Config", func() {
|
||||
})
|
||||
|
||||
It("Create Temporary file", func() {
|
||||
ctx := types.NewContext()
|
||||
|
||||
ctx.Config.GetSystem().TmpDirBase = os.TempDir() + "/tmpluet"
|
||||
|
||||
tmpFile, err := ctx.Config.GetSystem().TempFile("testfile1")
|
||||
tmpFile, err := ctx.TempFile("testfile1")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(strings.HasPrefix(tmpFile.Name(), filepath.Join(os.TempDir(), "tmpluet"))).To(BeTrue())
|
||||
Expect(fileHelper.Exists(tmpFile.Name())).To(BeTrue())
|
||||
|
@@ -15,417 +15,16 @@
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
type Context interface {
|
||||
Logger
|
||||
GarbageCollector
|
||||
GetConfig() LuetConfig
|
||||
Copy() Context
|
||||
// SetAnnotation sets generic annotations to hold in a context
|
||||
SetAnnotation(s string, i interface{})
|
||||
|
||||
"github.com/kyokomi/emoji"
|
||||
"github.com/mudler/luet/pkg/helpers/terminal"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pterm/pterm"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
// GetAnnotation gets generic annotations to hold in a context
|
||||
GetAnnotation(s string) interface{}
|
||||
|
||||
const (
|
||||
ErrorLevel LogLevel = "error"
|
||||
WarningLevel LogLevel = "warning"
|
||||
InfoLevel LogLevel = "info"
|
||||
SuccessLevel LogLevel = "success"
|
||||
FatalLevel LogLevel = "fatal"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
context.Context
|
||||
Config *LuetConfig
|
||||
IsTerminal bool
|
||||
NoSpinner bool
|
||||
name string
|
||||
|
||||
s *pterm.SpinnerPrinter
|
||||
spinnerLock *sync.Mutex
|
||||
z *zap.Logger
|
||||
ProgressBar *pterm.ProgressbarPrinter
|
||||
}
|
||||
|
||||
func NewContext() *Context {
|
||||
return &Context{
|
||||
spinnerLock: &sync.Mutex{},
|
||||
IsTerminal: terminal.IsTerminal(os.Stdout),
|
||||
Config: &LuetConfig{
|
||||
ConfigFromHost: true,
|
||||
Logging: LuetLoggingConfig{},
|
||||
General: LuetGeneralConfig{},
|
||||
System: LuetSystemConfig{
|
||||
DatabasePath: filepath.Join("var", "db", "packages"),
|
||||
TmpDirBase: filepath.Join(os.TempDir(), "tmpluet")},
|
||||
Solver: LuetSolverOptions{},
|
||||
},
|
||||
s: pterm.DefaultSpinner.WithShowTimer(false).WithRemoveWhenDone(true),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) WithName(name string) *Context {
|
||||
newc := c.Copy()
|
||||
newc.name = name
|
||||
return newc
|
||||
}
|
||||
|
||||
func (c *Context) Copy() *Context {
|
||||
|
||||
configCopy := *c.Config
|
||||
configCopy.System = *c.Config.GetSystem()
|
||||
configCopy.General = *c.Config.GetGeneral()
|
||||
configCopy.Logging = *c.Config.GetLogging()
|
||||
|
||||
ctx := *c
|
||||
ctxCopy := &ctx
|
||||
ctxCopy.Config = &configCopy
|
||||
|
||||
return ctxCopy
|
||||
}
|
||||
|
||||
// GetTerminalSize returns the width and the height of the active terminal.
|
||||
func (c *Context) GetTerminalSize() (width, height int, err error) {
|
||||
w, h, err := term.GetSize(int(os.Stdout.Fd()))
|
||||
if w <= 0 {
|
||||
w = 0
|
||||
}
|
||||
if h <= 0 {
|
||||
h = 0
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.New("size not detectable")
|
||||
}
|
||||
return w, h, err
|
||||
}
|
||||
|
||||
func (c *Context) Init() (err error) {
|
||||
if c.IsTerminal {
|
||||
if !c.Config.Logging.Color {
|
||||
c.Debug("Disabling colors")
|
||||
c.NoColor()
|
||||
}
|
||||
} else {
|
||||
c.Debug("Not a terminal, disabling colors")
|
||||
c.NoColor()
|
||||
}
|
||||
|
||||
if c.Config.General.Quiet {
|
||||
c.NoColor()
|
||||
pterm.DisableStyling()
|
||||
}
|
||||
|
||||
c.Debug("Colors", c.Config.GetLogging().Color)
|
||||
c.Debug("Logging level", c.Config.GetLogging().Level)
|
||||
c.Debug("Debug mode", c.Config.GetGeneral().Debug)
|
||||
|
||||
if c.Config.GetLogging().EnableLogFile && c.Config.GetLogging().Path != "" {
|
||||
// Init zap logger
|
||||
err = c.InitZap()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Load repositories
|
||||
err = c.Config.LoadRepositories(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Context) NoColor() {
|
||||
pterm.DisableColor()
|
||||
}
|
||||
|
||||
func (c *Context) Ask() bool {
|
||||
var input string
|
||||
|
||||
c.Info("Do you want to continue with this operation? [y/N]: ")
|
||||
_, err := fmt.Scanln(&input)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
input = strings.ToLower(input)
|
||||
|
||||
if input == "y" || input == "yes" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Context) InitZap() error {
|
||||
var err error
|
||||
if c.z == nil {
|
||||
// TODO: test permission for open logfile.
|
||||
cfg := zap.NewProductionConfig()
|
||||
cfg.OutputPaths = []string{c.Config.GetLogging().Path}
|
||||
cfg.Level = c.Config.GetLogging().Level.ZapLevel()
|
||||
cfg.ErrorOutputPaths = []string{}
|
||||
if c.Config.GetLogging().JsonFormat {
|
||||
cfg.Encoding = "json"
|
||||
} else {
|
||||
cfg.Encoding = "console"
|
||||
}
|
||||
cfg.DisableCaller = true
|
||||
cfg.DisableStacktrace = true
|
||||
cfg.EncoderConfig.TimeKey = "time"
|
||||
cfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
|
||||
c.z, err = cfg.Build()
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, "Error on initialize file logger: "+err.Error()+"\n")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Spinner starts the spinner
|
||||
func (c *Context) Spinner() {
|
||||
if !c.IsTerminal || c.NoSpinner {
|
||||
return
|
||||
}
|
||||
|
||||
c.spinnerLock.Lock()
|
||||
defer c.spinnerLock.Unlock()
|
||||
var confLevel int
|
||||
if c.Config.GetGeneral().Debug {
|
||||
confLevel = 3
|
||||
} else {
|
||||
confLevel = c.Config.GetLogging().Level.ToNumber()
|
||||
}
|
||||
if 2 > confLevel {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.s.IsActive {
|
||||
c.s, _ = c.s.Start()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) Screen(text string) {
|
||||
pterm.DefaultHeader.WithBackgroundStyle(pterm.NewStyle(pterm.BgLightBlue)).WithMargin(2).Println(text)
|
||||
//pterm.DefaultCenter.Print(pterm.DefaultHeader.WithFullWidth().WithBackgroundStyle(pterm.NewStyle(pterm.BgLightBlue)).WithMargin(10).Sprint(text))
|
||||
}
|
||||
|
||||
func (c *Context) SpinnerText(suffix, prefix string) {
|
||||
if !c.IsTerminal || c.NoSpinner {
|
||||
return
|
||||
}
|
||||
|
||||
c.spinnerLock.Lock()
|
||||
defer c.spinnerLock.Unlock()
|
||||
if c.Config.GetGeneral().Debug {
|
||||
fmt.Printf("%s %s\n",
|
||||
suffix, prefix,
|
||||
)
|
||||
} else {
|
||||
c.s.UpdateText(suffix + prefix)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) SpinnerStop() {
|
||||
if !c.IsTerminal {
|
||||
return
|
||||
}
|
||||
|
||||
c.spinnerLock.Lock()
|
||||
defer c.spinnerLock.Unlock()
|
||||
var confLevel int
|
||||
if c.Config.GetGeneral().Debug {
|
||||
confLevel = 3
|
||||
} else {
|
||||
confLevel = c.Config.GetLogging().Level.ToNumber()
|
||||
}
|
||||
if 2 > confLevel {
|
||||
return
|
||||
}
|
||||
if c.s != nil {
|
||||
c.s.Success()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) log2File(level LogLevel, msg string) {
|
||||
switch level {
|
||||
case FatalLevel:
|
||||
c.z.Fatal(msg)
|
||||
case ErrorLevel:
|
||||
c.z.Error(msg)
|
||||
case WarningLevel:
|
||||
c.z.Warn(msg)
|
||||
case InfoLevel, SuccessLevel:
|
||||
c.z.Info(msg)
|
||||
default:
|
||||
c.z.Debug(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) Msg(level LogLevel, ln bool, msg ...interface{}) {
|
||||
var message string
|
||||
var confLevel, msgLevel int
|
||||
|
||||
if c.Config.GetGeneral().Debug {
|
||||
confLevel = 3
|
||||
pterm.EnableDebugMessages()
|
||||
} else {
|
||||
confLevel = c.Config.GetLogging().Level.ToNumber()
|
||||
}
|
||||
msgLevel = level.ToNumber()
|
||||
|
||||
if msgLevel > confLevel {
|
||||
return
|
||||
}
|
||||
|
||||
for _, m := range msg {
|
||||
message += " " + fmt.Sprintf("%v", m)
|
||||
}
|
||||
|
||||
// Color message
|
||||
levelMsg := message
|
||||
|
||||
if c.Config.GetLogging().Color {
|
||||
switch level {
|
||||
case WarningLevel:
|
||||
levelMsg = pterm.LightYellow(":construction: warning" + message)
|
||||
case InfoLevel:
|
||||
levelMsg = message
|
||||
case SuccessLevel:
|
||||
levelMsg = pterm.LightGreen(message)
|
||||
case ErrorLevel:
|
||||
levelMsg = pterm.Red(message)
|
||||
default:
|
||||
levelMsg = pterm.Blue(message)
|
||||
}
|
||||
}
|
||||
|
||||
// Strip emoji if needed
|
||||
if c.Config.GetLogging().EnableEmoji && c.IsTerminal {
|
||||
levelMsg = emoji.Sprint(levelMsg)
|
||||
} else {
|
||||
re := regexp.MustCompile(`[:][\w]+[:]`)
|
||||
levelMsg = re.ReplaceAllString(levelMsg, "")
|
||||
}
|
||||
|
||||
if c.name != "" {
|
||||
levelMsg = fmt.Sprintf("[%s] %s", c.name, levelMsg)
|
||||
}
|
||||
|
||||
if c.z != nil {
|
||||
c.log2File(level, message)
|
||||
}
|
||||
|
||||
// Print the message based on the level
|
||||
switch level {
|
||||
case SuccessLevel:
|
||||
if ln {
|
||||
pterm.Success.Println(levelMsg)
|
||||
} else {
|
||||
pterm.Success.Print(levelMsg)
|
||||
}
|
||||
case InfoLevel:
|
||||
if ln {
|
||||
pterm.Info.Println(levelMsg)
|
||||
} else {
|
||||
pterm.Info.Print(levelMsg)
|
||||
}
|
||||
case WarningLevel:
|
||||
if ln {
|
||||
pterm.Warning.Println(levelMsg)
|
||||
} else {
|
||||
pterm.Warning.Print(levelMsg)
|
||||
}
|
||||
case ErrorLevel:
|
||||
if ln {
|
||||
pterm.Error.Println(levelMsg)
|
||||
} else {
|
||||
pterm.Error.Print(levelMsg)
|
||||
}
|
||||
case FatalLevel:
|
||||
if ln {
|
||||
pterm.Fatal.Println(levelMsg)
|
||||
} else {
|
||||
pterm.Fatal.Print(levelMsg)
|
||||
}
|
||||
default:
|
||||
if ln {
|
||||
pterm.Debug.Println(levelMsg)
|
||||
} else {
|
||||
pterm.Debug.Print(levelMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) Warning(mess ...interface{}) {
|
||||
c.Msg("warning", true, mess...)
|
||||
if c.Config.GetGeneral().FatalWarns {
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) Debug(mess ...interface{}) {
|
||||
pc, file, line, ok := runtime.Caller(1)
|
||||
if ok {
|
||||
mess = append([]interface{}{fmt.Sprintf("(%s:#%d:%v)",
|
||||
path.Base(file), line, runtime.FuncForPC(pc).Name())}, mess...)
|
||||
}
|
||||
c.Msg("debug", true, mess...)
|
||||
}
|
||||
|
||||
func (c *Context) Info(mess ...interface{}) {
|
||||
c.Msg("info", true, mess...)
|
||||
}
|
||||
|
||||
func (c *Context) Success(mess ...interface{}) {
|
||||
c.Msg("success", true, mess...)
|
||||
}
|
||||
|
||||
func (c *Context) Error(mess ...interface{}) {
|
||||
c.Msg("error", true, mess...)
|
||||
}
|
||||
|
||||
func (c *Context) Fatal(mess ...interface{}) {
|
||||
c.Error(mess...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
type LogLevel string
|
||||
|
||||
func (level LogLevel) ToNumber() int {
|
||||
switch level {
|
||||
case ErrorLevel, FatalLevel:
|
||||
return 0
|
||||
case WarningLevel:
|
||||
return 1
|
||||
case InfoLevel, SuccessLevel:
|
||||
return 2
|
||||
default: // debug
|
||||
return 3
|
||||
}
|
||||
}
|
||||
|
||||
func (level LogLevel) ZapLevel() zap.AtomicLevel {
|
||||
switch level {
|
||||
case FatalLevel:
|
||||
return zap.NewAtomicLevelAt(zap.FatalLevel)
|
||||
case ErrorLevel:
|
||||
return zap.NewAtomicLevelAt(zap.ErrorLevel)
|
||||
case WarningLevel:
|
||||
return zap.NewAtomicLevelAt(zap.WarnLevel)
|
||||
case InfoLevel, SuccessLevel:
|
||||
return zap.NewAtomicLevelAt(zap.InfoLevel)
|
||||
default:
|
||||
return zap.NewAtomicLevelAt(zap.DebugLevel)
|
||||
}
|
||||
WithLoggingContext(s string) Context
|
||||
}
|
||||
|
25
pkg/api/core/types/garbagecollector.go
Normal file
25
pkg/api/core/types/garbagecollector.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// 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
|
||||
|
||||
import "os"
|
||||
|
||||
type GarbageCollector interface {
|
||||
Clean() error
|
||||
TempDir(pattern string) (string, error)
|
||||
TempFile(s string) (*os.File, error)
|
||||
String() string
|
||||
}
|
41
pkg/api/core/types/logger.go
Normal file
41
pkg/api/core/types/logger.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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
|
||||
|
||||
// Logger is a standard logging interface
|
||||
type Logger interface {
|
||||
Info(...interface{})
|
||||
Success(...interface{})
|
||||
Warning(...interface{})
|
||||
Warn(...interface{})
|
||||
Debug(...interface{})
|
||||
Error(...interface{})
|
||||
Fatal(...interface{})
|
||||
Panic(...interface{})
|
||||
Trace(...interface{})
|
||||
Infof(string, ...interface{})
|
||||
Warnf(string, ...interface{})
|
||||
Debugf(string, ...interface{})
|
||||
Errorf(string, ...interface{})
|
||||
Fatalf(string, ...interface{})
|
||||
Panicf(string, ...interface{})
|
||||
Tracef(string, ...interface{})
|
||||
|
||||
SpinnerStop()
|
||||
Spinner()
|
||||
Ask() bool
|
||||
Screen(string)
|
||||
}
|
Reference in New Issue
Block a user