Skip to content

Comments

feat: add support for container cp with tarballs#4704

Merged
AkihiroSuda merged 1 commit intocontainerd:mainfrom
sondavidb:add-container-cp-tarballs
Feb 18, 2026
Merged

feat: add support for container cp with tarballs#4704
AkihiroSuda merged 1 commit intocontainerd:mainfrom
sondavidb:add-container-cp-tarballs

Conversation

@sondavidb
Copy link
Contributor

Closes #4691

This PR should allow support for the following in container cp:

  • Specifying - in src to allow streaming the contents of a tarball into a container.
  • Specifying - in dst to allow streaming the contents of a container into a tarball that is written to stdout

- `nerdctl cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-`
- `nerdctl cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH`

Using `-` as the `SRC_PATH` streams the contents of `STDIN` as a tar archive. The command extracts the content of the tar to the `DEST_PATH` in container's filesystem. In this case, `DEST_PATH` must specify a directory. Using `-` as the `DEST_PATH` streams the contents of the resource as a tar archive to `STDOUT`.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

FYI, copied this exact wording from https://docs.docker.com/reference/cli/docker/container/cp/#corner-cases. LMK if this is a problem in any way.

@sondavidb
Copy link
Contributor Author

sondavidb commented Jan 16, 2026

Will get to adding testing tomorrow. Having some trouble adding tests to the existing testing suite for now.

@sondavidb sondavidb force-pushed the add-container-cp-tarballs branch from 5bd5c65 to 91f3775 Compare January 24, 2026 04:16
@sondavidb
Copy link
Contributor Author

I was very optimistic about the testing suite, stuffing the test cases in there took a lot of effort. This suite could certainly split up some functions (the amount of if statements in here is hard to keep track of, to say the least...)

In any case, tests passed locally so hoping this should be good to go 🙂

@sondavidb sondavidb force-pushed the add-container-cp-tarballs branch from 91f3775 to 8fe1537 Compare January 26, 2026 20:41
@sondavidb
Copy link
Contributor Author

Test failures look unrelated to changes?

@@ -134,6 +142,14 @@ func getPathSpecFromHost(originalPath string) (*pathSpecifier, error) {

// getPathSpecFromHost builds a pathSpecifier from a container location
Copy link
Contributor

Choose a reason for hiding this comment

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

not related but can we change this to getPathSpecFromContainer

if options.Container2Host && isGNUTar {
tarX = append(tarX, "--no-same-owner")
var tarX []string
if destinationSpec.originalPath == "-" {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can we add this to the containerCP option somthing like option.Stdin and option.Stdout and use that as the source of truth instead of doing this comparison for clarity.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, added options FromStdin and ToStdout in ContainerCpOptions + added fromStdin and toStdout in pathSpecifier


// getPathSpecFromHost builds a pathSpecifier from a container location
func getPathSpecFromContainer(originalPath string, conSpec *oci.Spec, containerHostRoot string) (*pathSpecifier, error) {
if originalPath == "-" {
Copy link
Contributor

Choose a reason for hiding this comment

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

this condition here is not intuitve, can we skip calling them when we have stdin and stdout options are true for their respective validations.

// getPathSpecFromHost builds a pathSpecifier from a host location
// errors with errDoesNotExist, errIsNotADir, "EvalSymlinks: too many links", or other hard filesystem errors from lstat/stat
func getPathSpecFromHost(originalPath string) (*pathSpecifier, error) {
if originalPath == "-" {
Copy link
Contributor

Choose a reason for hiding this comment

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

same response as below as this case is unintuitive especially for isADir case.

description: "DEST_PATH is tarball",
destinationSpec: "-",
expect: icmd.Expected{
ExitCode: 1,
Copy link
Contributor

Choose a reason for hiding this comment

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

need error type, otherwise test is not clear why it should fail. may be also adding a bit of description on the error would help

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added errors

},
},
{
description: "DEST_PATH is a tarball",
Copy link
Contributor

Choose a reason for hiding this comment

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

all description are DEST_PATH is a tarball? not sure i understand this test. my understanding is we copy from container into stdout if we have "-" in destination spec.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, changed to DEST_PATH is stdout. I used tarball since that was the format of the output but what you said is more correct I think. Made changes across testing suite for this.

Signed-off-by: David Son <davbson@amazon.com>
@sondavidb sondavidb force-pushed the add-container-cp-tarballs branch from 8fe1537 to 97facf2 Compare January 27, 2026 22:37
@AkihiroSuda AkihiroSuda added this to the v2.3.0 milestone Feb 4, 2026
@sondavidb
Copy link
Contributor Author

Do we think CI failure is unrelated? it seems so to me but seems we are having trouble getting it to pass at all..

@sondavidb
Copy link
Contributor Author

cc @ChengyuZhu6 @AkihiroSuda (since I see you two were trying to re-run the test)

@AkihiroSuda
Copy link
Member

=== Failed
=== FAIL: cmd/nerdctl/container TestRunWithSystemdTrueEnabled/waits_for_systemd_to_become_ready_and_lists_systemd_jobs (0.04s)
    container_run_systemd_linux_test.go:120: 
        
        +============================================================================================================+
        | 🚀      | "TestRunWithSystemdTrueEnabled/waits_for_systemd_to_become_ready_and_lists_systemd_jobs": starti |
        |         | ng test!                                                                                         |
        +============================================================================================================+
        | ⏳      | /tmp/TestRunWithSystemdTrueEnabledwaits_for_systemd_to_become_ready_208478251/001/aab6ed55       |
        +============================================================================================================+
        |         |                                                                                                  |
        +============================================================================================================+
        | config  | {                                                                                                |
        |         |   "Namespace": "nerdctl-test",                                                                   |
        |         |   "PrivateMode": ""                                                                              |
        |         | }                                                                                                |
        +============================================================================================================+
        | labels  | {}                                                                                               |
        +============================================================================================================+
        
    container_run_systemd_linux_test.go:120: 
        
        +------------------------------------------------------------------------------------------------------------+
        | ➡️      | ⚙️ /usr/local/bin/nerdctl exec nerdctl-testrunwithsystemdtrueenabled sh -c -- tries=0            |
        |         |                                                                                                  |
        |         |       until systemctl is-system-running >/dev/null 2>&1; do                                      |
        |         |                                                                                                  |
        |         |         >&2 printf "Waiting for systemd to come up...\n"                                         |
        |         |         sleep 1s                                                                                 |
        |         |         tries=$(( tries + 1))                                                                    |
        |         |         [ $tries -lt 10 ] || {                                                                   |
        |         |           >&2 printf "systemd failed to come up in a reasonable amount of time\n"                |
        |         |           exit 1                                                                                 |
        |         |         }                                                                                        |
        |         |       done                                                                                       |
        |         |       systemctl list-jobs                                                                        |
        +------------------------------------------------------------------------------------------------------------+
        |         | 🚫 command returned a non-zero exit code                                                         |
        +------------------------------------------------------------------------------------------------------------+
        |         | ⚠️ 1                                                                                             |
        +------------------------------------------------------------------------------------------------------------+
        |         | 🟠 time="2026-02-15T06:17:31Z" level=fatal msg="OCI runtime exec failed: exec failed: unable to  |
        |         | start container process: error starting setns process: exec: already started"                    |
        +------------------------------------------------------------------------------------------------------------+
        | 🌱      | HOME=/root                                                                                       |
        |         | PATH=/usr/local/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin |
        |         | :/bin                                                                                            |
        |         | DOCKER_CONFIG=/tmp/TestRunWithSystemdTrueEnabledwaits_for_systemd_to_become_ready_208478251/003  |
        |         | NERDCTL_TOML=/tmp/TestRunWithSystemdTrueEnabledwaits_for_systemd_to_become_ready_208478251/003/n |
        |         | erdctl.toml                                                                                      |
        +------------------------------------------------------------------------------------------------------------+
        | ⏰      | <1s (limit: 3m0s)                                                                                |
        +------------------------------------------------------------------------------------------------------------+
        | 📁      | /tmp/TestRunWithSystemdTrueEnabledwaits_for_systemd_to_become_ready_208478251/002                |
        +------------------------------------------------------------------------------------------------------------+
        
    container_run_systemd_linux_test.go:120: 	
        
        <<<<<<<<<<<<<<<<<<<<
        	🖊️ Command should succeed
        	👀 testing:		`command returned a non-zero exit code`
        	❌ FAILED!		is `<nil>`
        >>>>>>>>>>>>>>>>>>>>

https://github.com/containerd/nerdctl/actions/runs/21416963179/job/63655402439?pr=4704

Probably unrelated to this PR. Let's merge this

Copy link
Member

@AkihiroSuda AkihiroSuda left a comment

Choose a reason for hiding this comment

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

Thanks

@AkihiroSuda AkihiroSuda merged commit 92a7d9a into containerd:main Feb 18, 2026
299 of 320 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Support stdin/stdout streaming (-) in nerdctl cp

3 participants