mirror of
https://github.com/mudler/luet.git
synced 2025-09-03 00:06:36 +00:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
73c6cff15b | ||
|
65892f9bfc | ||
|
315bfb5a54 | ||
|
57c8236184 | ||
|
4d60795fdc | ||
|
6b45b1d61c | ||
|
5eb5a42bf7 | ||
|
0bacdc75f2 | ||
|
917d0935ad |
@@ -30,7 +30,7 @@ var cfgFile string
|
||||
var Verbose bool
|
||||
|
||||
const (
|
||||
LuetCLIVersion = "0.19.0"
|
||||
LuetCLIVersion = "0.19.2"
|
||||
LuetEnvPrefix = "LUET"
|
||||
)
|
||||
|
||||
|
@@ -122,6 +122,7 @@ func setDefaults(viper *viper.Viper) {
|
||||
viper.SetDefault("general.debug", false)
|
||||
viper.SetDefault("general.show_build_output", false)
|
||||
viper.SetDefault("general.fatal_warnings", false)
|
||||
viper.SetDefault("general.http_timeout", 360)
|
||||
|
||||
u, err := user.Current()
|
||||
// os/user doesn't work in from scratch environments
|
||||
@@ -178,6 +179,7 @@ func InitViper(ctx *types.Context, RootCmd *cobra.Command) {
|
||||
}
|
||||
pflags.Bool("same-owner", ctx.Config.GetGeneral().SameOwner, "Maintain same owner on uncompress.")
|
||||
pflags.Int("concurrency", runtime.NumCPU(), "Concurrency")
|
||||
pflags.Int("http-timeout", ctx.Config.General.HTTPTimeout, "Default timeout for http(s) requests")
|
||||
|
||||
viper.BindPFlag("logging.color", pflags.Lookup("color"))
|
||||
viper.BindPFlag("logging.enable_emoji", pflags.Lookup("emoji"))
|
||||
@@ -189,6 +191,7 @@ func InitViper(ctx *types.Context, RootCmd *cobra.Command) {
|
||||
viper.BindPFlag("general.fatal_warnings", pflags.Lookup("fatal"))
|
||||
viper.BindPFlag("general.same_owner", pflags.Lookup("same-owner"))
|
||||
viper.BindPFlag("plugin", pflags.Lookup("plugin"))
|
||||
viper.BindPFlag("general.http_timeout", pflags.Lookup("http-timeout"))
|
||||
|
||||
// Currently I maintain this only from cli.
|
||||
viper.BindPFlag("no_spinner", pflags.Lookup("no-spinner"))
|
||||
|
@@ -60,6 +60,7 @@ type LuetGeneralConfig struct {
|
||||
Debug bool `yaml:"debug,omitempty" mapstructure:"debug"`
|
||||
ShowBuildOutput bool `yaml:"show_build_output,omitempty" mapstructure:"show_build_output"`
|
||||
FatalWarns bool `yaml:"fatal_warnings,omitempty" mapstructure:"fatal_warnings"`
|
||||
HTTPTimeout int `yaml:"http_timeout,omitempty" mapstructure:"http_timeout"`
|
||||
}
|
||||
|
||||
type LuetSolverOptions struct {
|
||||
|
@@ -28,9 +28,11 @@ import (
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -48,13 +50,16 @@ type Context struct {
|
||||
NoSpinner bool
|
||||
|
||||
s *pterm.SpinnerPrinter
|
||||
spinnerLock sync.Mutex
|
||||
spinnerLock *sync.Mutex
|
||||
z *zap.Logger
|
||||
AreaPrinter *pterm.AreaPrinter
|
||||
ProgressBar *pterm.ProgressbarPrinter
|
||||
}
|
||||
|
||||
func NewContext() *Context {
|
||||
return &Context{
|
||||
IsTerminal: terminal.IsTerminal(os.Stdout),
|
||||
spinnerLock: &sync.Mutex{},
|
||||
IsTerminal: terminal.IsTerminal(os.Stdout),
|
||||
Config: &LuetConfig{
|
||||
ConfigFromHost: true,
|
||||
Logging: LuetLoggingConfig{},
|
||||
@@ -68,6 +73,35 @@ func NewContext() *Context {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -254,7 +288,9 @@ func (c *Context) Msg(level LogLevel, ln bool, msg ...interface{}) {
|
||||
switch level {
|
||||
case WarningLevel:
|
||||
levelMsg = pterm.LightYellow(":construction: warning" + message)
|
||||
case InfoLevel, SuccessLevel:
|
||||
case InfoLevel:
|
||||
levelMsg = message
|
||||
case SuccessLevel:
|
||||
levelMsg = pterm.LightGreen(message)
|
||||
case ErrorLevel:
|
||||
levelMsg = pterm.Red(message)
|
||||
|
@@ -37,6 +37,8 @@ type LuetRepository struct {
|
||||
Verify bool `json:"verify,omitempty" yaml:"verify,omitempty" mapstructure:"verify"`
|
||||
Arch string `json:"arch,omitempty" yaml:"arch,omitempty" mapstructure:"arch"`
|
||||
|
||||
ReferenceID string `json:"reference,omitempty" yaml:"reference,omitempty" mapstructure:"reference"`
|
||||
|
||||
// Incremented value that identify revision of the repository in a user-friendly way.
|
||||
Revision int `json:"revision,omitempty" yaml:"-" mapstructure:"-"`
|
||||
// Epoch time in seconds
|
||||
|
@@ -56,7 +56,7 @@ func (s *SimpleDocker) BuildImage(opts Options) error {
|
||||
return err
|
||||
}
|
||||
|
||||
s.ctx.Info(":whale: Building image " + name + " done")
|
||||
s.ctx.Success(":whale: Building image " + name + " done")
|
||||
|
||||
bus.Manager.Publish(bus.EventImagePostBuild, opts)
|
||||
|
||||
@@ -70,7 +70,7 @@ func (s *SimpleDocker) CopyImage(src, dst string) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed tagging image: "+string(out))
|
||||
}
|
||||
s.ctx.Info(":whale: Tagged image:", src, "->", dst)
|
||||
s.ctx.Success(":whale: Tagged image:", src, "->", dst)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ func (s *SimpleDocker) DownloadImage(opts Options) error {
|
||||
return errors.Wrap(err, "Failed pulling image: "+string(out))
|
||||
}
|
||||
|
||||
s.ctx.Info(":whale: Downloaded image:", name)
|
||||
s.ctx.Success(":whale: Downloaded image:", name)
|
||||
bus.Manager.Publish(bus.EventImagePostPull, opts)
|
||||
|
||||
return nil
|
||||
@@ -120,7 +120,7 @@ func (s *SimpleDocker) RemoveImage(opts Options) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed removing image: "+string(out))
|
||||
}
|
||||
s.ctx.Info(":whale: Removed image:", name)
|
||||
s.ctx.Success(":whale: Removed image:", name)
|
||||
//Info(string(out))
|
||||
return nil
|
||||
}
|
||||
@@ -137,7 +137,7 @@ func (s *SimpleDocker) Push(opts Options) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed pushing image: "+string(out))
|
||||
}
|
||||
s.ctx.Info(":whale: Pushed image:", name)
|
||||
s.ctx.Success(":whale: Pushed image:", name)
|
||||
bus.Manager.Publish(bus.EventImagePostPush, opts)
|
||||
|
||||
//Info(string(out))
|
||||
|
@@ -447,7 +447,7 @@ func (cs *LuetCompiler) genArtifact(p *compilerspec.LuetCompilationSpec, builder
|
||||
if err != nil {
|
||||
return a, errors.Wrap(err, "Failed while writing metadata file")
|
||||
}
|
||||
cs.Options.Context.Info(pkgTag, " :white_check_mark: done (empty virtual package)")
|
||||
cs.Options.Context.Success(pkgTag, " :white_check_mark: done (empty virtual package)")
|
||||
return a, nil
|
||||
}
|
||||
|
||||
@@ -477,7 +477,7 @@ func (cs *LuetCompiler) genArtifact(p *compilerspec.LuetCompilationSpec, builder
|
||||
if err != nil {
|
||||
return a, errors.Wrap(err, "Failed while writing metadata file")
|
||||
}
|
||||
cs.Options.Context.Info(pkgTag, " :white_check_mark: Done")
|
||||
cs.Options.Context.Success(pkgTag, " :white_check_mark: Done")
|
||||
|
||||
return a, nil
|
||||
}
|
||||
@@ -965,7 +965,7 @@ func (cs *LuetCompiler) resolveMultiStageImages(concurrency int, keepPermissions
|
||||
Source: c.Source,
|
||||
Destination: c.Destination,
|
||||
})
|
||||
cs.Options.Context.Info(copyTag2, ":white_check_mark: Done")
|
||||
cs.Options.Context.Success(copyTag2, ":white_check_mark: Done")
|
||||
} else {
|
||||
resolvedCopyFields = append(resolvedCopyFields, c)
|
||||
}
|
||||
@@ -1128,7 +1128,7 @@ func (cs *LuetCompiler) compile(concurrency int, keepPermissions bool, generateF
|
||||
|
||||
a.PackageCacheImage = assertion.Hash.PackageHash
|
||||
|
||||
cs.Options.Context.Info(pkgTag, ":white_check_mark: Done")
|
||||
cs.Options.Context.Success(pkgTag, ":white_check_mark: Done")
|
||||
|
||||
bus.Manager.Publish(bus.EventPackagePostBuild, struct {
|
||||
CompileSpec *compilerspec.LuetCompilationSpec
|
||||
|
@@ -75,8 +75,11 @@ func (c *DockerClient) DownloadArtifact(a *artifact.PackageArtifact) (*artifact.
|
||||
// - Check how verification is done when calling DownloadArtifact outside, similarly we need to check DownloadFile, and how verification
|
||||
// is done in such cases (see repository.go)
|
||||
|
||||
// We discard checksum, that are checked while during pull and unpack by containerd
|
||||
resultingArtifact.Checksums = artifact.Checksums{}
|
||||
|
||||
// Check if file is already in cache
|
||||
fileName, err := c.Cache.Get(a)
|
||||
fileName, err := c.Cache.Get(resultingArtifact)
|
||||
// Check if file is already in cache
|
||||
if err == nil {
|
||||
resultingArtifact = a
|
||||
@@ -118,8 +121,6 @@ func (c *DockerClient) DownloadArtifact(a *artifact.PackageArtifact) (*artifact.
|
||||
c.context.Info(fmt.Sprintf("Size: %s", units.BytesSize(float64(info.Target.Size))))
|
||||
c.context.Debug("\nCompressing result ", filepath.Join(temp), "to", tempArtifact.Name())
|
||||
|
||||
// We discard checksum, that are checked while during pull and unpack
|
||||
resultingArtifact.Checksums = artifact.Checksums{}
|
||||
resultingArtifact.Path = tempArtifact.Name() // First set to cache file
|
||||
err = resultingArtifact.Compress(temp, 1)
|
||||
if err != nil {
|
||||
|
@@ -23,7 +23,6 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/mudler/luet/pkg/api/core/types"
|
||||
@@ -38,8 +37,6 @@ type HttpClient struct {
|
||||
RepoData RepoData
|
||||
Cache *artifact.ArtifactCache
|
||||
context *types.Context
|
||||
|
||||
ProgressBarArea *pterm.AreaPrinter
|
||||
}
|
||||
|
||||
func NewHttpClient(r RepoData, ctx *types.Context) *HttpClient {
|
||||
@@ -50,20 +47,11 @@ func NewHttpClient(r RepoData, ctx *types.Context) *HttpClient {
|
||||
}
|
||||
}
|
||||
|
||||
func NewGrabClient() *grab.Client {
|
||||
httpTimeout := 360
|
||||
timeout := os.Getenv("HTTP_TIMEOUT")
|
||||
if timeout != "" {
|
||||
timeoutI, err := strconv.Atoi(timeout)
|
||||
if err == nil {
|
||||
httpTimeout = timeoutI
|
||||
}
|
||||
}
|
||||
|
||||
func NewGrabClient(timeout int) *grab.Client {
|
||||
return &grab.Client{
|
||||
UserAgent: "grab",
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: time.Duration(httpTimeout) * time.Second,
|
||||
Timeout: time.Duration(timeout) * time.Second,
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
},
|
||||
@@ -103,7 +91,7 @@ func (c *HttpClient) DownloadFile(p string) (string, error) {
|
||||
}
|
||||
defer os.RemoveAll(temp)
|
||||
|
||||
client := NewGrabClient()
|
||||
client := NewGrabClient(c.context.Config.General.HTTPTimeout)
|
||||
|
||||
for _, uri := range c.RepoData.Urls {
|
||||
file, err = c.context.Config.GetSystem().TempFile("HttpClient")
|
||||
@@ -126,11 +114,16 @@ func (c *HttpClient) DownloadFile(p string) (string, error) {
|
||||
}
|
||||
|
||||
resp := client.Do(req)
|
||||
pb := pterm.DefaultProgressbar.WithTotal(int(resp.Size()))
|
||||
if c.ProgressBarArea != nil {
|
||||
pb = pb.WithPrintTogether(c.ProgressBarArea)
|
||||
|
||||
// Initialize a progressbar only if we have one in the current context
|
||||
var pb *pterm.ProgressbarPrinter
|
||||
if c.context.ProgressBar != nil {
|
||||
pb = pterm.DefaultProgressbar.WithTotal(int(resp.Size()))
|
||||
if c.context.AreaPrinter != nil {
|
||||
pb = pb.WithPrintTogether(c.context.AreaPrinter)
|
||||
}
|
||||
pb, _ = pb.WithTitle(filepath.Base(resp.Request.HTTPRequest.URL.RequestURI())).Start()
|
||||
}
|
||||
pb, _ = pb.WithTitle(filepath.Base(resp.Request.HTTPRequest.URL.RequestURI())).Start()
|
||||
// start download loop
|
||||
t := time.NewTicker(500 * time.Millisecond)
|
||||
defer t.Stop()
|
||||
@@ -140,11 +133,15 @@ func (c *HttpClient) DownloadFile(p string) (string, error) {
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
// bar.Set64(resp.BytesComplete())
|
||||
//pb.Increment()
|
||||
pb.Increment().Current = int(resp.BytesComplete())
|
||||
// update the progress bar
|
||||
if pb != nil {
|
||||
pb.Increment().Current = int(resp.BytesComplete())
|
||||
}
|
||||
case <-resp.Done:
|
||||
pb.Increment().Current = int(resp.BytesComplete())
|
||||
// update the progress bar
|
||||
if pb != nil {
|
||||
pb.Increment().Current = int(resp.BytesComplete())
|
||||
}
|
||||
// download is complete
|
||||
break download_loop
|
||||
}
|
||||
@@ -157,7 +154,11 @@ func (c *HttpClient) DownloadFile(p string) (string, error) {
|
||||
c.context.Info("Downloaded", p, "of",
|
||||
fmt.Sprintf("%.2f", (float64(resp.BytesComplete())/1000)/1000), "MB (",
|
||||
fmt.Sprintf("%.2f", (float64(resp.BytesPerSecond())/1024)/1024), "MiB/s )")
|
||||
pb.Stop()
|
||||
|
||||
if pb != nil {
|
||||
// stop the progressbar if active
|
||||
pb.Stop()
|
||||
}
|
||||
//bar.Finish()
|
||||
downloaded = true
|
||||
break
|
||||
|
@@ -32,7 +32,6 @@ import (
|
||||
"github.com/mudler/luet/pkg/helpers"
|
||||
fileHelper "github.com/mudler/luet/pkg/helpers/file"
|
||||
"github.com/mudler/luet/pkg/helpers/match"
|
||||
"github.com/mudler/luet/pkg/installer/client"
|
||||
pkg "github.com/mudler/luet/pkg/package"
|
||||
"github.com/mudler/luet/pkg/solver"
|
||||
"github.com/pterm/pterm"
|
||||
@@ -566,21 +565,30 @@ func (l *LuetInstaller) download(syncedRepos Repositories, toDownload map[string
|
||||
|
||||
var wg = new(sync.WaitGroup)
|
||||
|
||||
area, _ := pterm.DefaultArea.Start()
|
||||
ctx := l.Options.Context.Copy()
|
||||
|
||||
p, _ := pterm.DefaultProgressbar.WithPrintTogether(area).WithTotal(len(toDownload)).WithTitle("Downloading packages").Start()
|
||||
// Check if the terminal is big enough to display a progress bar
|
||||
// https://github.com/pterm/pterm/blob/4c725e56bfd9eb38e1c7b9dec187b50b93baa8bd/progressbar_printer.go#L190
|
||||
w, _, err := ctx.GetTerminalSize()
|
||||
if ctx.IsTerminal && err == nil && w > 100 {
|
||||
area, _ := pterm.DefaultArea.Start()
|
||||
p, _ := pterm.DefaultProgressbar.WithPrintTogether(area).WithTotal(len(toDownload)).WithTitle("Downloading packages").Start()
|
||||
|
||||
ctx.AreaPrinter = area
|
||||
ctx.ProgressBar = p
|
||||
defer area.Stop()
|
||||
}
|
||||
|
||||
// Download
|
||||
for i := 0; i < l.Options.Concurrency; i++ {
|
||||
wg.Add(1)
|
||||
go l.downloadWorker(i, wg, all, p, area)
|
||||
go l.downloadWorker(i, wg, all, ctx)
|
||||
}
|
||||
for _, c := range toDownload {
|
||||
all <- c
|
||||
}
|
||||
close(all)
|
||||
wg.Wait()
|
||||
area.Stop()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -772,7 +780,7 @@ func (l *LuetInstaller) checkFileconflicts(toInstall map[string]ArtifactMatch, c
|
||||
|
||||
filesToInstall := []string{}
|
||||
for _, m := range toInstall {
|
||||
a, err := l.downloadPackage(m, nil)
|
||||
a, err := l.downloadPackage(m, l.Options.Context)
|
||||
if err != nil && !l.Options.Force {
|
||||
return errors.Wrap(err, "Failed downloading package")
|
||||
}
|
||||
@@ -867,14 +875,9 @@ func (l *LuetInstaller) install(o Option, syncedRepos Repositories, toInstall ma
|
||||
return s.ExecuteFinalizers(l.Options.Context, toFinalize)
|
||||
}
|
||||
|
||||
func (l *LuetInstaller) downloadPackage(a ArtifactMatch, area *pterm.AreaPrinter) (*artifact.PackageArtifact, error) {
|
||||
func (l *LuetInstaller) downloadPackage(a ArtifactMatch, ctx *types.Context) (*artifact.PackageArtifact, error) {
|
||||
|
||||
cli := a.Repository.Client(l.Options.Context)
|
||||
|
||||
switch v := cli.(type) {
|
||||
case *client.HttpClient:
|
||||
v.ProgressBarArea = area
|
||||
}
|
||||
cli := a.Repository.Client(ctx)
|
||||
|
||||
artifact, err := cli.DownloadArtifact(a.Artifact)
|
||||
if err != nil {
|
||||
@@ -890,7 +893,7 @@ func (l *LuetInstaller) downloadPackage(a ArtifactMatch, area *pterm.AreaPrinter
|
||||
|
||||
func (l *LuetInstaller) installPackage(m ArtifactMatch, s *System) error {
|
||||
|
||||
a, err := l.downloadPackage(m, nil)
|
||||
a, err := l.downloadPackage(m, l.Options.Context)
|
||||
if err != nil && !l.Options.Force {
|
||||
return errors.Wrap(err, "Failed downloading package")
|
||||
}
|
||||
@@ -910,20 +913,21 @@ func (l *LuetInstaller) installPackage(m ArtifactMatch, s *System) error {
|
||||
return s.Database.SetPackageFiles(&pkg.PackageFile{PackageFingerprint: m.Package.GetFingerPrint(), Files: files})
|
||||
}
|
||||
|
||||
func (l *LuetInstaller) downloadWorker(i int, wg *sync.WaitGroup, c <-chan ArtifactMatch, pb *pterm.ProgressbarPrinter, area *pterm.AreaPrinter) error {
|
||||
func (l *LuetInstaller) downloadWorker(i int, wg *sync.WaitGroup, c <-chan ArtifactMatch, ctx *types.Context) error {
|
||||
defer wg.Done()
|
||||
|
||||
for p := range c {
|
||||
// TODO: Keep trace of what was added from the tar, and save it into system
|
||||
_, err := l.downloadPackage(p, area)
|
||||
_, err := l.downloadPackage(p, ctx)
|
||||
if err != nil {
|
||||
l.Options.Context.Error("Failed downloading package "+p.Package.GetName(), err.Error())
|
||||
return errors.Wrap(err, "Failed downloading package "+p.Package.GetName())
|
||||
} else {
|
||||
l.Options.Context.Success(":package: Package ", p.Package.HumanReadableString(), "downloaded")
|
||||
}
|
||||
pb.Increment()
|
||||
|
||||
if ctx.ProgressBar != nil {
|
||||
ctx.ProgressBar.Increment()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -539,6 +539,7 @@ func (r *LuetSystemRepository) AddMetadata(ctx *types.Context, repospec, dst str
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a, err := r.AddRepositoryFile(metaTmpDir, REPOFILE_META_KEY, dst, NewDefaultMetaRepositoryFile())
|
||||
if err != nil {
|
||||
return a, errors.Wrap(err, "Error met while adding archive to repository")
|
||||
@@ -558,7 +559,7 @@ func (r *LuetSystemRepository) AddMetadata(ctx *types.Context, repospec, dst str
|
||||
// AddTree adds a tree.Builder with the given key to the repository.
|
||||
// It will generate an artifact which will be then embedded in the repository manifest
|
||||
// It returns the generated artifacts and an error
|
||||
func (r *LuetSystemRepository) AddTree(ctx *types.Context, t tree.Builder, dst, key string, f LuetRepositoryFile) (*artifact.PackageArtifact, error) {
|
||||
func (r *LuetSystemRepository) AddTree(ctx *types.Context, t tree.Builder, dst, key string, defaults LuetRepositoryFile) (*artifact.PackageArtifact, error) {
|
||||
// Create tree and repository file
|
||||
archive, err := ctx.Config.GetSystem().TempDir("archive")
|
||||
if err != nil {
|
||||
@@ -570,13 +571,70 @@ func (r *LuetSystemRepository) AddTree(ctx *types.Context, t tree.Builder, dst,
|
||||
return nil, errors.Wrap(err, "Error met while saving the tree")
|
||||
}
|
||||
|
||||
a, err := r.AddRepositoryFile(archive, key, dst, f)
|
||||
a, err := r.AddRepositoryFile(archive, key, dst, defaults)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error met while adding archive to repository")
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Snapshot creates a copy of the current repository index into dst.
|
||||
// The copy will be prefixed with "id".
|
||||
// This allows the clients to refer to old versions of the repository by using the reference_id
|
||||
func (r *LuetSystemRepository) Snapshot(id, dst string) (artifacts []*artifact.PackageArtifact, snapshotIndex string, err error) {
|
||||
|
||||
var snapshotFmt string = "%s-%s"
|
||||
|
||||
repospec := filepath.Join(dst, REPOSITORY_SPECFILE)
|
||||
snapshotIndex = filepath.Join(dst, fmt.Sprintf(snapshotFmt, id, REPOSITORY_SPECFILE))
|
||||
|
||||
err = fileHelper.CopyFile(repospec, filepath.Join(dst, snapshotIndex))
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "while copying repo spec")
|
||||
return
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(repospec)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
newRepoIndex := &LuetSystemRepository{}
|
||||
err = yaml.Unmarshal(b, newRepoIndex)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, key := range []string{REPOFILE_META_KEY, REPOFILE_TREE_KEY, REPOFILE_COMPILER_TREE_KEY} {
|
||||
var luetFile LuetRepositoryFile
|
||||
luetFile, err = r.GetRepositoryFile(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
newMetaFile := fmt.Sprintf(snapshotFmt, id, luetFile.FileName)
|
||||
err = fileHelper.CopyFile(filepath.Join(dst, luetFile.FileName), filepath.Join(dst, newMetaFile))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
m := &luetFile
|
||||
m.FileName = newMetaFile
|
||||
newRepoIndex.RepositoryFiles[key] = *m
|
||||
artifacts = append(artifacts, artifact.NewPackageArtifact(filepath.Join(dst, newMetaFile)))
|
||||
}
|
||||
|
||||
_, serialized := newRepoIndex.Serialize()
|
||||
|
||||
data, err := yaml.Marshal(serialized)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(snapshotIndex, data, os.ModePerm)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// AddRepositoryFile adds a path to a key in the repository manifest.
|
||||
// The path will be compressed, and a default File has to be passed in case there is no entry into
|
||||
// the repository manifest
|
||||
@@ -772,6 +830,14 @@ func (r *LuetSystemRepository) SyncBuildMetadata(ctx *types.Context, path string
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *LuetSystemRepository) referenceID() string {
|
||||
repositoryReferenceID := REPOSITORY_SPECFILE
|
||||
if r.ReferenceID != "" {
|
||||
repositoryReferenceID = r.ReferenceID
|
||||
}
|
||||
return repositoryReferenceID
|
||||
}
|
||||
|
||||
func (r *LuetSystemRepository) Sync(ctx *types.Context, force bool) (*LuetSystemRepository, error) {
|
||||
var repoUpdated bool = false
|
||||
var treefs, metafs string
|
||||
@@ -782,10 +848,12 @@ func (r *LuetSystemRepository) Sync(ctx *types.Context, force bool) (*LuetSystem
|
||||
return nil, errors.New("no client could be generated from repository")
|
||||
}
|
||||
|
||||
repositoryReferenceID := r.referenceID()
|
||||
|
||||
// Retrieve remote repository.yaml for retrieve revision and date
|
||||
file, err := c.DownloadFile(REPOSITORY_SPECFILE)
|
||||
file, err := c.DownloadFile(repositoryReferenceID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "while downloading "+REPOSITORY_SPECFILE)
|
||||
return nil, errors.Wrap(err, "while downloading "+repositoryReferenceID)
|
||||
}
|
||||
|
||||
repobasedir := ctx.Config.GetSystem().GetRepoDatabaseDirPath(r.GetName())
|
||||
@@ -799,7 +867,7 @@ func (r *LuetSystemRepository) Sync(ctx *types.Context, force bool) (*LuetSystem
|
||||
|
||||
if r.Cached {
|
||||
if !force {
|
||||
localRepo, _ := r.ReadSpecFile(filepath.Join(repobasedir, REPOSITORY_SPECFILE))
|
||||
localRepo, _ := r.ReadSpecFile(filepath.Join(repobasedir, repositoryReferenceID))
|
||||
if localRepo != nil {
|
||||
if localRepo.GetRevision() == downloadedRepoMeta.GetRevision() &&
|
||||
localRepo.GetLastUpdate() == downloadedRepoMeta.GetLastUpdate() {
|
||||
@@ -850,9 +918,9 @@ func (r *LuetSystemRepository) Sync(ctx *types.Context, force bool) (*LuetSystem
|
||||
|
||||
if r.Cached {
|
||||
// Copy updated repository.yaml file to repo dir now that the tree is synced.
|
||||
err = fileHelper.CopyFile(file, filepath.Join(repobasedir, REPOSITORY_SPECFILE))
|
||||
err = fileHelper.CopyFile(file, filepath.Join(repobasedir, repositoryReferenceID))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error on update "+REPOSITORY_SPECFILE)
|
||||
return nil, errors.Wrap(err, "Error on update "+repositoryReferenceID)
|
||||
}
|
||||
// Remove previous tree
|
||||
os.RemoveAll(treefs)
|
||||
|
@@ -136,7 +136,7 @@ func (d *dockerRepositoryGenerator) pushFileFromArtifact(a *artifact.PackageArti
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dockerRepositoryGenerator) pushRepoMetadata(repospec string, r *LuetSystemRepository) error {
|
||||
func (d *dockerRepositoryGenerator) pushRepoMetadata(repospec, tag string, r *LuetSystemRepository) error {
|
||||
// create temp dir for metafile
|
||||
metaDir, err := d.context.Config.GetSystem().TempDir("metadata")
|
||||
if err != nil {
|
||||
@@ -144,13 +144,13 @@ func (d *dockerRepositoryGenerator) pushRepoMetadata(repospec string, r *LuetSys
|
||||
}
|
||||
defer os.RemoveAll(metaDir) // clean up
|
||||
|
||||
tempRepoFile := filepath.Join(metaDir, REPOSITORY_SPECFILE+".tar")
|
||||
tempRepoFile := filepath.Join(metaDir, tag+".tar")
|
||||
if err := helpers.Tar(repospec, tempRepoFile); err != nil {
|
||||
return errors.Wrap(err, "Error met while archiving repository file")
|
||||
}
|
||||
|
||||
a := artifact.NewPackageArtifact(tempRepoFile)
|
||||
imageRepo := fmt.Sprintf("%s:%s", d.imagePrefix, REPOSITORY_SPECFILE)
|
||||
imageRepo := fmt.Sprintf("%s:%s", d.imagePrefix, tag)
|
||||
|
||||
if err := d.pushFileFromArtifact(a, imageRepo); err != nil {
|
||||
return errors.Wrap(err, "while pushing file from artifact")
|
||||
@@ -242,14 +242,7 @@ func (d *dockerRepositoryGenerator) Generate(r *LuetSystemRepository, imagePrefi
|
||||
return errors.Wrap(err, "error met while pushing compiler tree")
|
||||
}
|
||||
|
||||
// create temp dir for metafile
|
||||
metaDir, err := d.context.Config.GetSystem().TempDir("metadata")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error met while creating tempdir for metadata")
|
||||
}
|
||||
defer os.RemoveAll(metaDir) // clean up
|
||||
|
||||
a, err = r.AddMetadata(d.context, repospec, metaDir)
|
||||
a, err = r.AddMetadata(d.context, repospec, repoTemp)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed adding Metadata file to repository")
|
||||
}
|
||||
@@ -258,10 +251,28 @@ func (d *dockerRepositoryGenerator) Generate(r *LuetSystemRepository, imagePrefi
|
||||
return errors.Wrap(err, "error met while pushing docker image from artifact")
|
||||
}
|
||||
|
||||
if err := d.pushRepoMetadata(repospec, r); err != nil {
|
||||
if err := d.pushRepoMetadata(repospec, REPOSITORY_SPECFILE, r); err != nil {
|
||||
return errors.Wrap(err, "while pushing repository metadata tree")
|
||||
}
|
||||
|
||||
// Create a named snapshot and push it.
|
||||
// It edits the metadata pointing at the repository files associated with the snapshot
|
||||
// And copies the new files
|
||||
id := time.Now().Format("20060102150405")
|
||||
artifacts, snapshotRepoFile, err := r.Snapshot(id, repoTemp)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while creating snapshot")
|
||||
}
|
||||
if err := d.pushRepoMetadata(snapshotRepoFile, filepath.Base(snapshotRepoFile), r); err != nil {
|
||||
return errors.Wrap(err, "while pushing repository snapshot metadata tree")
|
||||
}
|
||||
|
||||
for _, a := range artifacts {
|
||||
if err := d.pushImageFromArtifact(a, d.b, false); err != nil {
|
||||
return errors.Wrap(err, "error met while pushing docker image from artifact")
|
||||
}
|
||||
}
|
||||
|
||||
bus.Manager.Publish(bus.EventRepositoryPostBuild, struct {
|
||||
Repo LuetSystemRepository
|
||||
Path string
|
||||
|
@@ -121,6 +121,13 @@ func (g *localRepositoryGenerator) Generate(r *LuetSystemRepository, dst string,
|
||||
return errors.Wrap(err, "failed adding Metadata file to repository")
|
||||
}
|
||||
|
||||
// Create named snapshot.
|
||||
// It edits the metadata pointing at the repository files associated with the snapshot
|
||||
// And copies the new files
|
||||
if _, _, err := r.Snapshot(time.Now().Format("20060102150405"), dst); err != nil {
|
||||
return errors.Wrap(err, "while creating snapshot")
|
||||
}
|
||||
|
||||
bus.Manager.Publish(bus.EventRepositoryPostBuild, struct {
|
||||
Repo LuetSystemRepository
|
||||
Path string
|
||||
|
@@ -218,6 +218,124 @@ urls:
|
||||
Expect(err).To(HaveOccurred()) // should throw error
|
||||
})
|
||||
|
||||
It("Generates snapshots", func() {
|
||||
|
||||
tmpdir, err := ioutil.TempDir("", "tree")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(tmpdir) // clean up
|
||||
|
||||
generalRecipe := tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false))
|
||||
|
||||
err = generalRecipe.Load("../../tests/fixtures/buildable")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
generalRecipe2 := tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false))
|
||||
|
||||
err = generalRecipe2.Load("../../tests/fixtures/finalizers")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(len(generalRecipe2.GetDatabase().GetPackages())).To(Equal(1))
|
||||
Expect(len(generalRecipe.GetDatabase().GetPackages())).To(Equal(3))
|
||||
|
||||
compiler2 := compiler.NewLuetCompiler(backend.NewSimpleDockerBackend(ctx), generalRecipe2.GetDatabase(), options.WithContext(ctx))
|
||||
spec2, err := compiler2.FromPackage(&pkg.DefaultPackage{Name: "alpine", Category: "seed", Version: "1.0"})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
compiler := compiler.NewLuetCompiler(backend.NewSimpleDockerBackend(ctx), generalRecipe.GetDatabase(), options.WithContext(ctx))
|
||||
|
||||
spec, err := compiler.FromPackage(&pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(spec.GetPackage().GetPath()).ToNot(Equal(""))
|
||||
Expect(spec2.GetPackage().GetPath()).ToNot(Equal(""))
|
||||
|
||||
tmpdir, err = ioutil.TempDir("", "tree")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(tmpdir) // clean up
|
||||
|
||||
Expect(spec.BuildSteps()).To(Equal([]string{"echo artifact5 > /test5", "echo artifact6 > /test6", "chmod +x generate.sh", "./generate.sh"}))
|
||||
Expect(spec.GetPreBuildSteps()).To(Equal([]string{"echo foo > /test", "echo bar > /test2"}))
|
||||
|
||||
spec.SetOutputPath(tmpdir)
|
||||
spec2.SetOutputPath(tmpdir)
|
||||
|
||||
artifact, err := compiler.Compile(false, spec)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(fileHelper.Exists(artifact.Path)).To(BeTrue())
|
||||
Expect(helpers.Untar(artifact.Path, tmpdir, false)).ToNot(HaveOccurred())
|
||||
|
||||
artifact2, err := compiler2.Compile(false, spec2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(fileHelper.Exists(artifact2.Path)).To(BeTrue())
|
||||
Expect(helpers.Untar(artifact2.Path, tmpdir, false)).ToNot(HaveOccurred())
|
||||
|
||||
Expect(fileHelper.Exists(spec.Rel("test5"))).To(BeTrue())
|
||||
Expect(fileHelper.Exists(spec.Rel("test6"))).To(BeTrue())
|
||||
|
||||
content1, err := fileHelper.Read(spec.Rel("test5"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
content2, err := fileHelper.Read(spec.Rel("test6"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(content1).To(Equal("artifact5\n"))
|
||||
Expect(content2).To(Equal("artifact6\n"))
|
||||
|
||||
// will contain both
|
||||
Expect(fileHelper.Exists(spec.Rel("b-test-1.0.package.tar"))).To(BeTrue())
|
||||
Expect(fileHelper.Exists(spec.Rel("b-test-1.0.metadata.yaml"))).To(BeTrue())
|
||||
Expect(fileHelper.Exists(spec2.Rel("alpine-seed-1.0.package.tar"))).To(BeTrue())
|
||||
Expect(fileHelper.Exists(spec2.Rel("alpine-seed-1.0.metadata.yaml"))).To(BeTrue())
|
||||
|
||||
repo, err := GenerateRepository(
|
||||
WithName("test"),
|
||||
WithDescription("description"),
|
||||
WithType("disk"),
|
||||
WithUrls(tmpdir),
|
||||
WithPriority(1),
|
||||
WithSource(tmpdir),
|
||||
FromMetadata(true), // Enabling from metadata makes the package visible
|
||||
WithTree("../../tests/fixtures/buildable"),
|
||||
WithContext(ctx),
|
||||
WithDatabase(pkg.NewInMemoryDatabase(false)),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(repo.GetName()).To(Equal("test"))
|
||||
Expect(fileHelper.Exists(spec.Rel(REPOSITORY_SPECFILE))).ToNot(BeTrue())
|
||||
Expect(fileHelper.Exists(spec.Rel(TREE_TARBALL + ".gz"))).ToNot(BeTrue())
|
||||
Expect(fileHelper.Exists(spec.Rel(REPOSITORY_METAFILE + ".tar"))).ToNot(BeTrue())
|
||||
err = repo.Write(ctx, tmpdir, false, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(fileHelper.Exists(spec.Rel(REPOSITORY_SPECFILE))).To(BeTrue())
|
||||
Expect(fileHelper.Exists(spec.Rel(TREE_TARBALL + ".gz"))).To(BeTrue())
|
||||
Expect(fileHelper.Exists(spec.Rel(REPOSITORY_METAFILE + ".tar"))).To(BeTrue())
|
||||
|
||||
artifacts, index, err := repo.Snapshot("foo", tmpdir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(artifacts)).To(Equal(3))
|
||||
Expect(index).To(ContainSubstring("foo-repository.yaml"))
|
||||
r := &LuetSystemRepository{}
|
||||
|
||||
r, err = r.ReadSpecFile(index)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(len(r.RepositoryFiles)).To(Equal(3))
|
||||
|
||||
for k, v := range r.RepositoryFiles {
|
||||
_, err := os.Stat(filepath.Join(tmpdir, "foo-compilertree.tar.gz"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
switch k {
|
||||
case REPOFILE_COMPILER_TREE_KEY:
|
||||
Expect(v.FileName).To(Equal("foo-compilertree.tar.gz"))
|
||||
case REPOFILE_META_KEY:
|
||||
Expect(v.FileName).To(Equal("foo-repository.meta.yaml.tar"))
|
||||
case REPOFILE_TREE_KEY:
|
||||
Expect(v.FileName).To(Equal("foo-tree.tar.gz"))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
It("Generate repository metadata of files referenced in a tree and from packages", func() {
|
||||
|
||||
tmpdir, err := ioutil.TempDir("", "tree")
|
||||
|
Reference in New Issue
Block a user