stage_executor: while mounting stages use freshly built stage
When using `--mount=` in RUN instruction and source is a stage make sure that freshly built stage is used if the stage selected in source was just rebuilt. Closes: https://github.com/containers/buildah/issues/4522 Signed-off-by: Aditya R <arajan@redhat.com>
This commit is contained in:
parent
c541c355c0
commit
ac7458e70d
|
|
@ -70,6 +70,7 @@ type StageExecutor struct {
|
|||
output string
|
||||
containerIDs []string
|
||||
stage *imagebuilder.Stage
|
||||
didExecute bool
|
||||
argsFromContainerfile []string
|
||||
}
|
||||
|
||||
|
|
@ -516,7 +517,7 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte
|
|||
// to `mountPoint` replaced from additional
|
||||
// build-context. Reason: Parser will use this
|
||||
// `from` to refer from stageMountPoints map later.
|
||||
stageMountPoints[from] = internal.StageMountDetails{IsStage: false, MountPoint: mountPoint}
|
||||
stageMountPoints[from] = internal.StageMountDetails{IsStage: false, DidExecute: true, MountPoint: mountPoint}
|
||||
break
|
||||
} else {
|
||||
// Most likely this points to path on filesystem
|
||||
|
|
@ -548,7 +549,7 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte
|
|||
mountPoint = additionalBuildContext.DownloadedCache
|
||||
}
|
||||
}
|
||||
stageMountPoints[from] = internal.StageMountDetails{IsStage: true, MountPoint: mountPoint}
|
||||
stageMountPoints[from] = internal.StageMountDetails{IsStage: true, DidExecute: true, MountPoint: mountPoint}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -559,14 +560,14 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte
|
|||
return nil, err
|
||||
}
|
||||
if otherStage, ok := s.executor.stages[from]; ok && otherStage.index < s.index {
|
||||
stageMountPoints[from] = internal.StageMountDetails{IsStage: true, MountPoint: otherStage.mountPoint}
|
||||
stageMountPoints[from] = internal.StageMountDetails{IsStage: true, DidExecute: otherStage.didExecute, MountPoint: otherStage.mountPoint}
|
||||
break
|
||||
} else {
|
||||
mountPoint, err := s.getImageRootfs(s.ctx, from)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s from=%s: no stage or image found with that name", flag, from)
|
||||
}
|
||||
stageMountPoints[from] = internal.StageMountDetails{IsStage: false, MountPoint: mountPoint}
|
||||
stageMountPoints[from] = internal.StageMountDetails{IsStage: false, DidExecute: true, MountPoint: mountPoint}
|
||||
break
|
||||
}
|
||||
default:
|
||||
|
|
@ -1148,6 +1149,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
|||
// If we're doing a single-layer build, just process the
|
||||
// instruction.
|
||||
if !s.executor.layers {
|
||||
s.didExecute = true
|
||||
err := ib.Run(step, s, noRunsRemaining)
|
||||
if err != nil {
|
||||
logrus.Debugf("Error building at step %+v: %v", *step, err)
|
||||
|
|
@ -1193,6 +1195,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
|||
}
|
||||
|
||||
// We're in a multi-layered build.
|
||||
s.didExecute = false
|
||||
var (
|
||||
commitName string
|
||||
cacheID string
|
||||
|
|
@ -1204,7 +1207,36 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
|||
canMatchCacheOnlyAfterRun bool
|
||||
)
|
||||
|
||||
needsCacheKey := (s.executor.cacheFrom != nil || s.executor.cacheTo != nil)
|
||||
// Only attempt to find cache if its needed, this part is needed
|
||||
// so that if a step is using RUN --mount and mounts content from
|
||||
// previous stages then it uses the freshly built stage instead
|
||||
// of re-using the older stage from the store.
|
||||
avoidLookingCache := false
|
||||
var mounts []string
|
||||
for _, a := range node.Flags {
|
||||
arg, err := imagebuilder.ProcessWord(a, s.stage.Builder.Arguments())
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(arg, "--mount="):
|
||||
mount := strings.TrimPrefix(arg, "--mount=")
|
||||
mounts = append(mounts, mount)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
stageMountPoints, err := s.runStageMountPoints(mounts)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
for _, mountPoint := range stageMountPoints {
|
||||
if mountPoint.DidExecute {
|
||||
avoidLookingCache = true
|
||||
}
|
||||
}
|
||||
|
||||
needsCacheKey := (s.executor.cacheFrom != nil || s.executor.cacheTo != nil) && !avoidLookingCache
|
||||
|
||||
// If we have to commit for this instruction, only assign the
|
||||
// stage's configured output name to the last layer.
|
||||
|
|
@ -1228,13 +1260,14 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
|||
// we need to call ib.Run() to correctly put the args together before
|
||||
// determining if a cached layer with the same build args already exists
|
||||
// and that is done in the if block below.
|
||||
if checkForLayers && step.Command != "arg" && !(s.executor.squash && lastInstruction && lastStage) {
|
||||
if checkForLayers && step.Command != "arg" && !(s.executor.squash && lastInstruction && lastStage) && !avoidLookingCache {
|
||||
// For `COPY` and `ADD`, history entries include digests computed from
|
||||
// the content that's copied in. We need to compute that information so that
|
||||
// it can be used to evaluate the cache, which means we need to go ahead
|
||||
// and copy the content.
|
||||
canMatchCacheOnlyAfterRun = (step.Command == command.Add || step.Command == command.Copy)
|
||||
if canMatchCacheOnlyAfterRun {
|
||||
s.didExecute = true
|
||||
if err = ib.Run(step, s, noRunsRemaining); err != nil {
|
||||
logrus.Debugf("Error building at step %+v: %v", *step, err)
|
||||
return "", nil, fmt.Errorf("building at STEP \"%s\": %w", step.Message, err)
|
||||
|
|
@ -1281,6 +1314,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
|||
// cases above, so we shouldn't do it again.
|
||||
if cacheID == "" && !canMatchCacheOnlyAfterRun {
|
||||
// Process the instruction directly.
|
||||
s.didExecute = true
|
||||
if err = ib.Run(step, s, noRunsRemaining); err != nil {
|
||||
logrus.Debugf("Error building at step %+v: %v", *step, err)
|
||||
return "", nil, fmt.Errorf("building at STEP \"%s\": %w", step.Message, err)
|
||||
|
|
@ -1298,7 +1332,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
|||
|
||||
// Check if there's already an image based on our parent that
|
||||
// has the same change that we just made.
|
||||
if checkForLayers {
|
||||
if checkForLayers && !avoidLookingCache {
|
||||
cacheID, err = s.intermediateImageExists(ctx, node, addedContentSummary, s.stepRequiresLayer(step))
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("checking if cached image exists from a previous build: %w", err)
|
||||
|
|
@ -1334,6 +1368,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
|||
// still don't want to restart using the image's
|
||||
// configuration blob.
|
||||
if !s.stepRequiresLayer(step) {
|
||||
s.didExecute = true
|
||||
err := ib.Run(step, s, noRunsRemaining)
|
||||
if err != nil {
|
||||
logrus.Debugf("Error building at step %+v: %v", *step, err)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ const (
|
|||
// StageExecutor has ability to mount stages/images in current context and
|
||||
// automatically clean them up.
|
||||
type StageMountDetails struct {
|
||||
DidExecute bool // tells if the stage which is being mounted was freshly executed or was part of older cache
|
||||
IsStage bool // tells if mountpoint returned from stage executor is stage or image
|
||||
MountPoint string // mountpoint of stage/image
|
||||
}
|
||||
|
|
|
|||
|
|
@ -550,6 +550,47 @@ _EOF
|
|||
expect_output --substring "world"
|
||||
}
|
||||
|
||||
@test "build-test do not use mount stage from cache if it was rebuilt" {
|
||||
local contextdir=${TEST_SCRATCH_DIR}/bud/platform
|
||||
mkdir -p $contextdir
|
||||
|
||||
cat > $contextdir/Dockerfile << _EOF
|
||||
FROM alpine as dependencies
|
||||
|
||||
RUN mkdir /build && echo v1 > /build/version
|
||||
|
||||
FROM alpine
|
||||
|
||||
RUN --mount=type=bind,source=/build,target=/build,from=dependencies \
|
||||
cp /build/version /version
|
||||
|
||||
RUN cat /version
|
||||
_EOF
|
||||
|
||||
run_buildah build $WITH_POLICY_JSON --layers -t source -f $contextdir/Dockerfile
|
||||
run_buildah build $WITH_POLICY_JSON --layers -t source2 -f $contextdir/Dockerfile
|
||||
expect_output --substring "Using cache"
|
||||
|
||||
# First stage i.e dependencies is changed so it should not use the steps in second stage from
|
||||
# cache
|
||||
cat > $contextdir/Dockerfile << _EOF
|
||||
FROM alpine as dependencies
|
||||
|
||||
RUN mkdir /build && echo v2 > /build/version
|
||||
|
||||
FROM alpine
|
||||
|
||||
RUN --mount=type=bind,source=/build,target=/build,from=dependencies \
|
||||
cp /build/version /version
|
||||
|
||||
RUN cat /version
|
||||
_EOF
|
||||
|
||||
run_buildah build $WITH_POLICY_JSON --layers -t source3 -f $contextdir/Dockerfile
|
||||
assert "$output" !~ "Using cache"
|
||||
|
||||
}
|
||||
|
||||
@test "build-test skipping unwanted stages with --skip-unused-stages=false and --skip-unused-stages=true" {
|
||||
local contextdir=${TEST_SCRATCH_DIR}/bud/platform
|
||||
mkdir -p $contextdir
|
||||
|
|
|
|||
Loading…
Reference in New Issue