diff options
-rw-r--r-- | cmd/podman/cliconfig/config.go | 13 | ||||
-rw-r--r-- | cmd/podman/commit.go | 17 | ||||
-rw-r--r-- | cmd/podman/images.go | 7 | ||||
-rw-r--r-- | docs/podman-commit.1.md | 4 | ||||
-rw-r--r-- | libpod/container_commit.go | 17 | ||||
-rw-r--r-- | libpod/container_internal.go | 8 | ||||
-rw-r--r-- | libpod/oci_linux.go | 53 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 7 | ||||
-rw-r--r-- | pkg/spec/spec.go | 3 | ||||
-rw-r--r-- | test/e2e/commit_test.go | 19 | ||||
-rw-r--r-- | test/e2e/images_test.go | 11 | ||||
-rw-r--r-- | test/e2e/prune_test.go | 4 | ||||
-rw-r--r-- | test/e2e/rmi_test.go | 2 | ||||
-rw-r--r-- | test/system/030-run.bats | 8 |
14 files changed, 138 insertions, 35 deletions
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index f7ac0de6c..2692ace36 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -88,12 +88,13 @@ type CheckpointValues struct { type CommitValues struct { PodmanCommand - Change []string - Format string - Message string - Author string - Pause bool - Quiet bool + Change []string + Format string + Message string + Author string + Pause bool + Quiet bool + IncludeVolumes bool } type ContainersPrune struct { diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go index f7e206856..0077ff297 100644 --- a/cmd/podman/commit.go +++ b/cmd/podman/commit.go @@ -2,19 +2,19 @@ package main import ( "fmt" - "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/spf13/cobra" "io" "os" "strings" "github.com/containers/buildah" "github.com/containers/image/manifest" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" + "github.com/spf13/cobra" ) var ( @@ -47,7 +47,7 @@ func init() { flags.StringVarP(&commitCommand.Author, "author", "a", "", "Set the author for the image committed") flags.BoolVarP(&commitCommand.Pause, "pause", "p", false, "Pause container during commit") flags.BoolVarP(&commitCommand.Quiet, "quiet", "q", false, "Suppress output") - + flags.BoolVar(&commitCommand.IncludeVolumes, "include-volumes", false, "Include container volumes as image volumes") } func commitCmd(c *cliconfig.CommitValues) error { @@ -109,11 +109,12 @@ func commitCmd(c *cliconfig.CommitValues) error { PreferredManifestType: mimeType, } options := libpod.ContainerCommitOptions{ - CommitOptions: coptions, - Pause: c.Pause, - Message: c.Message, - Changes: c.Change, - Author: c.Author, + CommitOptions: coptions, + Pause: c.Pause, + IncludeVolumes: c.IncludeVolumes, + Message: c.Message, + Changes: c.Change, + Author: c.Author, } newImage, err := ctr.Commit(getContext(), reference, options) if err != nil { diff --git a/cmd/podman/images.go b/cmd/podman/images.go index 6133450be..bea27e2ff 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -318,13 +318,14 @@ func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage) func generateImagesOutput(ctx context.Context, images []*adapter.ContainerImage, opts imagesOptions) error { templateMap := GenImageOutputMap() - if len(images) == 0 { - return nil - } var out formats.Writer switch opts.format { case formats.JSONString: + // If 0 images are present, print nothing for JSON + if len(images) == 0 { + return nil + } imagesOutput := getImagesJSONOutput(ctx, images) out = formats.JSONStructArray{Output: imagesToGeneric([]imagesTemplateParams{}, imagesOutput)} default: diff --git a/docs/podman-commit.1.md b/docs/podman-commit.1.md index acde51859..7c74d7a33 100644 --- a/docs/podman-commit.1.md +++ b/docs/podman-commit.1.md @@ -39,6 +39,10 @@ not specifically set, the default format used is _oci_. Write the image ID to the file. +**--include-volumes** + +Include in the committed image any volumes added to the container by the `--volume` or `--mount` options to the `podman create` and `podman run` commands. + **--message, -m** Set commit message for committed image. The message field is not supported in _oci_ format. diff --git a/libpod/container_commit.go b/libpod/container_commit.go index 0604a550b..db67f7a30 100644 --- a/libpod/container_commit.go +++ b/libpod/container_commit.go @@ -20,10 +20,11 @@ import ( //libpod type ContainerCommitOptions struct { buildah.CommitOptions - Pause bool - Author string - Message string - Changes []string + Pause bool + IncludeVolumes bool + Author string + Message string + Changes []string } // ChangeCmds is the list of valid Changes commands to passed to the Commit call @@ -113,9 +114,11 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai // User importBuilder.SetUser(c.User()) // Volumes - for _, v := range c.config.UserVolumes { - if v != "" { - importBuilder.AddVolume(v) + if options.IncludeVolumes { + for _, v := range c.config.UserVolumes { + if v != "" { + importBuilder.AddVolume(v) + } } } // Workdir diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 3c7319963..36b5e01df 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -350,7 +350,7 @@ func (c *Container) teardownStorage() error { artifacts := filepath.Join(c.config.StaticDir, artifactsDir) if err := os.RemoveAll(artifacts); err != nil { - return errors.Wrapf(err, "error removing artifacts %q", artifacts) + return errors.Wrapf(err, "error removing container %s artifacts %q", c.ID(), artifacts) } if err := c.cleanupStorage(); err != nil { @@ -1113,13 +1113,13 @@ func (c *Container) cleanup(ctx context.Context) error { // Remove healthcheck unit/timer file if it execs if c.config.HealthCheckConfig != nil { if err := c.removeTimer(); err != nil { - logrus.Error(err) + logrus.Errorf("Error removing timer for container %s healthcheck: %v", c.ID(), err) } } // Clean up network namespace, if present if err := c.cleanupNetwork(); err != nil { - lastError = err + lastError = errors.Wrapf(err, "error removing container %s network", c.ID()) } // Unmount storage @@ -1127,7 +1127,7 @@ func (c *Container) cleanup(ctx context.Context) error { if lastError != nil { logrus.Errorf("Error unmounting container %s storage: %v", c.ID(), err) } else { - lastError = err + lastError = errors.Wrapf(err, "error unmounting container %s storage", c.ID()) } } diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go index 8c0abad80..01f7c3649 100644 --- a/libpod/oci_linux.go +++ b/libpod/oci_linux.go @@ -3,15 +3,20 @@ package libpod import ( + "fmt" "os" "os/exec" "path/filepath" + "runtime" "strings" "syscall" "github.com/containerd/cgroups" + "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/utils" + pmount "github.com/containers/storage/pkg/mount" spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -91,6 +96,54 @@ func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restor return err } } + + // if we are running a non privileged container, be sure to umount some kernel paths so they are not + // bind mounted inside the container at all. + if !ctr.config.Privileged && !rootless.IsRootless() { + ch := make(chan error) + go func() { + runtime.LockOSThread() + err := func() error { + fd, err := os.Open(fmt.Sprintf("/proc/%d/task/%d/ns/mnt", os.Getpid(), unix.Gettid())) + if err != nil { + return err + } + defer fd.Close() + + // create a new mountns on the current thread + if err = unix.Unshare(unix.CLONE_NEWNS); err != nil { + return err + } + defer unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS) + + // don't spread our mounts around. We are setting only /sys to be slave + // so that the cleanup process is still able to umount the storage and the + // changes are propagated to the host. + err = unix.Mount("/sys", "/sys", "none", unix.MS_REC|unix.MS_SLAVE, "") + if err != nil { + return errors.Wrapf(err, "cannot make /sys slave") + } + + mounts, err := pmount.GetMounts() + if err != nil { + return err + } + for _, m := range mounts { + if !strings.HasPrefix(m.Mountpoint, "/sys/kernel") { + continue + } + err = unix.Unmount(m.Mountpoint, 0) + if err != nil { + return errors.Wrapf(err, "cannot unmount %s", m.Mountpoint) + } + } + return r.createOCIContainer(ctr, cgroupParent, restoreOptions) + }() + ch <- err + }() + err := <-ch + return err + } } return r.createOCIContainer(ctr, cgroupParent, restoreOptions) } diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 800b42851..85b860268 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -372,7 +372,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, // Clean up network namespace, cgroups, mounts if err := c.cleanup(ctx); err != nil { if cleanupErr == nil { - cleanupErr = err + cleanupErr = errors.Wrapf(err, "error cleaning up container %s", c.ID()) } else { logrus.Errorf("cleanup network, cgroups, mounts: %v", err) } @@ -404,12 +404,14 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, // Deallocate the container's lock if err := c.lock.Free(); err != nil { if cleanupErr == nil { - cleanupErr = err + cleanupErr = errors.Wrapf(err, "error freeing lock for container %s", c.ID()) } else { logrus.Errorf("free container lock: %v", err) } } + c.newContainerEvent(events.Remove) + if !removeVolume { return cleanupErr } @@ -425,7 +427,6 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, } } - c.newContainerEvent(events.Remove) return cleanupErr } diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go index 9b6bd089e..0371b6d4d 100644 --- a/pkg/spec/spec.go +++ b/pkg/spec/spec.go @@ -132,6 +132,9 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"}, } g.AddMount(sysMnt) + if !config.Privileged && isRootless { + g.AddLinuxMaskedPaths("/sys/kernel") + } } if isRootless { nGids, err := getAvailableGids() diff --git a/test/e2e/commit_test.go b/test/e2e/commit_test.go index fe4ae64cf..93e1ea7af 100644 --- a/test/e2e/commit_test.go +++ b/test/e2e/commit_test.go @@ -131,7 +131,7 @@ var _ = Describe("Podman commit", func() { Expect(check.ExitCode()).To(Equal(0)) }) - It("podman commit with volume mounts", func() { + It("podman commit with volumes mounts and no include-volumes", func() { s := podmanTest.Podman([]string{"run", "--name", "test1", "-v", "/tmp:/foo", "alpine", "date"}) s.WaitWithDefaultTimeout() Expect(s.ExitCode()).To(Equal(0)) @@ -145,6 +145,23 @@ var _ = Describe("Podman commit", func() { Expect(inspect.ExitCode()).To(Equal(0)) image := inspect.InspectImageJSON() _, ok := image[0].Config.Volumes["/foo"] + Expect(ok).To(BeFalse()) + }) + + It("podman commit with volume mounts and --include-volumes", func() { + s := podmanTest.Podman([]string{"run", "--name", "test1", "-v", "/tmp:/foo", "alpine", "date"}) + s.WaitWithDefaultTimeout() + Expect(s.ExitCode()).To(Equal(0)) + + c := podmanTest.Podman([]string{"commit", "--include-volumes", "test1", "newimage"}) + c.WaitWithDefaultTimeout() + Expect(c.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "newimage"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + image := inspect.InspectImageJSON() + _, ok := image[0].Config.Volumes["/foo"] Expect(ok).To(BeTrue()) r := podmanTest.Podman([]string{"run", "newimage"}) diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index a253dff63..48a964db4 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -43,6 +43,17 @@ var _ = Describe("Podman images", func() { Expect(session.LineInOuputStartsWith("docker.io/library/busybox")).To(BeTrue()) }) + It("podman images with no images prints header", func() { + rmi := podmanTest.Podman([]string{"rmi", "-a"}) + rmi.WaitWithDefaultTimeout() + Expect(rmi.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"images"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(1)) + }) + It("podman image List", func() { session := podmanTest.Podman([]string{"image", "list"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index 869ca3289..682f7ff2b 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -82,7 +82,7 @@ var _ = Describe("Podman rm", func() { prune.WaitWithDefaultTimeout() Expect(prune.ExitCode()).To(Equal(0)) - images := podmanTest.Podman([]string{"images", "-a"}) + images := podmanTest.Podman([]string{"images", "-aq"}) images.WaitWithDefaultTimeout() // all images are unused, so they all should be deleted! Expect(len(images.OutputToStringArray())).To(Equal(0)) @@ -95,7 +95,7 @@ var _ = Describe("Podman rm", func() { prune.WaitWithDefaultTimeout() Expect(prune.ExitCode()).To(Equal(0)) - images := podmanTest.Podman([]string{"images", "-a"}) + images := podmanTest.Podman([]string{"images", "-aq"}) images.WaitWithDefaultTimeout() // all images are unused, so they all should be deleted! Expect(len(images.OutputToStringArray())).To(Equal(0)) diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go index 78d175637..e034f24cf 100644 --- a/test/e2e/rmi_test.go +++ b/test/e2e/rmi_test.go @@ -270,7 +270,7 @@ RUN find $LOCAL fmt.Println(session.OutputToString()) Expect(session.ExitCode()).To(Equal(0)) - images := podmanTest.Podman([]string{"images", "--all"}) + images := podmanTest.Podman([]string{"images", "-aq"}) images.WaitWithDefaultTimeout() Expect(images.ExitCode()).To(Equal(0)) Expect(len(images.OutputToStringArray())).To(Equal(0)) diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 8ae68f33d..188070550 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -31,4 +31,12 @@ echo $rand | 0 | $rand done < <(parse_table "$tests") } +@test "podman run - uidmapping has no /sys/kernel mounts" { + run_podman $expected_rc run --uidmapping 0:100:10000 $IMAGE mount | grep /sys/kernel + is "$output" "" "podman run $cmd - output" + + run_podman $expected_rc run --net host --uidmapping 0:100:10000 $IMAGE mount | grep /sys/kernel + is "$output" "" "podman run $cmd - output" +} + # vim: filetype=sh |