Skip to content
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

Merged
merged 1 commit into from
Jul 24, 2023

Conversation

vsiravar
Copy link
Contributor

@vsiravar vsiravar commented Jul 5, 2023

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 for nerdctl cp command)
  • types(Modifies ContainerCpOptions struct)
  • container(Adds logic to search for container by long/short Id)
  • containerutil(Adds logic to copy files out and into non running containers)
  • rootlessutil(Add a function to retrieve rootless containerd socket address)

Fine level Details

cmd/nerdctl/container_cp_linux.go

  • Modifies processCpOptions function to include GlobalCommandOptions. Deletes logic for container inspect as we don't rely on inspect to find pid of the running container. For rootless mode options.GOptions.Address=/proc/<PID of containerd>/root/run/containerd/containerd.sock based on faq.md
  • Modifies cpAction to include containerd.Client which is used for searching container by long/short id and by SnapshotService to get the underlying Snapshotter.

main_linux.go

  • Modifies appNeedsRootlessParentMain to return true for nerdctl cp and nerdctl container cp. This is necessary to access containerd.sock in rootless mode.

pkg/api/types/container_types.go

  • Modifies ContainerCpOptions to add GlobalCommandOptions and ContainerReq. 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

  • Searches for container specified in nerdctl cp <containerid> command using ContainerWalker. If multiple containers are found it returns an error.
  • It returns an error when the specified container for copy is not found.

pkg/containerutil/cp_linux.go

  • Use containerd snapshot service to copy files into and out of created/stopped containers.
  • It tries to find [Task](https://github.com/containerd/containerd/blob/main/task.go#L154) from containerd. If it's not found(which is the case on nerdctl container create, it mounts the snapshot onto a temporary directory for copying. Any other type of error returns an error.
  • If the status of the containerd task is Running, then the root filesystem of the running process is used for copying files into /out of the container. (unchanged from previous commits)
  • If task is found and status is not Running(Exited container), then we fallback to the snapshot service for copying.
  • 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.
  • Introduces a helper function mountSnapshotForContainer which returns the temp directory that the container shapshot is mounted on.

pkg/rootlessutil/rootlessutil_linux.go

  • Adds a function to retrieve rootless containerd socket address

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.

@vsiravar vsiravar marked this pull request as draft July 5, 2023 06:17
@vsiravar vsiravar force-pushed the vsiravar/fix-1058 branch 2 times, most recently from 7476e98 to 0b2fbc2 Compare July 6, 2023 23:58
@vsiravar vsiravar marked this pull request as ready for review July 7, 2023 00:41
@@ -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
Copy link
Member

Choose a reason for hiding this comment

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

Could you add godoc

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

Comment on lines 227 to 246
mount.Unmount(tempDir, 0)
os.RemoveAll(tempDir)
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

err = mount.All(resp, tempDir)
if err != nil {
cleanup := func() {
os.RemoveAll(tempDir)
Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

Comment on lines 141 to 148
if rootlessutil.IsRootless() {
nsenter := []string{"nsenter", "-t", strconv.Itoa(pid), "-U", "--preserve-credentials", "--"}
if container2host {
tarC = append(nsenter, tarC...)
} else {
tarX = append(nsenter, tarX...)
}
}
Copy link
Member

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?

Copy link
Contributor Author

@vsiravar vsiravar Jul 10, 2023

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.

@AkihiroSuda
Copy link
Member

Please try rebasing to make the CI green

@@ -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
Copy link
Member

Choose a reason for hiding this comment

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

Why was this changed?

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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.

return false
case "container":
if len(commands) < 3 {
return true
}
switch commands[2] {
case "cp":
return false
return true
Copy link
Member

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

@vsiravar vsiravar force-pushed the vsiravar/fix-1058 branch 2 times, most recently from 4288d50 to cfb8af9 Compare July 20, 2023 00:29
if err != nil {
return "", err
}
return path.Join(fmt.Sprintf("/proc/%d/root/run/containerd/containerd.sock", childPid)), nil
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
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

}
err = mount.All(resp, tempDir)
if err != nil {
os.RemoveAll(tempDir)
Copy link
Member

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

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
Copy link
Member

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?

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 a comment explaining. Lmk if I need to additional details.

Signed-off-by: Vishwas Siravara <siravara@amazon.com>
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 b3a546f into containerd:main Jul 24, 2023
21 checks passed
@AkihiroSuda AkihiroSuda mentioned this pull request Jul 25, 2023
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.

cp should also work on created containers
3 participants