Bump to Buildah v1.41.0

Bump Buildah to v1.41.0 in preparation for
Podman v5.6

Signed-off-by: tomsweeneyredhat <tsweeney@redhat.com>
This commit is contained in:
tomsweeneyredhat
2025-07-21 12:05:56 -04:00
parent 4833d31dfa
commit 048729a560
32 changed files with 1207 additions and 401 deletions

View File

@@ -33,6 +33,7 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/moby/sys/userns"
digest "github.com/opencontainers/go-digest"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
)
@@ -102,6 +103,15 @@ type AddAndCopyOptions struct {
Parents bool
// Timestamp is a timestamp to override on all content as it is being read.
Timestamp *time.Time
// Link, when set to true, creates an independent layer containing the copied content
// that sits on top of existing layers. This layer can be cached and reused
// separately, and is not affected by filesystem changes from previous instructions.
Link bool
// BuildMetadata is consulted only when Link is true. Contains metadata used by
// imagebuildah for cache evaluation of linked layers (inheritLabels, unsetAnnotations,
// inheritAnnotations, newAnnotations). This field is internally managed and should
// not be set by external API users.
BuildMetadata string
}
// gitURLFragmentSuffix matches fragments to use as Git reference and build
@@ -495,15 +505,75 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
}
destUIDMap, destGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)
// Create the target directory if it doesn't exist yet.
var putRoot, putDir, stagingDir string
var createdDirs []string
var latestTimestamp time.Time
mkdirOptions := copier.MkdirOptions{
UIDMap: destUIDMap,
GIDMap: destGIDMap,
ChownNew: chownDirs,
}
if err := copier.Mkdir(mountPoint, extractDirectory, mkdirOptions); err != nil {
return fmt.Errorf("ensuring target directory exists: %w", err)
// If --link is specified, we create a staging directory to hold the content
// that will then become an independent layer
if options.Link {
containerDir, err := b.store.ContainerDirectory(b.ContainerID)
if err != nil {
return fmt.Errorf("getting container directory for %q: %w", b.ContainerID, err)
}
stagingDir, err = os.MkdirTemp(containerDir, "link-stage-")
if err != nil {
return fmt.Errorf("creating staging directory for link %q: %w", b.ContainerID, err)
}
putRoot = stagingDir
cleanDest := filepath.Clean(destination)
if strings.Contains(cleanDest, "..") {
return fmt.Errorf("invalid destination path %q: contains path traversal", destination)
}
if renameTarget != "" {
putDir = filepath.Dir(filepath.Join(stagingDir, cleanDest))
} else {
putDir = filepath.Join(stagingDir, cleanDest)
}
putDirAbs, err := filepath.Abs(putDir)
if err != nil {
return fmt.Errorf("failed to resolve absolute path: %w", err)
}
stagingDirAbs, err := filepath.Abs(stagingDir)
if err != nil {
return fmt.Errorf("failed to resolve staging directory absolute path: %w", err)
}
if !strings.HasPrefix(putDirAbs, stagingDirAbs+string(os.PathSeparator)) && putDirAbs != stagingDirAbs {
return fmt.Errorf("destination path %q escapes staging directory", destination)
}
if err := copier.Mkdir(putRoot, putDirAbs, mkdirOptions); err != nil {
return fmt.Errorf("ensuring target directory exists: %w", err)
}
tempPath := putDir
for tempPath != stagingDir && tempPath != filepath.Dir(tempPath) {
if _, err := os.Stat(tempPath); err == nil {
createdDirs = append(createdDirs, tempPath)
}
tempPath = filepath.Dir(tempPath)
}
} else {
if err := copier.Mkdir(mountPoint, extractDirectory, mkdirOptions); err != nil {
return fmt.Errorf("ensuring target directory exists: %w", err)
}
putRoot = extractDirectory
putDir = extractDirectory
}
// Copy each source in turn.
for _, src := range sources {
var multiErr *multierror.Error
@@ -580,7 +650,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
ChmodFiles: nil,
IgnoreDevices: userns.RunningInUserNS(),
}
putErr = copier.Put(extractDirectory, extractDirectory, putOptions, io.TeeReader(pipeReader, hasher))
putErr = copier.Put(putRoot, putDir, putOptions, io.TeeReader(pipeReader, hasher))
}
hashCloser.Close()
pipeReader.Close()
@@ -658,6 +728,9 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
itemsCopied++
}
st := localSourceStat.Results[globbed]
if options.Link && st.ModTime.After(latestTimestamp) {
latestTimestamp = st.ModTime
}
pipeReader, pipeWriter := io.Pipe()
wg.Add(1)
go func() {
@@ -741,12 +814,13 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
ChmodFiles: nil,
IgnoreDevices: userns.RunningInUserNS(),
}
putErr = copier.Put(extractDirectory, extractDirectory, putOptions, io.TeeReader(pipeReader, hasher))
putErr = copier.Put(putRoot, putDir, putOptions, io.TeeReader(pipeReader, hasher))
}
hashCloser.Close()
pipeReader.Close()
wg.Done()
}()
wg.Wait()
if getErr != nil {
getErr = fmt.Errorf("reading %q: %w", src, getErr)
@@ -776,6 +850,58 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
return fmt.Errorf("no items matching glob %q copied (%d filtered out%s): %w", localSourceStat.Glob, len(localSourceStat.Globbed), excludesFile, syscall.ENOENT)
}
}
if options.Link {
if !latestTimestamp.IsZero() {
for _, dir := range createdDirs {
if err := os.Chtimes(dir, latestTimestamp, latestTimestamp); err != nil {
logrus.Warnf("failed to set timestamp on directory %q: %v", dir, err)
}
}
}
var created time.Time
if options.Timestamp != nil {
created = *options.Timestamp
} else if !latestTimestamp.IsZero() {
created = latestTimestamp
} else {
created = time.Unix(0, 0).UTC()
}
command := "ADD"
if !extract {
command = "COPY"
}
contentType, digest := b.ContentDigester.Digest()
summary := contentType
if digest != "" {
if summary != "" {
summary = summary + ":"
}
summary = summary + digest.Encoded()
logrus.Debugf("added content from --link %s", summary)
}
createdBy := "/bin/sh -c #(nop) " + command + " --link " + summary + " in " + destination + " " + options.BuildMetadata
history := v1.History{
Created: &created,
CreatedBy: createdBy,
Comment: b.HistoryComment(),
}
linkedLayer := LinkedLayer{
History: history,
BlobPath: stagingDir,
}
b.AppendedLinkedLayers = append(b.AppendedLinkedLayers, linkedLayer)
if err := b.Save(); err != nil {
return fmt.Errorf("saving builder state after queuing linked layer: %w", err)
}
}
return nil
}