mirror of
https://github.com/mudler/luet.git
synced 2025-07-20 10:19:13 +00:00
Walk destination only once when computing delta
Avoid the double pass by constructing the list on the fly
This commit is contained in:
parent
1f0324c452
commit
daa9eb98d2
@ -51,14 +51,12 @@ var _ = Describe("Delta", func() {
|
|||||||
var tmpfile *os.File
|
var tmpfile *os.File
|
||||||
var ref, ref2 name.Reference
|
var ref, ref2 name.Reference
|
||||||
var img, img2 v1.Image
|
var img, img2 v1.Image
|
||||||
var diff ImageDiff
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
ref, _ = name.ParseReference("alpine")
|
ref, _ = name.ParseReference("alpine")
|
||||||
ref2, _ = name.ParseReference("golang:alpine")
|
ref2, _ = name.ParseReference("golang:alpine")
|
||||||
img, _ = daemon.Image(ref)
|
img, _ = daemon.Image(ref)
|
||||||
img2, _ = daemon.Image(ref2)
|
img2, _ = daemon.Image(ref2)
|
||||||
diff, err = Delta(img, img2)
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
ctx = types.NewContext()
|
ctx = types.NewContext()
|
||||||
@ -69,11 +67,15 @@ var _ = Describe("Delta", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("Extract all deltas", func() {
|
It("Extract all deltas", func() {
|
||||||
|
|
||||||
|
f, err := ExtractDeltaAdditionsFiles(ctx, img, []string{}, []string{})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
_, tmpdir, err := Extract(
|
_, tmpdir, err := Extract(
|
||||||
ctx,
|
ctx,
|
||||||
img2,
|
img2,
|
||||||
true,
|
true,
|
||||||
ExtractDeltaFiles(ctx, diff, []string{}, []string{}),
|
f,
|
||||||
)
|
)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
defer os.RemoveAll(tmpdir) // clean up
|
defer os.RemoveAll(tmpdir) // clean up
|
||||||
@ -85,11 +87,15 @@ var _ = Describe("Delta", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("Extract deltas and excludes /usr/local/go", func() {
|
It("Extract deltas and excludes /usr/local/go", func() {
|
||||||
|
f, err := ExtractDeltaAdditionsFiles(ctx, img, []string{}, []string{"usr/local/go"})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
_, tmpdir, err := Extract(
|
_, tmpdir, err := Extract(
|
||||||
ctx,
|
ctx,
|
||||||
img2,
|
img2,
|
||||||
true,
|
true,
|
||||||
ExtractDeltaFiles(ctx, diff, []string{}, []string{"usr/local/go"}),
|
f,
|
||||||
)
|
)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
defer os.RemoveAll(tmpdir) // clean up
|
defer os.RemoveAll(tmpdir) // clean up
|
||||||
@ -97,11 +103,14 @@ var _ = Describe("Delta", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("Extract deltas and excludes /usr/local/go/bin, but includes /usr/local/go", func() {
|
It("Extract deltas and excludes /usr/local/go/bin, but includes /usr/local/go", func() {
|
||||||
|
f, err := ExtractDeltaAdditionsFiles(ctx, img, []string{"usr/local/go"}, []string{"usr/local/go/bin"})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
_, tmpdir, err := Extract(
|
_, tmpdir, err := Extract(
|
||||||
ctx,
|
ctx,
|
||||||
img2,
|
img2,
|
||||||
true,
|
true,
|
||||||
ExtractDeltaFiles(ctx, diff, []string{"usr/local/go"}, []string{"usr/local/go/bin"}),
|
f,
|
||||||
)
|
)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
defer os.RemoveAll(tmpdir) // clean up
|
defer os.RemoveAll(tmpdir) // clean up
|
||||||
@ -110,11 +119,13 @@ var _ = Describe("Delta", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("Extract deltas and includes /usr/local/go", func() {
|
It("Extract deltas and includes /usr/local/go", func() {
|
||||||
|
f, err := ExtractDeltaAdditionsFiles(ctx, img, []string{"usr/local/go"}, []string{})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
_, tmpdir, err := Extract(
|
_, tmpdir, err := Extract(
|
||||||
ctx,
|
ctx,
|
||||||
img2,
|
img2,
|
||||||
true,
|
true,
|
||||||
ExtractDeltaFiles(ctx, diff, []string{"usr/local/go"}, []string{}),
|
f,
|
||||||
)
|
)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
defer os.RemoveAll(tmpdir) // clean up
|
defer os.RemoveAll(tmpdir) // clean up
|
||||||
|
@ -37,23 +37,38 @@ import (
|
|||||||
// Afterward create artifact pointing to the dir
|
// Afterward create artifact pointing to the dir
|
||||||
|
|
||||||
// ExtractDeltaFiles returns an handler to extract files in a list
|
// ExtractDeltaFiles returns an handler to extract files in a list
|
||||||
func ExtractDeltaFiles(
|
func ExtractDeltaAdditionsFiles(
|
||||||
ctx *types.Context,
|
ctx *types.Context,
|
||||||
d ImageDiff,
|
srcimg v1.Image,
|
||||||
includes []string, excludes []string,
|
includes []string, excludes []string,
|
||||||
) func(h *tar.Header) (bool, error) {
|
) (func(h *tar.Header) (bool, error), error) {
|
||||||
|
|
||||||
includeRegexp := compileRegexes(includes)
|
includeRegexp := compileRegexes(includes)
|
||||||
excludeRegexp := compileRegexes(excludes)
|
excludeRegexp := compileRegexes(excludes)
|
||||||
|
filesSrc := map[string]interface{}{}
|
||||||
|
|
||||||
additions := map[string]interface{}{}
|
srcReader := mutate.Extract(srcimg)
|
||||||
for _, a := range d.Additions {
|
defer srcReader.Close()
|
||||||
additions[a.Name] = nil
|
|
||||||
|
srcTar := tar.NewReader(srcReader)
|
||||||
|
|
||||||
|
for {
|
||||||
|
var hdr *tar.Header
|
||||||
|
hdr, err := srcTar.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
// end of tar archive
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
filesSrc[hdr.Name] = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(h *tar.Header) (bool, error) {
|
return func(h *tar.Header) (bool, error) {
|
||||||
fileName := filepath.Join(string(os.PathSeparator), h.Name)
|
fileName := filepath.Join(string(os.PathSeparator), h.Name)
|
||||||
_, exists := additions[h.Name]
|
_, exists := filesSrc[h.Name]
|
||||||
if !exists {
|
if exists {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +112,7 @@ func ExtractDeltaFiles(
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtractFiles(
|
func ExtractFiles(
|
||||||
@ -166,51 +181,63 @@ func ExtractFiles(
|
|||||||
func ExtractReader(ctx *types.Context, reader io.ReadCloser, output string, keepPerms bool, filter func(h *tar.Header) (bool, error), opts ...containerdarchive.ApplyOpt) (int64, string, error) {
|
func ExtractReader(ctx *types.Context, reader io.ReadCloser, output string, keepPerms bool, filter func(h *tar.Header) (bool, error), opts ...containerdarchive.ApplyOpt) (int64, string, error) {
|
||||||
defer reader.Close()
|
defer reader.Close()
|
||||||
|
|
||||||
perms := map[string][]int{}
|
// If no filter is specified, grab all.
|
||||||
xattrs := map[string]map[string]string{}
|
if filter == nil {
|
||||||
paxrecords := map[string]map[string]string{}
|
filter = func(h *tar.Header) (bool, error) { return true, nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep records of permissions as we walk the tar
|
||||||
|
type permData struct {
|
||||||
|
PAX, Xattrs map[string]string
|
||||||
|
Uid, Gid int
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
perms := []permData{}
|
||||||
|
|
||||||
f := func(h *tar.Header) (bool, error) {
|
f := func(h *tar.Header) (bool, error) {
|
||||||
perms[h.Name] = []int{h.Gid, h.Uid}
|
res, err := filter(h)
|
||||||
xattrs[h.Name] = h.Xattrs
|
if res {
|
||||||
paxrecords[h.Name] = h.PAXRecords
|
perms = append(perms, permData{
|
||||||
if filter != nil {
|
PAX: h.PAXRecords,
|
||||||
return filter(h)
|
Uid: h.Uid, Gid: h.Gid,
|
||||||
|
Xattrs: h.Xattrs,
|
||||||
|
Name: h.Name,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return true, nil
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = append(opts, containerdarchive.WithFilter(f))
|
opts = append(opts, containerdarchive.WithFilter(f))
|
||||||
|
|
||||||
|
// Handle the extraction
|
||||||
c, err := containerdarchive.Apply(context.Background(), output, reader, opts...)
|
c, err := containerdarchive.Apply(context.Background(), output, reader, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, "", err
|
return 0, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Parametrize this
|
// Reconstruct permissions
|
||||||
if keepPerms {
|
if keepPerms {
|
||||||
for f, p := range perms {
|
ctx.Info("Reconstructing permissions")
|
||||||
ff := filepath.Join(output, f)
|
for _, p := range perms {
|
||||||
|
ff := filepath.Join(output, p.Name)
|
||||||
if _, err := os.Lstat(ff); err == nil {
|
if _, err := os.Lstat(ff); err == nil {
|
||||||
if err := os.Lchown(ff, p[1], p[0]); 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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
for _, attrs := range []map[string]string{p.Xattrs, p.PAX} {
|
||||||
|
|
||||||
for _, m := range []map[string]map[string]string{xattrs, paxrecords} {
|
|
||||||
for key, attrs := range m {
|
|
||||||
ff := filepath.Join(output, key)
|
|
||||||
for k, attr := range attrs {
|
for k, attr := range attrs {
|
||||||
if err := system.Lsetxattr(ff, k, []byte(attr), 0); err != nil {
|
if err := system.Lsetxattr(ff, k, []byte(attr), 0); err != nil {
|
||||||
if errors.Is(err, syscall.ENOTSUP) {
|
if errors.Is(err, syscall.ENOTSUP) {
|
||||||
ctx.Debug("ignored xattr %s in archive", key)
|
ctx.Debug("ignored xattr %s in archive", ff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, output, nil
|
return c, output, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,29 +311,35 @@ func (cs *LuetCompiler) unpackDelta(concurrency int, keepPermissions bool, p *co
|
|||||||
|
|
||||||
cs.Options.Context.Info(pkgTag, ":hammer: Generating delta")
|
cs.Options.Context.Info(pkgTag, ":hammer: Generating delta")
|
||||||
|
|
||||||
|
cs.Options.Context.Info(pkgTag, ":hammer: Retrieving reference for", builderOpts.ImageName)
|
||||||
|
|
||||||
ref, err := cs.Backend.ImageReference(builderOpts.ImageName)
|
ref, err := cs.Backend.ImageReference(builderOpts.ImageName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cs.Options.Context.Info(pkgTag, ":hammer: Retrieving reference for", runnerOpts.ImageName)
|
||||||
|
|
||||||
ref2, err := cs.Backend.ImageReference(runnerOpts.ImageName)
|
ref2, err := cs.Backend.ImageReference(runnerOpts.ImageName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
diff, err := image.Delta(ref, ref2)
|
cs.Options.Context.Info(pkgTag, ":hammer: Generating filters for extraction")
|
||||||
|
|
||||||
|
filter, err := image.ExtractDeltaAdditionsFiles(cs.Options.Context, ref, p.GetIncludes(), p.GetExcludes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "failed generating filter for extraction")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: includes/excludes might need to get "/" stripped from prefix
|
cs.Options.Context.Info(pkgTag, ":hammer: Extracting artifact from image", runnerOpts.ImageName)
|
||||||
a, err := artifact.ImageToArtifact(
|
a, err := artifact.ImageToArtifact(
|
||||||
cs.Options.Context,
|
cs.Options.Context,
|
||||||
ref2,
|
ref2,
|
||||||
cs.Options.CompressionType,
|
cs.Options.CompressionType,
|
||||||
p.Rel(fmt.Sprintf("%s%s", p.GetPackage().GetFingerPrint(), ".package.tar")),
|
p.Rel(fmt.Sprintf("%s%s", p.GetPackage().GetFingerPrint(), ".package.tar")),
|
||||||
keepPermissions,
|
keepPermissions,
|
||||||
image.ExtractDeltaFiles(cs.Options.Context, diff, p.GetIncludes(), p.GetExcludes()),
|
filter,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
Loading…
Reference in New Issue
Block a user