From c14b6c366288c875155d8bbb33c322843b438741 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Wed, 12 Aug 2020 09:15:02 -0400 Subject: Change /sys/fs/cgroup/systemd mount to rprivate I used the wrong propagation first time around because I forgot that rprivate is the default propagation. Oops. Switch to rprivate so we're using the default. Signed-off-by: Matthew Heon --- libpod/container_internal_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libpod') diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index d72e3ad47..b9e4f9a93 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -575,7 +575,7 @@ func (c *Container) setupSystemd(mounts []spec.Mount, g generate.Generator) erro Destination: "/sys/fs/cgroup/systemd", Type: "bind", Source: "/sys/fs/cgroup/systemd", - Options: []string{"bind", "nodev", "noexec", "nosuid"}, + Options: []string{"bind", "nodev", "noexec", "nosuid", "rprivate"}, } g.AddMount(systemdMnt) g.AddLinuxMaskedPaths("/sys/fs/cgroup/systemd/release_agent") -- cgit v1.2.3-54-g00ecf From c22ea20bf6f12ccf289a48cc185d5159ce7fdaf4 Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Mon, 3 Aug 2020 12:53:33 -0500 Subject: add event for image build upon image build completion, a new image type event is written for "build". more intricate details, like pulling an image, that might be done by build must be implemented in different vendored packages only after libpod is split from podman. Fixes: #7022 Signed-off-by: Brent Baude Signed-off-by: Matt Heon --- libpod/events/config.go | 2 ++ libpod/events/events.go | 2 ++ libpod/runtime_img.go | 16 ++++++++++++++++ 3 files changed, 20 insertions(+) (limited to 'libpod') diff --git a/libpod/events/config.go b/libpod/events/config.go index c34408e63..bb35c03c0 100644 --- a/libpod/events/config.go +++ b/libpod/events/config.go @@ -101,6 +101,8 @@ const ( Attach Status = "attach" // AutoUpdate ... AutoUpdate Status = "auto-update" + // Build ... + Build Status = "build" // Checkpoint ... Checkpoint Status = "checkpoint" // Cleanup ... diff --git a/libpod/events/events.go b/libpod/events/events.go index 0253b1ee5..722c9595e 100644 --- a/libpod/events/events.go +++ b/libpod/events/events.go @@ -127,6 +127,8 @@ func StringToStatus(name string) (Status, error) { switch name { case Attach.String(): return Attach, nil + case Build.String(): + return Build, nil case Checkpoint.String(): return Checkpoint, nil case Cleanup.String(): diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go index 7c75dbf98..1a82cdc07 100644 --- a/libpod/runtime_img.go +++ b/libpod/runtime_img.go @@ -11,7 +11,11 @@ import ( "github.com/containers/buildah/imagebuildah" "github.com/containers/image/v5/docker/reference" + ociarchive "github.com/containers/image/v5/oci/archive" + "github.com/containers/image/v5/oci/layout" + "github.com/containers/image/v5/types" "github.com/containers/libpod/v2/libpod/define" + "github.com/containers/libpod/v2/libpod/events" "github.com/containers/libpod/v2/libpod/image" "github.com/containers/libpod/v2/pkg/util" "github.com/containers/storage" @@ -147,9 +151,21 @@ func removeStorageContainers(ctrIDs []string, store storage.Store) error { return nil } +// newBuildEvent creates a new event based on completion of a built image +func (r *Runtime) newImageBuildCompleteEvent(idOrName string) { + e := events.NewEvent(events.Build) + e.Type = events.Image + e.Name = idOrName + if err := r.eventer.Write(e); err != nil { + logrus.Errorf("unable to write build event: %q", err) + } +} + // Build adds the runtime to the imagebuildah call func (r *Runtime) Build(ctx context.Context, options imagebuildah.BuildOptions, dockerfiles ...string) (string, reference.Canonical, error) { id, ref, err := imagebuildah.BuildDockerfiles(ctx, r.store, options, dockerfiles...) + // Write event for build completion + r.newImageBuildCompleteEvent(id) return id, ref, err } -- cgit v1.2.3-54-g00ecf From d4c3365454d903077ece3c1a31367f639ee24900 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Fri, 31 Jul 2020 17:08:06 -0400 Subject: Ensure WORKDIR from images is created A recent crun change stopped the creation of the container's working directory if it does not exist. This is arguably correct for user-specified directories, to protect against typos; it is definitely not correct for image WORKDIR, where the image author definitely intended for the directory to be used. This makes Podman create the working directory and chown it to container root, if it does not already exist, and only if it was specified by an image, not the user. Signed-off-by: Matthew Heon --- libpod/container.go | 4 ++++ libpod/container_internal_linux.go | 27 ++++++++++++++++++++++++++- libpod/options.go | 13 +++++++++++++ pkg/specgen/generate/container_create.go | 11 +++++++++++ test/e2e/run_test.go | 10 ++++++++++ 5 files changed, 64 insertions(+), 1 deletion(-) (limited to 'libpod') diff --git a/libpod/container.go b/libpod/container.go index 9ad938a5c..644647bc9 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -261,6 +261,10 @@ type ContainerConfig struct { Mounts []string `json:"mounts,omitempty"` // NamedVolumes lists the named volumes to mount into the container. NamedVolumes []*ContainerNamedVolume `json:"namedVolumes,omitempty"` + // CreateWorkingDir indicates that Libpod should create the container's + // working directory if it does not exist. Some OCI runtimes do this by + // default, but others do not. + CreateWorkingDir bool `json:"createWorkingDir,omitempty"` // Security Config diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index b9e4f9a93..0d9a1c824 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -157,7 +157,32 @@ func (c *Container) prepare() error { } // Save changes to container state - return c.save() + if err := c.save(); err != nil { + return err + } + + // Ensure container entrypoint is created (if required) + if c.config.CreateWorkingDir { + workdir, err := securejoin.SecureJoin(c.state.Mountpoint, c.WorkingDir()) + if err != nil { + return errors.Wrapf(err, "error creating path to container %s working dir", c.ID()) + } + rootUID := c.RootUID() + rootGID := c.RootGID() + + if err := os.MkdirAll(workdir, 0755); err != nil { + if os.IsExist(err) { + return nil + } + return errors.Wrapf(err, "error creating container %s working dir", c.ID()) + } + + if err := os.Chown(workdir, rootUID, rootGID); err != nil { + return errors.Wrapf(err, "error chowning container %s working directory to container root", c.ID()) + } + } + + return nil } // cleanupNetwork unmounts and cleans up the container's network diff --git a/libpod/options.go b/libpod/options.go index 560b406e2..a4e4b99e9 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1395,6 +1395,19 @@ func WithCreateCommand(cmd []string) CtrCreateOption { } } +// WithCreateWorkingDir tells Podman to create the container's working directory +// if it does not exist. +func WithCreateWorkingDir() CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + + ctr.config.CreateWorkingDir = true + return nil + } +} + // Volume Creation Options // WithVolumeName sets the name of the volume. diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index b31bc91e0..42be5e812 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -215,6 +215,17 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. if s.Entrypoint != nil { options = append(options, libpod.WithEntrypoint(s.Entrypoint)) } + // If the user did not set an workdir but the image did, ensure it is + // created. + if s.WorkDir == "" && img != nil { + newWD, err := img.WorkingDir(ctx) + if err != nil { + return nil, err + } + if newWD != "" { + options = append(options, libpod.WithCreateWorkingDir()) + } + } if s.StopSignal != nil { options = append(options, libpod.WithStopSignal(*s.StopSignal)) } diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 13f0db550..ef275b32e 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -1060,4 +1060,14 @@ USER mail` Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring(limit)) }) + + It("podman run makes entrypoint from image", func() { + dockerfile := `FROM busybox +WORKDIR /madethis` + podmanTest.BuildImage(dockerfile, "test", "false") + session := podmanTest.Podman([]string{"run", "--rm", "test", "pwd"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("/madethis")) + }) }) -- cgit v1.2.3-54-g00ecf From eff0c29098fe988327112c37884d57a7b244ac40 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Tue, 4 Aug 2020 15:51:13 -0400 Subject: Unconditionally retrieve pod names via API The ListContainers API previously had a Pod parameter, which determined if pod name was returned (but, notably, not Pod ID, which was returned unconditionally). This was fairly confusing, so we decided to deprecate/remove the parameter and return it unconditionally. To do this without serious performance implications, we need to avoid expensive JSON decodes of pod configuration in the DB. The way our Bolt tables are structured, retrieving name given ID is actually quite cheap, but we did not expose this via the Libpod API. Add a new GetName API to do this. Fixes #7214 Signed-off-by: Matthew Heon --- libpod/boltdb_state.go | 55 +++++++++++++++++++++++++++++++++++ libpod/in_memory_state.go | 30 +++++++++++++++++++ libpod/runtime.go | 16 ++++++++++ libpod/state.go | 6 ++++ pkg/api/handlers/libpod/containers.go | 3 +- pkg/api/server/register_containers.go | 3 +- pkg/bindings/containers/containers.go | 5 +--- pkg/bindings/test/containers_test.go | 19 ++++++++++-- pkg/domain/infra/tunnel/containers.go | 2 +- pkg/domain/infra/tunnel/helpers.go | 2 +- pkg/ps/ps.go | 7 +++-- 11 files changed, 134 insertions(+), 14 deletions(-) (limited to 'libpod') diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index 38881d3e4..4e2630526 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -424,6 +424,61 @@ func (s *BoltState) SetNamespace(ns string) error { return nil } +// GetName returns the name associated with a given ID. Since IDs are globally +// unique, it works for both containers and pods. +// Returns ErrNoSuchCtr if the ID does not exist. +func (s *BoltState) GetName(id string) (string, error) { + if id == "" { + return "", define.ErrEmptyID + } + + if !s.valid { + return "", define.ErrDBClosed + } + + idBytes := []byte(id) + + db, err := s.getDBCon() + if err != nil { + return "", err + } + defer s.deferredCloseDBCon(db) + + name := "" + + err = db.View(func(tx *bolt.Tx) error { + idBkt, err := getIDBucket(tx) + if err != nil { + return err + } + + nameBytes := idBkt.Get(idBytes) + if nameBytes == nil { + return define.ErrNoSuchCtr + } + + if s.namespaceBytes != nil { + nsBkt, err := getNSBucket(tx) + if err != nil { + return err + } + + idNs := nsBkt.Get(idBytes) + if !bytes.Equal(idNs, s.namespaceBytes) { + return define.ErrNoSuchCtr + } + } + + name = string(nameBytes) + return nil + }) + if err != nil { + return "", err + } + + return name, nil +} + // Container retrieves a single container from the state by its full ID func (s *BoltState) Container(id string) (*Container, error) { if id == "" { diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go index 794212bf0..b0eae0992 100644 --- a/libpod/in_memory_state.go +++ b/libpod/in_memory_state.go @@ -106,6 +106,36 @@ func (s *InMemoryState) SetNamespace(ns string) error { return nil } +// GetName retrieves the name associated with a given ID. +// Works with both Container and Pod IDs. +func (s *InMemoryState) GetName(id string) (string, error) { + if id == "" { + return "", define.ErrEmptyID + } + + var idIndex *truncindex.TruncIndex + if s.namespace != "" { + nsIndex, ok := s.namespaceIndexes[s.namespace] + if !ok { + // We have no containers in the namespace + // Return false + return "", define.ErrNoSuchCtr + } + idIndex = nsIndex.idIndex + } else { + idIndex = s.idIndex + } + + fullID, err := idIndex.Get(id) + if err != nil { + if err == truncindex.ErrNotExist { + return "", define.ErrNoSuchCtr + } + return "", errors.Wrapf(err, "error performing truncindex lookup for ID %s", id) + } + return fullID, nil +} + // Container retrieves a container from its full ID func (s *InMemoryState) Container(id string) (*Container, error) { if id == "" { diff --git a/libpod/runtime.go b/libpod/runtime.go index 24370d50e..867644be7 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -732,6 +732,22 @@ func (r *Runtime) GetStore() storage.Store { return r.store } +// GetName retrieves the name associated with a given full ID. +// This works for both containers and pods, and does not distinguish between the +// two. +// If the given ID does not correspond to any existing Pod or Container, +// ErrNoSuchCtr is returned. +func (r *Runtime) GetName(id string) (string, error) { + r.lock.RLock() + defer r.lock.RUnlock() + + if !r.valid { + return "", define.ErrRuntimeStopped + } + + return r.state.GetName(id) +} + // DBConfig is a set of Libpod runtime configuration settings that are saved in // a State when it is first created, and can subsequently be retrieved. type DBConfig struct { diff --git a/libpod/state.go b/libpod/state.go index 6206a2994..44632b02f 100644 --- a/libpod/state.go +++ b/libpod/state.go @@ -43,6 +43,12 @@ type State interface { // containers and pods in all namespaces will be returned. SetNamespace(ns string) error + // Resolve an ID into a Name. Since Podman names and IDs are globally + // unique between Pods and Containers, the ID may belong to either a pod + // or container. Despite this, we will always return ErrNoSuchCtr if the + // ID does not exist. + GetName(id string) (string, error) + // Return a container from the database from its full ID. // If the container is not in the set namespace, an error will be // returned. diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 2303ff17a..abfc79a0b 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -39,7 +39,6 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { Filters map[string][]string `schema:"filters"` Last int `schema:"last"` Namespace bool `schema:"namespace"` - Pod bool `schema:"pod"` Size bool `schema:"size"` Sync bool `schema:"sync"` }{ @@ -59,7 +58,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { Size: query.Size, Sort: "", Namespace: query.Namespace, - Pod: query.Pod, + Pod: true, Sync: query.Sync, } pss, err := ps.GetContainerLists(runtime, opts) diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index 18ff2f423..a0c9ac574 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -661,11 +661,10 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // type: boolean // description: Include namespace information // default: false - // - in: query // name: pod // type: boolean // default: false - // description: Include Pod ID and Name if applicable + // description: Ignored. Previously included details on pod name and ID that are currently included by default. // - in: query // name: size // type: boolean diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index c479e5dcb..d7e67567b 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -24,7 +24,7 @@ var ( // the most recent number of containers. The pod and size booleans indicate that pod information and rootfs // size information should also be included. Finally, the sync bool synchronizes the OCI runtime and // container state. -func List(ctx context.Context, filters map[string][]string, all *bool, last *int, pod, size, sync *bool) ([]entities.ListContainer, error) { // nolint:typecheck +func List(ctx context.Context, filters map[string][]string, all *bool, last *int, size, sync *bool) ([]entities.ListContainer, error) { // nolint:typecheck conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -37,9 +37,6 @@ func List(ctx context.Context, filters map[string][]string, all *bool, last *int if last != nil { params.Set("last", strconv.Itoa(*last)) } - if pod != nil { - params.Set("pod", strconv.FormatBool(*pod)) - } if size != nil { params.Set("size", strconv.FormatBool(*size)) } diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index 4b2c78353..0685a3377 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -509,7 +509,7 @@ var _ = Describe("Podman containers ", func() { Expect(err).To(BeNil()) _, err = bt.RunTopContainer(&name2, bindings.PFalse, nil) Expect(err).To(BeNil()) - containerLatestList, err := containers.List(bt.conn, nil, nil, &latestContainers, nil, nil, nil) + containerLatestList, err := containers.List(bt.conn, nil, nil, &latestContainers, nil, nil) Expect(err).To(BeNil()) err = containers.Kill(bt.conn, containerLatestList[0].Names[0], "SIGTERM") Expect(err).To(BeNil()) @@ -754,8 +754,23 @@ var _ = Describe("Podman containers ", func() { // Validate list container with id filter filters := make(map[string][]string) filters["id"] = []string{cid} - c, err := containers.List(bt.conn, filters, bindings.PTrue, nil, nil, nil, nil) + c, err := containers.List(bt.conn, filters, bindings.PTrue, nil, nil, nil) Expect(err).To(BeNil()) Expect(len(c)).To(Equal(1)) }) + + It("List containers always includes pod information", func() { + podName := "testpod" + ctrName := "testctr" + bt.Podcreate(&podName) + _, err := bt.RunTopContainer(&ctrName, bindings.PTrue, &podName) + Expect(err).To(BeNil()) + + lastNum := 1 + + c, err := containers.List(bt.conn, nil, bindings.PTrue, &lastNum, nil, nil) + Expect(err).To(BeNil()) + Expect(len(c)).To(Equal(1)) + Expect(c[0].PodName).To(Equal(podName)) + }) }) diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 8835248ca..dd72064a3 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -496,7 +496,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri } func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) { - return containers.List(ic.ClientCxt, options.Filters, &options.All, &options.Last, &options.Pod, &options.Size, &options.Sync) + return containers.List(ic.ClientCxt, options.Filters, &options.All, &options.Last, &options.Size, &options.Sync) } func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go index 9974c4d1d..90e52c5c0 100644 --- a/pkg/domain/infra/tunnel/helpers.go +++ b/pkg/domain/infra/tunnel/helpers.go @@ -20,7 +20,7 @@ func getContainersByContext(contextWithConnection context.Context, all bool, nam if all && len(namesOrIDs) > 0 { return nil, errors.New("cannot lookup containers and all") } - c, err := containers.List(contextWithConnection, nil, bindings.PTrue, nil, nil, nil, bindings.PTrue) + c, err := containers.List(contextWithConnection, nil, bindings.PTrue, nil, nil, bindings.PTrue) if err != nil { return nil, err } diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index 2b81311af..d4fd96596 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -175,11 +175,14 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities State: conState.String(), } if opts.Pod && len(conConfig.Pod) > 0 { - pod, err := rt.GetPod(conConfig.Pod) + podName, err := rt.GetName(conConfig.Pod) if err != nil { + if errors.Cause(err) == define.ErrNoSuchCtr { + return entities.ListContainer{}, errors.Wrapf(define.ErrNoSuchPod, "could not find container %s pod (id %s) in state", conConfig.ID, conConfig.Pod) + } return entities.ListContainer{}, err } - ps.PodName = pod.Name() + ps.PodName = podName } if opts.Namespace { -- cgit v1.2.3-54-g00ecf From 579360e862ceb82e88b65081d1b3fe3dc8ae2680 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Tue, 18 Aug 2020 16:30:29 -0400 Subject: Fix imports for runtime_img.go Signed-off-by: Matthew Heon --- libpod/runtime_img.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'libpod') diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go index 1a82cdc07..8f2f82ad3 100644 --- a/libpod/runtime_img.go +++ b/libpod/runtime_img.go @@ -10,22 +10,18 @@ import ( "os" "github.com/containers/buildah/imagebuildah" + "github.com/containers/image/v5/directory" + dockerarchive "github.com/containers/image/v5/docker/archive" "github.com/containers/image/v5/docker/reference" ociarchive "github.com/containers/image/v5/oci/archive" - "github.com/containers/image/v5/oci/layout" - "github.com/containers/image/v5/types" "github.com/containers/libpod/v2/libpod/define" "github.com/containers/libpod/v2/libpod/events" "github.com/containers/libpod/v2/libpod/image" "github.com/containers/libpod/v2/pkg/util" "github.com/containers/storage" + v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" - - "github.com/containers/image/v5/directory" - dockerarchive "github.com/containers/image/v5/docker/archive" - ociarchive "github.com/containers/image/v5/oci/archive" - v1 "github.com/opencontainers/image-spec/specs-go/v1" ) // Runtime API -- cgit v1.2.3-54-g00ecf From 402d002184fa33b8aa7bfe91a83b04ba80425e28 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Wed, 19 Aug 2020 17:41:36 -0400 Subject: Unmount c/storage containers before removing them When `podman rmi --force` is run, it will remove any containers that depend on the image. This includes Podman containers, but also any other c/storage users who may be using it. With Podman containers, we use the standard Podman removal function for containers, which handles all edge cases nicely, shutting down running containers, ensuring they're unmounted, etc. Unfortunately, no such convient function exists (or can exist) for all c/storage containers. Identifying the PID of a Buildah, CRI-O, or Podman container is extremely different, and those are just the implementations under the containers org. We can't reasonably be able to know if a c/storage container is *in use* and safe for removal if it's not a Podman container. At the very least, though, we can attempt to unmount a storage container before removing it. If it is in use, this will fail (probably with a not-particularly-helpful error message), but if it is not in use but not fully cleaned up, this should make our removing it much more robust than it normally is. Signed-off-by: Matthew Heon --- libpod/runtime_img.go | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'libpod') diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go index 8f2f82ad3..9515aa468 100644 --- a/libpod/runtime_img.go +++ b/libpod/runtime_img.go @@ -140,6 +140,10 @@ func storageContainers(imageID string, store storage.Store) ([]string, error) { // Removes the containers passed in the array. func removeStorageContainers(ctrIDs []string, store storage.Store) error { for _, ctrID := range ctrIDs { + if _, err := store.Unmount(ctrID, true); err != nil { + return errors.Wrapf(err, "could not unmount container %q to remove it", ctrID) + } + if err := store.DeleteContainer(ctrID); err != nil { return errors.Wrapf(err, "could not remove container %q", ctrID) } -- cgit v1.2.3-54-g00ecf