Compare commits

...

7 Commits

Author SHA1 Message Date
Ettore Di Giacinto
c58a462e79 🆕 Tag 0.20.8 2021-11-05 23:44:27 +01:00
Ettore Di Giacinto
1e78570c50 Allow to push final images while compiling
Add --push-final-images, --push-final-images-repository and
--push-final-images-force to luet build.

--push-final-images enables pushing final artifact during build. Each
package built will be packed and pushed to the final repository
specified with --push-final-images-repository. By default if no
final-repository is specified and pushing final images is enabled will
default to the cache repository.

--push-final-images-force allows to force-push final images even if
there are already available on the specified registry
2021-11-05 23:03:29 +01:00
Ettore Di Giacinto
0589bead99 Tag 0.20.7 2021-11-04 13:02:23 +01:00
Ettore Di Giacinto
fba420865a 🔧 Preserve suid,sgid and sticky bits when extracting images 2021-11-04 11:34:36 +01:00
Ettore Di Giacinto
9857bea5ff 🎨 Lazy progressbar start 2021-10-31 21:21:08 +01:00
Ettore Di Giacinto
100c313804 Tag 0.20.6 2021-10-31 12:21:15 +01:00
Ettore Di Giacinto
d43b8c4af0 Attach platform data when creating images from tars 2021-10-31 10:48:35 +01:00
15 changed files with 305 additions and 31 deletions

View File

@@ -110,6 +110,9 @@ Build packages specifying multiple definition trees:
onlyTarget, _ := cmd.Flags().GetBool("only-target-package") onlyTarget, _ := cmd.Flags().GetBool("only-target-package")
full, _ := cmd.Flags().GetBool("full") full, _ := cmd.Flags().GetBool("full")
rebuild, _ := cmd.Flags().GetBool("rebuild") rebuild, _ := cmd.Flags().GetBool("rebuild")
pushFinalImages, _ := cmd.Flags().GetBool("push-final-images")
pushFinalImagesRepository, _ := cmd.Flags().GetString("push-final-images-repository")
pushFinalImagesForce, _ := cmd.Flags().GetBool("push-final-images-force")
var results Results var results Results
backendArgs := viper.GetStringSlice("backend-args") backendArgs := viper.GetStringSlice("backend-args")
@@ -159,8 +162,7 @@ Build packages specifying multiple definition trees:
opts.Options = solver.Options{Type: solver.SingleCoreSimple, Concurrency: concurrency} opts.Options = solver.Options{Type: solver.SingleCoreSimple, Concurrency: concurrency}
luetCompiler := compiler.NewLuetCompiler(compilerBackend, generalRecipe.GetDatabase(), compileropts := []options.Option{options.NoDeps(nodeps),
options.NoDeps(nodeps),
options.WithBackendType(backendType), options.WithBackendType(backendType),
options.PushImages(push), options.PushImages(push),
options.WithBuildValues(values), options.WithBuildValues(values),
@@ -177,8 +179,21 @@ Build packages specifying multiple definition trees:
options.WithContext(util.DefaultContext), options.WithContext(util.DefaultContext),
options.BackendArgs(backendArgs), options.BackendArgs(backendArgs),
options.Concurrency(concurrency), options.Concurrency(concurrency),
options.WithCompressionType(compression.Implementation(compressionType)), options.WithCompressionType(compression.Implementation(compressionType))}
)
if pushFinalImages {
compileropts = append(compileropts, options.EnablePushFinalImages)
if pushFinalImagesForce {
compileropts = append(compileropts, options.ForcePushFinalImages)
}
if pushFinalImagesRepository != "" {
compileropts = append(compileropts, options.WithFinalRepository(pushFinalImagesRepository))
} else if imageRepository != "" {
compileropts = append(compileropts, options.WithFinalRepository(imageRepository))
}
}
luetCompiler := compiler.NewLuetCompiler(compilerBackend, generalRecipe.GetDatabase(), compileropts...)
if full { if full {
specs, err := luetCompiler.FromDatabase(generalRecipe.GetDatabase(), true, dst) specs, err := luetCompiler.FromDatabase(generalRecipe.GetDatabase(), true, dst)
@@ -302,6 +317,11 @@ func init() {
buildCmd.Flags().Bool("privileged", true, "Privileged (Keep permissions)") buildCmd.Flags().Bool("privileged", true, "Privileged (Keep permissions)")
buildCmd.Flags().Bool("revdeps", false, "Build with revdeps") buildCmd.Flags().Bool("revdeps", false, "Build with revdeps")
buildCmd.Flags().Bool("all", false, "Build all specfiles in the tree") buildCmd.Flags().Bool("all", false, "Build all specfiles in the tree")
buildCmd.Flags().Bool("push-final-images", false, "Push final images while building")
buildCmd.Flags().Bool("push-final-images-force", false, "Override existing images")
buildCmd.Flags().String("push-final-images-repository", "", "Repository where to push final images to")
buildCmd.Flags().Bool("full", false, "Build all packages (optimized)") buildCmd.Flags().Bool("full", false, "Build all packages (optimized)")
buildCmd.Flags().StringSlice("values", []string{}, "Build values file to interpolate with each package") buildCmd.Flags().StringSlice("values", []string{}, "Build values file to interpolate with each package")
buildCmd.Flags().StringSliceP("backend-args", "a", []string{}, "Backend args") buildCmd.Flags().StringSliceP("backend-args", "a", []string{}, "Backend args")

View File

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

View File

@@ -27,7 +27,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
func imageFromTar(imagename string, r io.Reader) (name.Reference, v1.Image, error) { func imageFromTar(imagename, architecture, OS string, r io.Reader) (name.Reference, v1.Image, error) {
newRef, err := name.ParseReference(imagename) newRef, err := name.ParseReference(imagename)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@@ -38,7 +38,20 @@ func imageFromTar(imagename string, r io.Reader) (name.Reference, v1.Image, erro
return nil, nil, err return nil, nil, err
} }
img, err := mutate.Append(empty.Image, mutate.Addendum{ baseImage := empty.Image
cfg, err := baseImage.ConfigFile()
if err != nil {
return nil, nil, err
}
cfg.Architecture = architecture
cfg.OS = OS
baseImage, err = mutate.ConfigFile(baseImage, cfg)
if err != nil {
return nil, nil, err
}
img, err := mutate.Append(baseImage, mutate.Addendum{
Layer: layer, Layer: layer,
History: v1.History{ History: v1.History{
CreatedBy: "luet", CreatedBy: "luet",
@@ -53,25 +66,25 @@ func imageFromTar(imagename string, r io.Reader) (name.Reference, v1.Image, erro
} }
// CreateTar a imagetarball from a standard tarball // CreateTar a imagetarball from a standard tarball
func CreateTar(srctar, dstimageTar, imagename string) error { func CreateTar(srctar, dstimageTar, imagename, architecture, OS string) error {
f, err := os.Open(srctar) f, err := os.Open(srctar)
if err != nil { if err != nil {
return errors.Wrap(err, "Cannot open "+srctar) return errors.Wrap(err, "Cannot open "+srctar)
} }
defer f.Close() defer f.Close()
return CreateTarReader(f, dstimageTar, imagename) return CreateTarReader(f, dstimageTar, imagename, architecture, OS)
} }
// CreateTarReader a imagetarball from a standard tarball // CreateTarReader a imagetarball from a standard tarball
func CreateTarReader(r io.Reader, dstimageTar, imagename string) error { func CreateTarReader(r io.Reader, dstimageTar, imagename, architecture, OS string) error {
dstFile, err := os.Create(dstimageTar) dstFile, err := os.Create(dstimageTar)
if err != nil { if err != nil {
return errors.Wrap(err, "Cannot create "+dstimageTar) return errors.Wrap(err, "Cannot create "+dstimageTar)
} }
defer dstFile.Close() defer dstFile.Close()
newRef, img, err := imageFromTar(imagename, r) newRef, img, err := imageFromTar(imagename, architecture, OS, r)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -18,6 +18,7 @@ package image_test
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"runtime"
. "github.com/mudler/luet/pkg/api/core/image" . "github.com/mudler/luet/pkg/api/core/image"
"github.com/mudler/luet/pkg/api/core/types" "github.com/mudler/luet/pkg/api/core/types"
@@ -58,7 +59,7 @@ var _ = Describe("Create", func() {
a.Compress(dir, 1) a.Compress(dir, 1)
// Unfortunately there is no other easy way to test this // Unfortunately there is no other easy way to test this
err = CreateTar(srcTar.Name(), dst.Name(), "testimage") err = CreateTar(srcTar.Name(), dst.Name(), "testimage", runtime.GOARCH, runtime.GOOS)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
b.LoadImage(dst.Name()) b.LoadImage(dst.Name())

View File

@@ -19,6 +19,7 @@ import (
"archive/tar" "archive/tar"
"context" "context"
"io" "io"
"io/fs"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -208,6 +209,7 @@ func ExtractReader(ctx *types.Context, reader io.ReadCloser, output string, keep
PAX, Xattrs map[string]string PAX, Xattrs map[string]string
Uid, Gid int Uid, Gid int
Name string Name string
FileMode fs.FileMode
} }
permstore, err := ctx.Config.System.TempDir("permstore") permstore, err := ctx.Config.System.TempDir("permstore")
@@ -222,8 +224,9 @@ func ExtractReader(ctx *types.Context, reader io.ReadCloser, output string, keep
perms.SetValue(h.Name, permData{ perms.SetValue(h.Name, permData{
PAX: h.PAXRecords, PAX: h.PAXRecords,
Uid: h.Uid, Gid: h.Gid, Uid: h.Uid, Gid: h.Gid,
Xattrs: h.Xattrs, Xattrs: h.Xattrs,
Name: h.Name, Name: h.Name,
FileMode: h.FileInfo().Mode(),
}) })
//perms = append(perms, }) //perms = append(perms, })
} }
@@ -249,6 +252,10 @@ func ExtractReader(ctx *types.Context, reader io.ReadCloser, output string, keep
if err := os.Lchown(ff, p.Uid, p.Gid); err != nil { if err := os.Lchown(ff, p.Uid, p.Gid); err != nil {
ctx.Warning(err, "failed chowning file") ctx.Warning(err, "failed chowning file")
} }
ctx.Debug("Set", p.Name, p.FileMode)
if err := os.Chmod(ff, p.FileMode); err != nil {
ctx.Warning(err, "failed chmod file")
}
} }
for _, attrs := range []map[string]string{p.Xattrs, p.PAX} { for _, attrs := range []map[string]string{p.Xattrs, p.PAX} {
for k, attr := range attrs { for k, attr := range attrs {

View File

@@ -27,6 +27,7 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"runtime"
"github.com/docker/docker/pkg/pools" "github.com/docker/docker/pkg/pools"
v1 "github.com/google/go-containerregistry/pkg/v1" v1 "github.com/google/go-containerregistry/pkg/v1"
@@ -227,7 +228,7 @@ func (a *PackageArtifact) GenerateFinalImage(ctx *types.Context, imageName strin
} }
defer os.RemoveAll(tempimage.Name()) // clean up defer os.RemoveAll(tempimage.Name()) // clean up
if err := image.CreateTarReader(decompressed, tempimage.Name(), imageName); err != nil { if err := image.CreateTarReader(decompressed, tempimage.Name(), imageName, runtime.GOARCH, runtime.GOOS); err != nil {
return errors.Wrap(err, "could not create image from tar") return errors.Wrap(err, "could not create image from tar")
} }

View File

@@ -52,7 +52,6 @@ type Context struct {
s *pterm.SpinnerPrinter s *pterm.SpinnerPrinter
spinnerLock *sync.Mutex spinnerLock *sync.Mutex
z *zap.Logger z *zap.Logger
AreaPrinter *pterm.AreaPrinter
ProgressBar *pterm.ProgressbarPrinter ProgressBar *pterm.ProgressbarPrinter
} }

View File

@@ -488,12 +488,14 @@ func (cs *LuetCompiler) genArtifact(p *compilerspec.LuetCompilationSpec, builder
a.CompileSpec = p a.CompileSpec = p
a.CompileSpec.GetPackage().SetBuildTimestamp(time.Now().String()) a.CompileSpec.GetPackage().SetBuildTimestamp(time.Now().String())
err = a.WriteYAML(p.GetOutputPath()) err = a.WriteYAML(p.GetOutputPath())
if err != nil { if err != nil {
return a, errors.Wrap(err, "Failed while writing metadata file") return a, errors.Wrap(err, "Failed while writing metadata file")
} }
cs.Options.Context.Success(pkgTag, " :white_check_mark: done (empty virtual package)") cs.Options.Context.Success(pkgTag, " :white_check_mark: done (empty virtual package)")
if cs.Options.PushFinalImages {
cs.pushFinalArtifact(a, p, keepPermissions)
}
return a, nil return a, nil
} }
@@ -523,11 +525,53 @@ func (cs *LuetCompiler) genArtifact(p *compilerspec.LuetCompilationSpec, builder
if err != nil { if err != nil {
return a, errors.Wrap(err, "Failed while writing metadata file") return a, errors.Wrap(err, "Failed while writing metadata file")
} }
cs.Options.Context.Success(pkgTag, " :white_check_mark: Done") cs.Options.Context.Success(pkgTag, " :white_check_mark: Done building")
if cs.Options.PushFinalImages {
cs.pushFinalArtifact(a, p, keepPermissions)
}
return a, nil return a, nil
} }
// TODO: A small readaptation of repository_docker.go pushImageFromArtifact()
// Move this to a common place
func (cs *LuetCompiler) pushFinalArtifact(a *artifact.PackageArtifact, p *compilerspec.LuetCompilationSpec, keepPermissions bool) error {
cs.Options.Context.Info("Pushing final image for", a.CompileSpec.Package.HumanReadableString())
imageID := fmt.Sprintf("%s:%s", cs.Options.PushFinalImagesRepository, a.CompileSpec.Package.ImageID())
// First push the package image
if !cs.Backend.ImageAvailable(imageID) || cs.Options.PushFinalImagesForce {
cs.Options.Context.Info("Generating and pushing final image for", a.CompileSpec.Package.HumanReadableString(), "as", imageID)
if err := a.GenerateFinalImage(cs.Options.Context, imageID, cs.GetBackend(), true); err != nil {
return errors.Wrap(err, "while creating final image")
}
if err := cs.Backend.Push(backend.Options{ImageName: imageID}); err != nil {
return errors.Wrapf(err, "Could not push image: %s", imageID)
}
}
// Then the image ID
metadataImageID := fmt.Sprintf("%s:%s", cs.Options.PushFinalImagesRepository, a.CompileSpec.GetPackage().GetMetadataFilePath())
if !cs.Backend.ImageAvailable(metadataImageID) || cs.Options.PushFinalImagesForce {
cs.Options.Context.Info("Generating metadata image for", a.CompileSpec.Package.HumanReadableString(), metadataImageID)
a := artifact.NewPackageArtifact(filepath.Join(p.GetOutputPath(), a.CompileSpec.GetPackage().GetMetadataFilePath()))
metadataArchive, err := artifact.CreateArtifactForFile(cs.Options.Context, a.Path)
if err != nil {
return errors.Wrap(err, "failed generating checksums for tree")
}
if err := metadataArchive.GenerateFinalImage(cs.Options.Context, metadataImageID, cs.Backend, keepPermissions); err != nil {
return errors.Wrap(err, "Failed generating metadata tree "+metadataImageID)
}
if err = cs.Backend.Push(backend.Options{ImageName: metadataImageID}); err != nil {
return errors.Wrapf(err, "Could not push image: %s", metadataImageID)
}
}
return nil
}
func (cs *LuetCompiler) waitForImages(images []string) { func (cs *LuetCompiler) waitForImages(images []string) {
if cs.Options.PullFirst && cs.Options.Wait { if cs.Options.PullFirst && cs.Options.Wait {
available, _ := oneOfImagesAvailable(images, cs.Backend) available, _ := oneOfImagesAvailable(images, cs.Backend)

View File

@@ -16,10 +16,15 @@
package compiler_test package compiler_test
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings"
helpers "github.com/mudler/luet/tests/helpers"
"github.com/mudler/luet/pkg/api/core/image"
"github.com/mudler/luet/pkg/api/core/types" "github.com/mudler/luet/pkg/api/core/types"
"github.com/mudler/luet/pkg/api/core/types/artifact" "github.com/mudler/luet/pkg/api/core/types/artifact"
. "github.com/mudler/luet/pkg/compiler" . "github.com/mudler/luet/pkg/compiler"
@@ -853,6 +858,67 @@ var _ = Describe("Compiler", func() {
Expect(fileHelper.Exists(spec.Rel("bin/busybox"))).To(BeTrue()) Expect(fileHelper.Exists(spec.Rel("bin/busybox"))).To(BeTrue())
Expect(fileHelper.Exists(spec.Rel("var"))).ToNot(BeTrue()) Expect(fileHelper.Exists(spec.Rel("var"))).ToNot(BeTrue())
}) })
It("Pushes final images along", func() {
generalRecipe := tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false))
randString := strings.ToLower(helpers.String(10))
imageName := fmt.Sprintf("ttl.sh/%s", randString)
b := sd.NewSimpleDockerBackend(ctx)
err := generalRecipe.Load("../../tests/fixtures/packagelayers")
Expect(err).ToNot(HaveOccurred())
Expect(len(generalRecipe.GetDatabase().GetPackages())).To(Equal(2))
compiler := NewLuetCompiler(b, generalRecipe.GetDatabase(),
options.EnablePushFinalImages, options.ForcePushFinalImages, options.WithFinalRepository(imageName))
spec, err := compiler.FromPackage(&pkg.DefaultPackage{Name: "runtime", Category: "layer", Version: "0.1"})
Expect(err).ToNot(HaveOccurred())
spec2, err := compiler.FromPackage(&pkg.DefaultPackage{Name: "build", Category: "layer", Version: "0.1"})
Expect(err).ToNot(HaveOccurred())
Expect(spec.GetPackage().GetPath()).ToNot(Equal(""))
tmpdir, err := ioutil.TempDir("", "tree")
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(tmpdir) // clean up
spec.SetOutputPath(tmpdir)
artifacts, errs := compiler.CompileParallel(false, compilerspec.NewLuetCompilationspecs(spec, spec2))
Expect(errs).To(BeNil())
Expect(len(artifacts)).To(Equal(2))
//Expect(len(artifacts[0].Dependencies)).To(Equal(1))
Expect(b.ImageAvailable(fmt.Sprintf("%s:%s", imageName, artifacts[0].Runtime.ImageID()))).To(BeTrue())
Expect(b.ImageAvailable(fmt.Sprintf("%s:%s", imageName, artifacts[0].Runtime.GetMetadataFilePath()))).To(BeTrue())
Expect(b.ImageAvailable(fmt.Sprintf("%s:%s", imageName, artifacts[1].Runtime.ImageID()))).To(BeTrue())
Expect(b.ImageAvailable(fmt.Sprintf("%s:%s", imageName, artifacts[1].Runtime.GetMetadataFilePath()))).To(BeTrue())
img, err := b.ImageReference(fmt.Sprintf("%s:%s", imageName, artifacts[0].Runtime.ImageID()), true)
Expect(err).ToNot(HaveOccurred())
_, path, err := image.Extract(ctx, img, false, nil)
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(path) // clean up
Expect(fileHelper.Exists(filepath.Join(path, "bin/busybox"))).To(BeTrue())
img, err = b.ImageReference(fmt.Sprintf("%s:%s", imageName, artifacts[1].Runtime.GetMetadataFilePath()), true)
Expect(err).ToNot(HaveOccurred())
_, path, err = image.Extract(ctx, img, false, nil)
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(path) // clean up
meta := filepath.Join(path, artifacts[1].Runtime.GetMetadataFilePath())
Expect(fileHelper.Exists(meta)).To(BeTrue())
d, err := ioutil.ReadFile(meta)
Expect(err).ToNot(HaveOccurred())
Expect(string(d)).To(ContainSubstring(artifacts[1].CompileSpec.GetPackage().GetName()))
})
}) })
Context("Packages which conents are a package folder", func() { Context("Packages which conents are a package folder", func() {

View File

@@ -47,6 +47,12 @@ type Compiler struct {
// TemplatesFolder. should default to tree/templates // TemplatesFolder. should default to tree/templates
TemplatesFolder []string TemplatesFolder []string
// Tells wether to push final container images after building
PushFinalImages bool
PushFinalImagesForce bool
// Image repository to push to
PushFinalImagesRepository string
Context *types.Context Context *types.Context
} }
@@ -85,6 +91,25 @@ func WithOptions(opt *Compiler) func(cfg *Compiler) error {
} }
} }
// WithFinalRepository Sets the final repository where to push
// images of built artifacts
func WithFinalRepository(r string) func(cfg *Compiler) error {
return func(cfg *Compiler) error {
cfg.PushFinalImagesRepository = r
return nil
}
}
func EnablePushFinalImages(cfg *Compiler) error {
cfg.PushFinalImages = true
return nil
}
func ForcePushFinalImages(cfg *Compiler) error {
cfg.PushFinalImagesForce = true
return nil
}
func WithBackendType(r string) func(cfg *Compiler) error { func WithBackendType(r string) func(cfg *Compiler) error {
return func(cfg *Compiler) error { return func(cfg *Compiler) error {
cfg.BackendType = r cfg.BackendType = r
@@ -113,6 +138,8 @@ func WithPullRepositories(r []string) func(cfg *Compiler) error {
} }
} }
// WithPushRepository Sets the image reference where to push
// cache images
func WithPushRepository(r string) func(cfg *Compiler) error { func WithPushRepository(r string) func(cfg *Compiler) error {
return func(cfg *Compiler) error { return func(cfg *Compiler) error {
if len(cfg.PullImageRepository) == 0 { if len(cfg.PullImageRepository) == 0 {

View File

@@ -118,11 +118,7 @@ func (c *HttpClient) DownloadFile(p string) (string, error) {
// Initialize a progressbar only if we have one in the current context // Initialize a progressbar only if we have one in the current context
var pb *pterm.ProgressbarPrinter var pb *pterm.ProgressbarPrinter
if c.context.ProgressBar != nil { if c.context.ProgressBar != nil {
pb = pterm.DefaultProgressbar.WithTotal(int(resp.Size())) pb, _ = c.context.ProgressBar.WithTotal(int(resp.Size())).WithTitle(filepath.Base(resp.Request.HTTPRequest.URL.RequestURI())).Start()
if c.context.AreaPrinter != nil {
pb = pb.WithPrintTogether(c.context.AreaPrinter)
}
pb, _ = pb.WithTitle(filepath.Base(resp.Request.HTTPRequest.URL.RequestURI())).Start()
} }
// start download loop // start download loop
t := time.NewTicker(500 * time.Millisecond) t := time.NewTicker(500 * time.Millisecond)

View File

@@ -576,19 +576,19 @@ func (l *LuetInstaller) download(syncedRepos Repositories, toDownload map[string
// Check if the terminal is big enough to display a progress bar // Check if the terminal is big enough to display a progress bar
// https://github.com/pterm/pterm/blob/4c725e56bfd9eb38e1c7b9dec187b50b93baa8bd/progressbar_printer.go#L190 // https://github.com/pterm/pterm/blob/4c725e56bfd9eb38e1c7b9dec187b50b93baa8bd/progressbar_printer.go#L190
w, _, err := ctx.GetTerminalSize() w, _, err := ctx.GetTerminalSize()
var pb *pterm.ProgressbarPrinter
if ctx.IsTerminal && err == nil && w > 100 { if ctx.IsTerminal && err == nil && w > 100 {
area, _ := pterm.DefaultArea.Start() area, _ := pterm.DefaultArea.Start()
p, _ := pterm.DefaultProgressbar.WithPrintTogether(area).WithTotal(len(toDownload)).WithTitle("Downloading packages").Start() ctx.ProgressBar = pterm.DefaultProgressbar.WithPrintTogether(area).WithTotal(len(toDownload)).WithTitle("Downloading packages")
pb, _ = ctx.ProgressBar.Start()
ctx.AreaPrinter = area
ctx.ProgressBar = p
defer area.Stop() defer area.Stop()
} }
// Download // Download
for i := 0; i < l.Options.Concurrency; i++ { for i := 0; i < l.Options.Concurrency; i++ {
wg.Add(1) wg.Add(1)
go l.downloadWorker(i, wg, all, ctx) go l.downloadWorker(i, wg, pb, all, ctx)
} }
for _, c := range toDownload { for _, c := range toDownload {
all <- c all <- c
@@ -920,7 +920,7 @@ func (l *LuetInstaller) installPackage(m ArtifactMatch, s *System) error {
return s.Database.SetPackageFiles(&pkg.PackageFile{PackageFingerprint: m.Package.GetFingerPrint(), Files: files}) return s.Database.SetPackageFiles(&pkg.PackageFile{PackageFingerprint: m.Package.GetFingerPrint(), Files: files})
} }
func (l *LuetInstaller) downloadWorker(i int, wg *sync.WaitGroup, c <-chan ArtifactMatch, ctx *types.Context) error { func (l *LuetInstaller) downloadWorker(i int, wg *sync.WaitGroup, pb *pterm.ProgressbarPrinter, c <-chan ArtifactMatch, ctx *types.Context) error {
defer wg.Done() defer wg.Done()
for p := range c { for p := range c {
@@ -932,8 +932,8 @@ func (l *LuetInstaller) downloadWorker(i int, wg *sync.WaitGroup, c <-chan Artif
} else { } else {
l.Options.Context.Success(":package: Package ", p.Package.HumanReadableString(), "downloaded") l.Options.Context.Success(":package: Package ", p.Package.HumanReadableString(), "downloaded")
} }
if ctx.ProgressBar != nil { if pb != nil {
ctx.ProgressBar.Increment() pb.Increment()
} }
} }

View File

@@ -0,0 +1,18 @@
image: "alpine"
unpack: true
includes:
- /foo
- /foo/bar
- /foo/bar/suid
- /foo/bar/sticky
- /foo/bar/sgid
steps:
- mkdir -p /foo/bar
- touch /foo/bar/suid
- touch /foo/bar/sgid
- touch /foo/bar/sticky
- chown 100:100 /foo/bar
- chown 101:101 /foo/bar/suid
- chmod u+s /foo/bar/suid
- chmod u-s,g+s /foo/bar/sgid
- chmod +t /foo/bar/sticky

View File

@@ -0,0 +1,3 @@
category: "test"
name: "extra-perms"
version: "0.1"

View File

@@ -0,0 +1,79 @@
#!/bin/bash
export LUET_NOLOCK=true
oneTimeSetUp() {
export tmpdir="$(mktemp -d)"
}
oneTimeTearDown() {
rm -rf "$tmpdir"
}
testBuild() {
[ "$LUET_BACKEND" == "img" ] && startSkipping
mkdir $tmpdir/testbuild
luet build -d --tree "$ROOT_DIR/tests/fixtures/extra_perms" --same-owner=true --destination $tmpdir/testbuild --compression gzip --full
buildst=$?
assertTrue 'create package perms 0.1' "[ -e '$tmpdir/testbuild/extra-perms-test-0.1.package.tar.gz' ]"
assertEquals 'builds successfully' "$buildst" "0"
}
testRepo() {
[ "$LUET_BACKEND" == "img" ] && startSkipping
assertTrue 'no repository' "[ ! -e '$tmpdir/testbuild/repository.yaml' ]"
luet create-repo --tree "$ROOT_DIR/tests/fixtures/extra_perms" \
--output $tmpdir/testbuild \
--packages $tmpdir/testbuild \
--name "test" \
--descr "Test Repo" \
--urls $tmpdir/testrootfs \
--type http
createst=$?
assertEquals 'create repo successfully' "$createst" "0"
assertTrue 'create repository' "[ -e '$tmpdir/testbuild/repository.yaml' ]"
}
testConfig() {
[ "$LUET_BACKEND" == "img" ] && startSkipping
mkdir $tmpdir/testrootfs
cat <<EOF > $tmpdir/luet.yaml
general:
debug: true
system:
rootfs: $tmpdir/testrootfs
database_path: "/"
database_engine: "boltdb"
config_from_host: true
repositories:
- name: "main"
type: "disk"
enable: true
urls:
- "$tmpdir/testbuild"
EOF
luet config --config $tmpdir/luet.yaml
res=$?
assertEquals 'config test successfully' "$res" "0"
}
testInstall() {
[ "$LUET_BACKEND" == "img" ] && startSkipping
$ROOT_DIR/tests/integration/bin/luet install -y --config $tmpdir/luet.yaml test/extra-perms
installst=$?
assertEquals 'install test successfully' "$installst" "0"
tree $tmpdir/testrootfs/foo/bar
assertTrue 'package installed bar' "[ -d '$tmpdir/testrootfs/foo/bar' ]"
assertContains 'perms2' "$(stat -c %u:%g $tmpdir/testrootfs/foo/bar)" "100:100"
assertContains 'suid' "$(stat -c %a $tmpdir/testrootfs/foo/bar/suid)" "4644"
assertContains 'sgid' "$(stat -c %a $tmpdir/testrootfs/foo/bar/sgid)" "2644"
assertContains 'sticky' "$(stat -c %a $tmpdir/testrootfs/foo/bar/sticky)" "1644"
}
# Load shUnit2.
. "$ROOT_DIR/tests/integration/shunit2"/shunit2