Skip to content

Feat: send additional build contexts as tar files for remote builds #26628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

2004joshua
Copy link
Contributor

@2004joshua 2004joshua commented Jul 14, 2025

Fixed the --build-context flag to properly send files for remote builds. Previously only the main context was sent over as a tar while additional contexts were passed as local paths and this would cause builds to fail since the files wouldn't exist.

New changes modifies the Build API to use multipart HTTP requests allowing multiple build contexts to be sent as tar files. Each additional context is packaged and transferred based on its type:

  • Local Directories: Sent as tar archives
  • Git Repositories: URL sent to the server, which then clones it
  • Container Images: Image reference sent to the server, which pulls the image
  • URLs/archives: URL sent to the server, which handles the download

Skip("Waiting for VM images with Podman > 5.5.2 for multipart build context support") is in place until VM images are updated to podman 5.6.0

Version check is in place to make sure that the server is updated to at least the upcoming 5.6.0 update or else the fix wont be usable.

Fixes: #23433

Does this PR introduce a user-facing change?

Added support for --build-context for podman-remote

Copy link
Contributor

openshift-ci bot commented Jul 14, 2025

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 2004joshua
Once this PR has been reviewed and has the lgtm label, please assign mheon for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@github-actions github-actions bot added the kind/api-change Change to remote API; merits scrutiny label Jul 14, 2025
Copy link
Member

@Luap99 Luap99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a full review, but reading into memory does not seem viable to me.
Have a look at ManifestModify() and the bindings manifests Modify() functions which AFAICT to such a multipart request without reading all into memory

Git Repositories: cloned locally then sent as a tar
URLs/archives: downloaded locally then sent as a tar

It would be best if just the server downloads them instead of the client, sending them as tar via the API just sounds slower.

Comment on lines 951 to 953
const maxMemory = 256 << 20

if err := r.ParseMultipartForm(maxMemory); err != nil {
return "", nil, fmt.Errorf("failed to parse multipart form: %w", err)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This read everything into memory which does not seem good and the limit is likely to low for some contexts

Comment on lines 644 to 645
var body bytes.Buffer
writer := multipart.NewWriter(&body)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These contexts can be very large, much bigger than the available memory so reading everything into memory seems unacceptable to me

@2004joshua 2004joshua force-pushed the build_context branch 2 times, most recently from 7ea602e to b2db9e2 Compare July 14, 2025 15:32
Copy link

[NON-BLOCKING] Packit jobs failed. @containers/packit-build please check. Everyone else, feel free to ignore.

@2004joshua 2004joshua force-pushed the build_context branch 2 times, most recently from d9d6872 to 5b62920 Compare July 14, 2025 19:40
Fixed the --build-context flag to properly send files for remote builds. Previously
only the main context was sent over as a tar while additional contexts were passed as
local paths and this would cause builds to fail since the files wouldn't exist.

New changes modifies the Build API to use multipart HTTP requests allowing multiple
build contexts to be used. Each additional context is packaged and
transferred based on its type:
- Local Directories: Sent as tar archives
- Git Repositories: link sent to the server where its then cloned
- Container Images: Image reference sent to the server, it then pulls the image there
- URLs/archives: URL sent to the server, which handles the download

Fixes: containers#23433

Signed-off-by: Joshua Arrevillaga <[email protected]>
Comment on lines +723 to +742
if err != nil {
return err
}

header, err := tar.FileInfoHeader(info, "")
if err != nil {
return err
}

relPath, err := filepath.Rel(context.Value, path)
if err != nil {
return err
}
if relPath == "." {
relPath = filepath.Base(context.Value)
}
header.Name = relPath

if err := tarWriter.WriteHeader(header); err != nil {
return err
Copy link
Collaborator

@flouthoc flouthoc Jul 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all the return err here should we wrapped with helper err message ?

Copy link
Member

@nalind nalind left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some unnecessary duplication of archive/extracting logic on both ends, probably still have an issue where the top-level directory of an additional build context has a different timestamp between otherwise-identical build requests made at different times.

return "", nil, fmt.Errorf("creating context directory: %w", err)
}

if err := archive.Untar(part, contextPath, nil); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should consider using extractTarFile() here, since it's what handles the default build context archive when the request isn't a multipart form.

switch contentType {
case "application/tar":
logrus.Infof("tar file content type is %s, should use \"application/x-tar\" content type", contentType)
case "application/x-tar":
break
case "multipart/form-data":
logrus.Debugf("Received multipart/form-data: %s", contentType)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this always going to log "Received multipart/form-data: multipart/form-data"?

}
logrus.Debugf("Added image context %q with reference %q", contextName, imageRef)

case strings.HasPrefix(fieldName, "buildcontext-local-") && fileName != "":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is fileName used at all?


case strings.HasPrefix(fieldName, "buildcontext-local-") && fileName != "":
contextName := strings.TrimPrefix(fieldName, "buildcontext-local-")
additionalAnchor := filepath.Join(anchorDir, "additional", contextName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rule of thumb: when possible, avoid using values supplied by the client when constructing path names. os.MkdirTemp() would be fine here.

case context.IsImage:
metadataField := fmt.Sprintf("buildcontext-image-%s", name)
if err := writer.WriteField(metadataField, context.Value); err != nil {
pipeWriter.CloseWithError(fmt.Errorf("adding URL context %q: %w", name, err))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pipeWriter.CloseWithError(fmt.Errorf("adding URL context %q: %w", name, err))
pipeWriter.CloseWithError(fmt.Errorf("adding image context %q: %w", name, err))

return
}
} else {
tarWriter := tar.NewWriter(part)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this calling nTar() instead? This is missing logic for handling symbolic and hard links, and compressing the archive.

}

// local context is already a tar
if strings.HasSuffix(context.Value, ".tar") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expecting the file name to correctly indicate things can lead to errors. Use github.com/containers/storage/pkg/archive.IsArchivePath().

if relPath == "." {
relPath = filepath.Base(context.Value)
}
header.Name = relPath
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If my additional build context path is "/tmp/build-context", this adds an entry for a directory named "build-context" to the archive? Why?

return "", nil, fmt.Errorf("creating additional context directory %q: %w", contextName, err)
}

if err := archive.Untar(part, additionalAnchor, nil); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The archive content's coming from a client. Why isn't it extracted using chrootarchive.Untar(), or extractTarFile()?

logrus.Debugf("Processing part: field=%q, file=%q", fieldName, fileName)

switch {
case fieldName == "context" && fileName != "":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is fileName used at all?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/api-change Change to remote API; merits scrutiny machine release-note
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Additional build contexts should be sent as additional tar files
4 participants