-
Notifications
You must be signed in to change notification settings - Fork 604
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
feat: Add support for copying files in and out of stopped containers #2355
Conversation
7476e98
to
0b2fbc2
Compare
0b2fbc2
to
b805ee9
Compare
@@ -408,9 +408,10 @@ type ContainerListOptions struct { | |||
|
|||
// ContainerCpOptions specifies options for `nerdctl (container) cp` | |||
type ContainerCpOptions struct { | |||
// GOptions is the global options. | |||
GOptions GlobalCommandOptions | |||
ContainerReq string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add godoc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
pkg/containerutil/cp_linux.go
Outdated
mount.Unmount(tempDir, 0) | ||
os.RemoveAll(tempDir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe RemoveAll
should be called when unmount succeeds. And cleanup failure should print a warning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
pkg/containerutil/cp_linux.go
Outdated
err = mount.All(resp, tempDir) | ||
if err != nil { | ||
cleanup := func() { | ||
os.RemoveAll(tempDir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not remove it now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
pkg/containerutil/cp_linux.go
Outdated
if rootlessutil.IsRootless() { | ||
nsenter := []string{"nsenter", "-t", strconv.Itoa(pid), "-U", "--preserve-credentials", "--"} | ||
if container2host { | ||
tarC = append(nsenter, tarC...) | ||
} else { | ||
tarX = append(nsenter, tarX...) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can it be safely removed when status.Status == containerd.Running
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite understand this part, in rootless mode its already in the namespace of the rootlesskit child pid. Am I missing anything here? The integration tests pass okay when status.Status == containerd.Running
.
Update: After realizing that enabling copying out of stopped/created containers in rootless mode causes regression, reverted this back to the original state.
b805ee9
to
a2e5d95
Compare
a2e5d95
to
d08c919
Compare
Please try rebasing to make the CI green |
d08c919
to
d8ecbad
Compare
cmd/nerdctl/main_linux.go
Outdated
@@ -39,16 +39,15 @@ func appNeedsRootlessParentMain(cmd *cobra.Command, args []string) bool { | |||
switch commands[1] { | |||
// completion, login, logout: false, because it shouldn't require the daemon to be running | |||
// apparmor: false, because it requires the initial mount namespace to access /sys/kernel/security | |||
// cp: false, because it requires the initial mount namespace to inspect file owners |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I modified appNeedsRootlessParentMain
to return true for nerdctl cp
and nerdctl container cp
. This was necessary to access containerd.sock in rootless mode to create containerd client. When it's running in rootless mode without this change, get /run/containerd/containerd.sock": no such file or directory
. However this change does not preserve the uid/gid mapping while copying to container. Should we disable the copying behaviour for stopped/created containers in rootless mode and only support this feature in rootful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this change does not preserve the uid/gid mapping while copying to container.
This is a regression.
Should we disable the copying behaviour for stopped/created containers in rootless mode and only support this feature in rootful.
Yes, at least for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AkihiroSuda I updated the PR to
- use rootless containerd sock based on faq.md in rootless mode.
- Disabled copying behaviour for stopped/created containers in rootless mode and only support this feature in rootful(more info in PR description).
- The uid/gid mapping is preserved when files are copied.
PTAL, thanks! I noticed that the docker-compatibility check fails although all the tests for container copy pass. Not sure if this is related to this change. PTAL, thanks for the review.
cmd/nerdctl/main_linux.go
Outdated
return false | ||
case "container": | ||
if len(commands) < 3 { | ||
return true | ||
} | ||
switch commands[2] { | ||
case "cp": | ||
return false | ||
return true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true
is returned on L53
4288d50
to
cfb8af9
Compare
if err != nil { | ||
return "", err | ||
} | ||
return path.Join(fmt.Sprintf("/proc/%d/root/run/containerd/containerd.sock", childPid)), nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return path.Join(fmt.Sprintf("/proc/%d/root/run/containerd/containerd.sock", childPid)), nil | |
return filepath.Join(fmt.Sprintf("/proc/%d/root/run/containerd/containerd.sock", childPid)), nil |
pkg/containerutil/cp_linux.go
Outdated
} | ||
err = mount.All(resp, tempDir) | ||
if err != nil { | ||
os.RemoveAll(tempDir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unsafe, as tempDir may contain partial mounts
pkg/containerutil/cp_linux.go
Outdated
task, err := container.Task(ctx, nil) | ||
if err != nil { | ||
// if the task is simply not found, we should try to mount the snapshot. any other type of error from Task() is fatal here. | ||
// Rootless does not support copying out of stopped/created containers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add the reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment explaining. Lmk if I need to additional details.
Signed-off-by: Vishwas Siravara <siravara@amazon.com>
cfb8af9
to
0d0cbb8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks
What does this PR do
This adds support to copy files in and out of non running containers only in rootful mode thereby improving feature compatibility with podman and docker. It fixes #1058
High level design
This PR adds logic in 4 packages
main(Modifies parsing logic fornerdctl cp
command)ContainerCpOptions
struct)Fine level Details
cmd/nerdctl/container_cp_linux.go
processCpOptions
function to includeGlobalCommandOptions
. Deletes logic forcontainer inspect
as we don't rely on inspect to findpid
of the running container. For rootless modeoptions.GOptions.Address=/proc/<PID of containerd>/root/run/containerd/containerd.sock
based on faq.mdModifiescpAction
to includecontainerd.Client
which is used for searching container by long/short id and bySnapshotService
to get the underlyingSnapshotter
.main_linux.goModifiesappNeedsRootlessParentMain
to return true fornerdctl cp
andnerdctl container cp
. This is necessary to access containerd.sock in rootless mode.pkg/api/types/container_types.go
ContainerCpOptions
to addGlobalCommandOptions
andContainerReq
.GlobalCommandOptions
are necessary for determining namespace, conatinerd socket address and name of underlying snapshotter.ContainerReq
required for finding container by short/long id.pkg/cmd/container/cp_linux.go
nerdctl cp <containerid>
command usingContainerWalker
. If multiple containers are found it returns an error.pkg/containerutil/cp_linux.go
[Task](https://github.com/containerd/containerd/blob/main/task.go#L154)
from containerd. If it's not found(which is the case onnerdctl container create
, it mounts the snapshot onto a temporary directory for copying. Any other type of error returns an error.For rootless mode it deletes the nsenter command to enter the namespace of the container process since it's already in the namespace of the rootlesskit child pid.mountSnapshotForContainer
which returns the temp directory that the container shapshot is mounted on.pkg/rootlessutil/rootlessutil_linux.go
Testing
Added tests for stopped containers for exactly the same existing test cases as running containers. Tests are skipped in rootless mode on stopped containers.