summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xAPI.md18
-rw-r--r--Dockerfile2
-rw-r--r--cmd/podman/cp.go34
-rw-r--r--cmd/podman/create.go6
-rw-r--r--cmd/podman/exec.go28
-rw-r--r--cmd/podman/export.go5
-rw-r--r--cmd/podman/kill.go7
-rw-r--r--cmd/podman/main.go87
-rw-r--r--cmd/podman/mount.go4
-rw-r--r--cmd/podman/pod.go47
-rw-r--r--cmd/podman/pod_kill.go2
-rw-r--r--cmd/podman/pod_restart.go14
-rw-r--r--cmd/podman/pod_rm.go13
-rw-r--r--cmd/podman/pod_stop.go14
-rw-r--r--cmd/podman/pod_top.go25
-rw-r--r--cmd/podman/ps.go4
-rw-r--r--cmd/podman/restart.go30
-rw-r--r--cmd/podman/rm.go84
-rw-r--r--cmd/podman/run.go4
-rw-r--r--cmd/podman/run_test.go15
-rw-r--r--cmd/podman/shared/create.go76
-rw-r--r--cmd/podman/stop.go2
-rw-r--r--cmd/podman/top.go14
-rw-r--r--cmd/podman/tree.go77
-rw-r--r--cmd/podman/varlink/io.podman.varlink10
-rw-r--r--docs/podman-volume-rm.1.md3
-rw-r--r--libpod/boltdb_state.go79
-rw-r--r--libpod/boltdb_state_internal.go53
-rw-r--r--libpod/container.go33
-rw-r--r--libpod/container_internal.go16
-rw-r--r--libpod/container_internal_linux.go18
-rw-r--r--libpod/image/image.go28
-rw-r--r--libpod/in_memory_state.go32
-rw-r--r--libpod/options.go92
-rw-r--r--libpod/runtime.go30
-rw-r--r--libpod/runtime_ctr.go100
-rw-r--r--libpod/runtime_volume_linux.go24
-rw-r--r--libpod/state.go4
-rw-r--r--pkg/adapter/images.go34
-rw-r--r--pkg/adapter/images_remote.go32
-rw-r--r--pkg/adapter/runtime.go73
-rw-r--r--pkg/adapter/runtime_remote.go14
-rw-r--r--pkg/annotations/annotations.go27
-rw-r--r--pkg/rootless/rootless.go9
-rw-r--r--pkg/rootless/rootless_linux.c41
-rw-r--r--pkg/rootless/rootless_linux.go277
-rw-r--r--pkg/rootless/rootless_unsupported.go45
-rw-r--r--pkg/spec/createconfig.go130
-rw-r--r--pkg/spec/spec.go40
-rw-r--r--pkg/varlinkapi/images.go38
-rw-r--r--test/e2e/commit_test.go2
-rw-r--r--test/e2e/run_test.go1
-rw-r--r--test/e2e/volume_rm_test.go4
53 files changed, 711 insertions, 1190 deletions
diff --git a/API.md b/API.md
index f1c1b89cd..0accf5009 100755
--- a/API.md
+++ b/API.md
@@ -5,6 +5,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func BuildImage(build: BuildInfo) MoreResponse](#BuildImage)
+[func BuildImageHierarchyMap(name: string) string](#BuildImageHierarchyMap)
+
[func Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) string](#Commit)
[func ContainerArtifacts(name: string, artifactName: string) string](#ContainerArtifacts)
@@ -57,6 +59,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func GetInfo() PodmanInfo](#GetInfo)
+[func GetLayersMapWithImageInfo() string](#GetLayersMapWithImageInfo)
+
[func GetPod(name: string) ListPodData](#GetPod)
[func GetPodStats(name: string) string, ContainerStats](#GetPodStats)
@@ -259,6 +263,11 @@ method BuildImage(build: [BuildInfo](#BuildInfo)) [MoreResponse](#MoreResponse)<
BuildImage takes a [BuildInfo](#BuildInfo) structure and builds an image. At a minimum, you must provide the
'dockerfile' and 'tags' options in the BuildInfo structure. It will return a [MoreResponse](#MoreResponse) structure
that contains the build logs and resulting image ID.
+### <a name="BuildImageHierarchyMap"></a>func BuildImageHierarchyMap
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method BuildImageHierarchyMap(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
+BuildImageHierarchyMap is for the development of Podman and should not be used.
### <a name="Commit"></a>func Commit
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -396,7 +405,7 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.DeleteUnusedImages
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method Diff(name: [string](https://godoc.org/builtin#string)) [DiffInfo](#DiffInfo)</div>
-
+Diff returns a diff between libpod objects
### <a name="ExportContainer"></a>func ExportContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -520,6 +529,11 @@ If the image caGetImage returns be found, [ImageNotFound](#ImageNotFound) will b
method GetInfo() [PodmanInfo](#PodmanInfo)</div>
GetInfo returns a [PodmanInfo](#PodmanInfo) struct that describes podman and its host such as storage stats,
build information of Podman, and system-wide registries.
+### <a name="GetLayersMapWithImageInfo"></a>func GetLayersMapWithImageInfo
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method GetLayersMapWithImageInfo() [string](https://godoc.org/builtin#string)</div>
+GetLayersMapWithImageInfo is for the development of Podman and should not be used.
### <a name="GetPod"></a>func GetPod
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -1507,6 +1521,8 @@ containers [int](https://godoc.org/builtin#int)
labels [map[string]](#map[string])
isParent [bool](https://godoc.org/builtin#bool)
+
+topLayer [string](https://godoc.org/builtin#string)
### <a name="ImageHistory"></a>type ImageHistory
ImageHistory describes the returned structure from ImageHistory.
diff --git a/Dockerfile b/Dockerfile
index e0da63ac3..83cd3fccd 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -38,6 +38,8 @@ RUN apt-get update && apt-get install -y \
socat \
lsof \
xz-utils \
+ unzip \
+ python3-yaml \
--no-install-recommends \
&& apt-get clean
diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go
index 18fb2cb73..a0dd46260 100644
--- a/cmd/podman/cp.go
+++ b/cmd/podman/cp.go
@@ -1,10 +1,8 @@
package main
import (
- "io/ioutil"
"os"
"path/filepath"
- "strconv"
"strings"
"github.com/containers/buildah/pkg/chrootuser"
@@ -12,7 +10,6 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/chrootarchive"
@@ -58,9 +55,6 @@ func cpCmd(c *cliconfig.CpValues) error {
if len(args) != 2 {
return errors.Errorf("you must provide a source path and a destination path")
}
- if os.Geteuid() != 0 {
- rootless.SetSkipStorageSetup(true)
- }
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
if err != nil {
@@ -90,34 +84,6 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin
ctr = destCtr
}
- if os.Geteuid() != 0 {
- s, err := ctr.State()
- if err != nil {
- return err
- }
- var became bool
- var ret int
- if s == libpod.ContainerStateRunning || s == libpod.ContainerStatePaused {
- data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile)
- if err != nil {
- return errors.Wrapf(err, "cannot read conmon PID file %q", ctr.Config().ConmonPidFile)
- }
- conmonPid, err := strconv.Atoi(string(data))
- if err != nil {
- return errors.Wrapf(err, "cannot parse PID %q", data)
- }
- became, ret, err = rootless.JoinDirectUserAndMountNS(uint(conmonPid))
- } else {
- became, ret, err = rootless.BecomeRootInUserNS()
- }
- if err != nil {
- return err
- }
- if became {
- os.Exit(ret)
- }
- }
-
mountPoint, err := ctr.Mount()
if err != nil {
return err
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index bceb606f6..984323653 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -2,12 +2,10 @@ package main
import (
"fmt"
- "os"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/pkg/rootless"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -54,10 +52,6 @@ func createCmd(c *cliconfig.CreateValues) error {
return err
}
- if os.Geteuid() != 0 {
- rootless.SetSkipStorageSetup(true)
- }
-
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go
index fc1c76e9f..f720a9aff 100644
--- a/cmd/podman/exec.go
+++ b/cmd/podman/exec.go
@@ -10,7 +10,6 @@ import (
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared/parse"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -67,7 +66,6 @@ func execCmd(c *cliconfig.ExecValues) error {
if c.Latest {
argStart = 0
}
- rootless.SetSkipStorageSetup(true)
cmd := args[argStart:]
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
if err != nil {
@@ -107,32 +105,6 @@ func execCmd(c *cliconfig.ExecValues) error {
}
- if os.Geteuid() != 0 {
- var became bool
- var ret int
-
- data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile)
- if err == nil {
- conmonPid, err := strconv.Atoi(string(data))
- if err != nil {
- return errors.Wrapf(err, "cannot parse PID %q", data)
- }
- became, ret, err = rootless.JoinDirectUserAndMountNS(uint(conmonPid))
- } else {
- pid, err := ctr.PID()
- if err != nil {
- return err
- }
- became, ret, err = rootless.JoinNS(uint(pid), c.PreserveFDs)
- }
- if err != nil {
- return err
- }
- if became {
- os.Exit(ret)
- }
- }
-
// ENVIRONMENT VARIABLES
env := map[string]string{}
diff --git a/cmd/podman/export.go b/cmd/podman/export.go
index 92633facd..db031aaf2 100644
--- a/cmd/podman/export.go
+++ b/cmd/podman/export.go
@@ -6,7 +6,6 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared/parse"
"github.com/containers/libpod/pkg/adapter"
- "github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -41,10 +40,6 @@ func init() {
// exportCmd saves a container to a tarball on disk
func exportCmd(c *cliconfig.ExportValues) error {
- if os.Geteuid() != 0 {
- rootless.SetSkipStorageSetup(true)
- }
-
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go
index 2c1e13eaf..6019fbfec 100644
--- a/cmd/podman/kill.go
+++ b/cmd/podman/kill.go
@@ -4,12 +4,10 @@ import (
"fmt"
"reflect"
- "github.com/containers/libpod/pkg/adapter"
- "github.com/opentracing/opentracing-go"
-
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/docker/docker/pkg/signal"
+ "github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -63,7 +61,6 @@ func killCmd(c *cliconfig.KillValues) error {
return err
}
- rootless.SetSkipStorageSetup(true)
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index cff9a6961..1ba58d1f3 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -3,13 +3,16 @@ package main
import (
"context"
"io"
+ "io/ioutil"
"log/syslog"
"os"
"runtime/pprof"
+ "strconv"
"strings"
"syscall"
"github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
_ "github.com/containers/libpod/pkg/hooks/0.1.0"
"github.com/containers/libpod/pkg/rootless"
@@ -60,36 +63,6 @@ var mainCommands = []*cobra.Command{
systemCommand.Command,
}
-var cmdsNotRequiringRootless = map[*cobra.Command]bool{
- _versionCommand: true,
- _createCommand: true,
- _execCommand: true,
- _cpCommand: true,
- _exportCommand: true,
- //// `info` must be executed in an user namespace.
- //// If this change, please also update libpod.refreshRootless()
- _loginCommand: true,
- _logoutCommand: true,
- _mountCommand: true,
- _killCommand: true,
- _pauseCommand: true,
- _podRmCommand: true,
- _podKillCommand: true,
- _podRestartCommand: true,
- _podStatsCommand: true,
- _podStopCommand: true,
- _podTopCommand: true,
- _restartCommand: true,
- &_psCommand: true,
- _rmCommand: true,
- _runCommand: true,
- _unpauseCommand: true,
- _searchCommand: true,
- _statsCommand: true,
- _stopCommand: true,
- _topCommand: true,
-}
-
var rootCmd = &cobra.Command{
Use: "podman",
Long: "manage pods and images",
@@ -153,18 +126,52 @@ func before(cmd *cobra.Command, args []string) error {
logrus.Errorf(err.Error())
os.Exit(1)
}
- if rootless.IsRootless() {
- notRequireRootless := cmdsNotRequiringRootless[cmd]
- if !notRequireRootless && !strings.HasPrefix(cmd.Use, "help") {
- became, ret, err := rootless.BecomeRootInUserNS()
- if err != nil {
- logrus.Errorf(err.Error())
- os.Exit(1)
- }
- if became {
- os.Exit(ret)
+ if os.Geteuid() != 0 && cmd != _searchCommand && cmd != _versionCommand && !strings.HasPrefix(cmd.Use, "help") {
+ podmanCmd := cliconfig.PodmanCommand{
+ cmd,
+ args,
+ MainGlobalOpts,
+ }
+ runtime, err := libpodruntime.GetRuntime(&podmanCmd)
+ if err != nil {
+ return errors.Wrapf(err, "could not get runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ ctrs, err := runtime.GetRunningContainers()
+ if err != nil {
+ logrus.Errorf(err.Error())
+ os.Exit(1)
+ }
+ var became bool
+ var ret int
+ if len(ctrs) == 0 {
+ became, ret, err = rootless.BecomeRootInUserNS()
+ } else {
+ for _, ctr := range ctrs {
+ data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile)
+ if err != nil {
+ logrus.Errorf(err.Error())
+ os.Exit(1)
+ }
+ conmonPid, err := strconv.Atoi(string(data))
+ if err != nil {
+ logrus.Errorf(err.Error())
+ os.Exit(1)
+ }
+ became, ret, err = rootless.JoinUserAndMountNS(uint(conmonPid))
+ if err == nil {
+ break
+ }
}
}
+ if err != nil {
+ logrus.Errorf(err.Error())
+ os.Exit(1)
+ }
+ if became {
+ os.Exit(ret)
+ }
}
if MainGlobalOpts.Syslog {
diff --git a/cmd/podman/mount.go b/cmd/podman/mount.go
index 138548097..a70684a39 100644
--- a/cmd/podman/mount.go
+++ b/cmd/podman/mount.go
@@ -60,10 +60,6 @@ type jsonMountPoint struct {
}
func mountCmd(c *cliconfig.MountValues) error {
- if os.Geteuid() != 0 {
- rootless.SetSkipStorageSetup(true)
- }
-
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go
index 9a9c7a702..2d9bca21d 100644
--- a/cmd/podman/pod.go
+++ b/cmd/podman/pod.go
@@ -1,12 +1,7 @@
package main
import (
- "os"
-
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/pkg/adapter"
- "github.com/containers/libpod/pkg/rootless"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -39,48 +34,6 @@ var podSubCommands = []*cobra.Command{
_podUnpauseCommand,
}
-func joinPodNS(runtime *adapter.LocalRuntime, all, latest bool, inputArgs []string) ([]string, bool, bool, error) {
- if rootless.IsRootless() {
- if os.Geteuid() == 0 {
- return []string{rootless.Argument()}, false, false, nil
- } else {
- var err error
- var pods []*adapter.Pod
- if all {
- pods, err = runtime.GetAllPods()
- if err != nil {
- return nil, false, false, errors.Wrapf(err, "unable to get pods")
- }
- } else if latest {
- pod, err := runtime.GetLatestPod()
- if err != nil {
- return nil, false, false, errors.Wrapf(err, "unable to get latest pod")
- }
- pods = append(pods, pod)
- } else {
- for _, i := range inputArgs {
- pod, err := runtime.LookupPod(i)
- if err != nil {
- return nil, false, false, errors.Wrapf(err, "unable to lookup pod %s", i)
- }
- pods = append(pods, pod)
- }
- }
- for _, p := range pods {
- _, ret, err := runtime.JoinOrCreateRootlessPod(p)
- if err != nil {
- return nil, false, false, err
- }
- if ret != 0 {
- os.Exit(ret)
- }
- }
- os.Exit(0)
- }
- }
- return inputArgs, all, latest, nil
-}
-
func init() {
podCommand.AddCommand(podSubCommands...)
podCommand.SetHelpTemplate(HelpTemplate())
diff --git a/cmd/podman/pod_kill.go b/cmd/podman/pod_kill.go
index c538674a4..ebd7db762 100644
--- a/cmd/podman/pod_kill.go
+++ b/cmd/podman/pod_kill.go
@@ -6,7 +6,6 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/pkg/adapter"
- "github.com/containers/libpod/pkg/rootless"
"github.com/docker/docker/pkg/signal"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -49,7 +48,6 @@ func init() {
// podKillCmd kills one or more pods with a signal
func podKillCmd(c *cliconfig.PodKillValues) error {
- rootless.SetSkipStorageSetup(true)
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
diff --git a/cmd/podman/pod_restart.go b/cmd/podman/pod_restart.go
index 9c8d28424..0765b98db 100644
--- a/cmd/podman/pod_restart.go
+++ b/cmd/podman/pod_restart.go
@@ -2,11 +2,9 @@ package main
import (
"fmt"
- "os"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/pkg/adapter"
- "github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -48,24 +46,12 @@ func init() {
func podRestartCmd(c *cliconfig.PodRestartValues) error {
var lastError error
- if os.Geteuid() != 0 {
- rootless.SetSkipStorageSetup(true)
- }
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
- if rootless.IsRootless() {
- var err error
-
- c.InputArgs, c.All, c.Latest, err = joinPodNS(runtime, c.All, c.Latest, c.InputArgs)
- if err != nil {
- return err
- }
- }
-
restartIDs, conErrors, restartErrors := runtime.RestartPods(getContext(), c)
for _, p := range restartIDs {
diff --git a/cmd/podman/pod_rm.go b/cmd/podman/pod_rm.go
index 735676f8a..cd9f23fe1 100644
--- a/cmd/podman/pod_rm.go
+++ b/cmd/podman/pod_rm.go
@@ -2,11 +2,9 @@ package main
import (
"fmt"
- "os"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/pkg/adapter"
- "github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -48,23 +46,12 @@ func init() {
// podRmCmd deletes pods
func podRmCmd(c *cliconfig.PodRmValues) error {
- if os.Geteuid() != 0 {
- rootless.SetSkipStorageSetup(true)
- }
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
- if rootless.IsRootless() {
- var err error
- c.InputArgs, c.All, c.Latest, err = joinPodNS(runtime, c.All, c.Latest, c.InputArgs)
- if err != nil {
- return err
- }
- }
-
podRmIds, podRmErrors := runtime.RemovePods(getContext(), c)
for _, p := range podRmIds {
fmt.Println(p)
diff --git a/cmd/podman/pod_stop.go b/cmd/podman/pod_stop.go
index 754a3a7db..f1b0ac51f 100644
--- a/cmd/podman/pod_stop.go
+++ b/cmd/podman/pod_stop.go
@@ -2,11 +2,9 @@ package main
import (
"fmt"
- "os"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/pkg/adapter"
- "github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -48,24 +46,12 @@ func init() {
}
func podStopCmd(c *cliconfig.PodStopValues) error {
- if os.Geteuid() != 0 {
- rootless.SetSkipStorageSetup(true)
- }
-
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
- if rootless.IsRootless() {
- var err error
- c.InputArgs, c.All, c.Latest, err = joinPodNS(runtime, c.All, c.Latest, c.InputArgs)
- if err != nil {
- return err
- }
- }
-
podStopIds, podStopErrors := runtime.StopPods(getContext(), c)
for _, p := range podStopIds {
fmt.Println(p)
diff --git a/cmd/podman/pod_top.go b/cmd/podman/pod_top.go
index f65d66df6..0d74dc3d6 100644
--- a/cmd/podman/pod_top.go
+++ b/cmd/podman/pod_top.go
@@ -9,7 +9,6 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -54,10 +53,6 @@ func podTopCmd(c *cliconfig.PodTopValues) error {
)
args := c.InputArgs
- if os.Geteuid() != 0 {
- rootless.SetSkipStorageSetup(true)
- }
-
if c.ListDescriptors {
descriptors, err := libpod.GetContainerPidInformationDescriptors()
if err != nil {
@@ -83,26 +78,6 @@ func podTopCmd(c *cliconfig.PodTopValues) error {
descriptors = args[1:]
}
- if os.Geteuid() != 0 {
- var pod *adapter.Pod
- var err error
- if c.Latest {
- pod, err = runtime.GetLatestPod()
- } else {
- pod, err = runtime.LookupPod(c.InputArgs[0])
- }
- if err != nil {
- return errors.Wrapf(err, "unable to lookup requested container")
- }
- became, ret, err := runtime.JoinOrCreateRootlessPod(pod)
- if err != nil {
- return err
- }
- if became {
- os.Exit(ret)
- }
- }
-
w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
psOutput, err := runtime.PodTop(c, descriptors)
if err != nil {
diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go
index 71c8dee97..759a03b86 100644
--- a/cmd/podman/ps.go
+++ b/cmd/podman/ps.go
@@ -17,7 +17,6 @@ import (
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-units"
@@ -202,9 +201,6 @@ func init() {
}
func psCmd(c *cliconfig.PsValues) error {
- if os.Geteuid() != 0 {
- rootless.SetSkipStorageSetup(true)
- }
if c.Bool("trace") {
span, _ := opentracing.StartSpanFromContext(Ctx, "psCmd")
defer span.Finish()
diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go
index e6a6d8434..1553ab805 100644
--- a/cmd/podman/restart.go
+++ b/cmd/podman/restart.go
@@ -1,13 +1,10 @@
package main
import (
- "os"
-
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -57,19 +54,6 @@ func restartCmd(c *cliconfig.RestartValues) error {
restartContainers []*libpod.Container
)
- if os.Geteuid() != 0 {
- rootless.SetSkipStorageSetup(true)
- }
- if rootless.IsRootless() {
- // If we are in the re-execed rootless environment,
- // override the arg to deal only with one container.
- if os.Geteuid() == 0 {
- c.All = false
- c.Latest = false
- c.InputArgs = []string{rootless.Argument()}
- }
- }
-
args := c.InputArgs
runOnly := c.Running
all := c.All
@@ -115,20 +99,6 @@ func restartCmd(c *cliconfig.RestartValues) error {
}
}
- if os.Geteuid() != 0 {
- // In rootless mode we can deal with one container at at time.
- for _, c := range restartContainers {
- _, ret, err := joinContainerOrCreateRootlessUserNS(runtime, c)
- if err != nil {
- return err
- }
- if ret != 0 {
- os.Exit(ret)
- }
- }
- os.Exit(0)
- }
-
maxWorkers := shared.Parallelize("restart")
if c.GlobalIsSet("max-workers") {
maxWorkers = c.GlobalFlags.MaxWorks
diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go
index 253771e14..52e281402 100644
--- a/cmd/podman/rm.go
+++ b/cmd/podman/rm.go
@@ -2,16 +2,12 @@ package main
import (
"fmt"
- "io/ioutil"
- "os"
- "strconv"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
- "github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -52,39 +48,11 @@ func init() {
markFlagHiddenForRemoteClient("latest", flags)
}
-func joinContainerOrCreateRootlessUserNS(runtime *libpod.Runtime, ctr *libpod.Container) (bool, int, error) {
- if os.Geteuid() == 0 {
- return false, 0, nil
- }
- s, err := ctr.State()
- if err != nil {
- return false, -1, err
- }
- opts := rootless.Opts{
- Argument: ctr.ID(),
- }
- if s == libpod.ContainerStateRunning || s == libpod.ContainerStatePaused {
- data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile)
- if err != nil {
- return false, -1, errors.Wrapf(err, "cannot read conmon PID file %q", ctr.Config().ConmonPidFile)
- }
- conmonPid, err := strconv.Atoi(string(data))
- if err != nil {
- return false, -1, errors.Wrapf(err, "cannot parse PID %q", data)
- }
- return rootless.JoinDirectUserAndMountNSWithOpts(uint(conmonPid), &opts)
- }
- return rootless.BecomeRootInUserNSWithOpts(&opts)
-}
-
// saveCmd saves the image to either docker-archive or oci
func rmCmd(c *cliconfig.RmValues) error {
var (
deleteFuncs []shared.ParallelWorkerInput
)
- if os.Geteuid() != 0 {
- rootless.SetSkipStorageSetup(true)
- }
ctx := getContext()
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
@@ -93,58 +61,6 @@ func rmCmd(c *cliconfig.RmValues) error {
}
defer runtime.Shutdown(false)
- if rootless.IsRootless() {
- // When running in rootless mode we cannot manage different containers and
- // user namespaces from the same context, so be sure to re-exec once for each
- // container we are dealing with.
- // What we do is to first collect all the containers we want to delete, then
- // we re-exec in each of the container namespaces and from there remove the single
- // container.
- var container *libpod.Container
- if os.Geteuid() == 0 {
- // We are in the namespace, override InputArgs with the single
- // argument that was passed down to us.
- c.All = false
- c.Latest = false
- c.InputArgs = []string{rootless.Argument()}
- } else {
- exitCode = 0
- var containers []*libpod.Container
- if c.All {
- containers, err = runtime.GetContainers()
- } else if c.Latest {
- container, err = runtime.GetLatestContainer()
- if err != nil {
- return errors.Wrapf(err, "unable to get latest pod")
- }
- containers = append(containers, container)
- } else {
- for _, c := range c.InputArgs {
- container, err = runtime.LookupContainer(c)
- if err != nil {
- if errors.Cause(err) == libpod.ErrNoSuchCtr {
- exitCode = 1
- continue
- }
- return err
- }
- containers = append(containers, container)
- }
- }
- // Now we really delete the containers.
- for _, c := range containers {
- _, ret, err := joinContainerOrCreateRootlessUserNS(runtime, c)
- if err != nil {
- return err
- }
- if ret != 0 {
- os.Exit(ret)
- }
- }
- os.Exit(exitCode)
- }
- }
-
failureCnt := 0
delContainers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, -1, "all")
if err != nil {
diff --git a/cmd/podman/run.go b/cmd/podman/run.go
index 3c26e98c1..4bd469106 100644
--- a/cmd/podman/run.go
+++ b/cmd/podman/run.go
@@ -12,7 +12,6 @@ import (
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/rootless"
opentracing "github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -57,9 +56,6 @@ func runCmd(c *cliconfig.RunValues) error {
if err := createInit(&c.PodmanCommand); err != nil {
return err
}
- if os.Geteuid() != 0 {
- rootless.SetSkipStorageSetup(true)
- }
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
if err != nil {
diff --git a/cmd/podman/run_test.go b/cmd/podman/run_test.go
index a896f1dc7..0bf9cb4d9 100644
--- a/cmd/podman/run_test.go
+++ b/cmd/podman/run_test.go
@@ -8,6 +8,7 @@ import (
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/pkg/inspect"
cc "github.com/containers/libpod/pkg/spec"
+ "github.com/containers/libpod/pkg/sysinfo"
"github.com/docker/go-units"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
spec "github.com/opencontainers/runtime-spec/specs-go"
@@ -16,8 +17,9 @@ import (
)
var (
- cmd = []string{"podman", "test", "alpine"}
- CLI *cliconfig.PodmanCommand
+ sysInfo = sysinfo.New(true)
+ cmd = []string{"podman", "test", "alpine"}
+ CLI *cliconfig.PodmanCommand
)
// generates a mocked ImageData structure based on alpine
@@ -100,6 +102,9 @@ func TestPIDsLimit(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("seccomp, which is enabled by default, is only supported on Linux")
}
+ if !sysInfo.PidsLimit {
+ t.Skip("running test not supported by the host system")
+ }
args := []string{"--pids-limit", "22"}
a := createCLI(args)
a.InputArgs = args
@@ -119,6 +124,9 @@ func TestBLKIOWeightDevice(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("seccomp, which is enabled by default, is only supported on Linux")
}
+ if !sysInfo.BlkioWeightDevice {
+ t.Skip("running test not supported by the host system")
+ }
args := []string{"--blkio-weight-device", "/dev/zero:100"}
a := createCLI(args)
a.InputArgs = args
@@ -137,6 +145,9 @@ func TestMemorySwap(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("seccomp, which is enabled by default, is only supported on Linux")
}
+ if !sysInfo.SwapLimit {
+ t.Skip("running test not supported by the host system")
+ }
args := []string{"--memory-swap", "45m", "--memory", "40m"}
a := createCLI(args)
a.InputArgs = args
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index d927e5bf6..cd82e4f1c 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"io"
- "io/ioutil"
"os"
"path/filepath"
"strconv"
@@ -75,7 +74,8 @@ func CreateContainer(ctx context.Context, c *cliconfig.PodmanCommand, runtime *l
imageName := ""
var data *inspect.ImageData = nil
- if rootfs == "" && !rootless.SkipStorageSetup() {
+ // Set the storage if we are running as euid == 0 and there is no rootfs specified
+ if rootfs == "" && os.Geteuid() == 0 {
var writer io.Writer
if !c.Bool("quiet") {
writer = os.Stderr
@@ -758,71 +758,6 @@ type namespace interface {
Container() string
}
-func joinOrCreateRootlessUserNamespace(createConfig *cc.CreateConfig, runtime *libpod.Runtime) (bool, int, error) {
- if os.Geteuid() == 0 {
- return false, 0, nil
- }
-
- if createConfig.Pod != "" {
- pod, err := runtime.LookupPod(createConfig.Pod)
- if err != nil {
- return false, -1, err
- }
- inspect, err := pod.Inspect()
- for _, ctr := range inspect.Containers {
- prevCtr, err := runtime.LookupContainer(ctr.ID)
- if err != nil {
- return false, -1, err
- }
- s, err := prevCtr.State()
- if err != nil {
- return false, -1, err
- }
- if s != libpod.ContainerStateRunning && s != libpod.ContainerStatePaused {
- continue
- }
- data, err := ioutil.ReadFile(prevCtr.Config().ConmonPidFile)
- if err != nil {
- return false, -1, errors.Wrapf(err, "cannot read conmon PID file %q", prevCtr.Config().ConmonPidFile)
- }
- conmonPid, err := strconv.Atoi(string(data))
- if err != nil {
- return false, -1, errors.Wrapf(err, "cannot parse PID %q", data)
- }
- return rootless.JoinDirectUserAndMountNS(uint(conmonPid))
- }
- }
-
- namespacesStr := []string{string(createConfig.IpcMode), string(createConfig.NetMode), string(createConfig.UsernsMode), string(createConfig.PidMode), string(createConfig.UtsMode)}
- for _, i := range namespacesStr {
- if cc.IsNS(i) {
- return rootless.JoinNSPath(cc.NS(i))
- }
- }
-
- namespaces := []namespace{createConfig.IpcMode, createConfig.NetMode, createConfig.UsernsMode, createConfig.PidMode, createConfig.UtsMode}
- for _, i := range namespaces {
- if i.IsContainer() {
- ctr, err := runtime.LookupContainer(i.Container())
- if err != nil {
- return false, -1, err
- }
- pid, err := ctr.PID()
- if err != nil {
- return false, -1, err
- }
- if pid == 0 {
- if createConfig.Pod != "" {
- continue
- }
- return false, -1, errors.Errorf("dependency container %s is not running", ctr.ID())
- }
- return rootless.JoinNS(uint(pid), 0)
- }
- }
- return rootless.BecomeRootInUserNS()
-}
-
func CreateContainerFromCreateConfig(r *libpod.Runtime, createConfig *cc.CreateConfig, ctx context.Context, pod *libpod.Pod) (*libpod.Container, error) {
runtimeSpec, err := cc.CreateConfigToOCISpec(createConfig)
if err != nil {
@@ -833,13 +768,6 @@ func CreateContainerFromCreateConfig(r *libpod.Runtime, createConfig *cc.CreateC
if err != nil {
return nil, err
}
- became, ret, err := joinOrCreateRootlessUserNamespace(createConfig, r)
- if err != nil {
- return nil, err
- }
- if became {
- os.Exit(ret)
- }
ctr, err := r.NewContainer(ctx, runtimeSpec, options...)
if err != nil {
diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go
index 2a1470ad0..e27be64f6 100644
--- a/cmd/podman/stop.go
+++ b/cmd/podman/stop.go
@@ -7,7 +7,6 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/adapter"
- "github.com/containers/libpod/pkg/rootless"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -59,7 +58,6 @@ func stopCmd(c *cliconfig.StopValues) error {
defer span.Finish()
}
- rootless.SetSkipStorageSetup(true)
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
diff --git a/cmd/podman/top.go b/cmd/podman/top.go
index 2512631c1..5d394d2d6 100644
--- a/cmd/podman/top.go
+++ b/cmd/podman/top.go
@@ -9,7 +9,6 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -77,7 +76,6 @@ func topCmd(c *cliconfig.TopValues) error {
return errors.Errorf("you must provide the name or id of a running container")
}
- rootless.SetSkipStorageSetup(true)
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
@@ -104,18 +102,6 @@ func topCmd(c *cliconfig.TopValues) error {
if conStat != libpod.ContainerStateRunning {
return errors.Errorf("top can only be used on running containers")
}
-
- pid, err := container.PID()
- if err != nil {
- return err
- }
- became, ret, err := rootless.JoinNS(uint(pid), 0)
- if err != nil {
- return err
- }
- if became {
- os.Exit(ret)
- }
psOutput, err := container.GetContainerPidInformation(descriptors)
if err != nil {
return err
diff --git a/cmd/podman/tree.go b/cmd/podman/tree.go
index c56e35aef..371e88495 100644
--- a/cmd/podman/tree.go
+++ b/cmd/podman/tree.go
@@ -5,9 +5,9 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod/image"
- units "github.com/docker/go-units"
+ "github.com/containers/libpod/pkg/adapter"
+ "github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -41,16 +41,6 @@ func init() {
treeCommand.Flags().BoolVar(&treeCommand.WhatRequires, "whatrequires", false, "Show all child images and layers of the specified image")
}
-// infoImage keep information of Image along with all associated layers
-type infoImage struct {
- // id of image
- id string
- // tags of image
- tags []string
- // layers stores all layers of image.
- layers []image.LayerInfo
-}
-
func treeCmd(c *cliconfig.TreeValues) error {
args := c.InputArgs
if len(args) == 0 {
@@ -60,46 +50,33 @@ func treeCmd(c *cliconfig.TreeValues) error {
return errors.Errorf("you must provide at most 1 argument")
}
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
defer runtime.Shutdown(false)
-
- img, err := runtime.ImageRuntime().NewFromLocal(args[0])
+ imageInfo, layerInfoMap, img, err := runtime.Tree(c)
if err != nil {
return err
}
+ return printTree(imageInfo, layerInfoMap, img, c.WhatRequires)
+}
- // Fetch map of image-layers, which is used for printing output.
- layerInfoMap, err := image.GetLayersMapWithImageInfo(runtime.ImageRuntime())
- if err != nil {
- return errors.Wrapf(err, "error while retriving layers of image %q", img.InputName)
- }
-
- // Create an imageInfo and fill the image and layer info
- imageInfo := &infoImage{
- id: img.ID(),
- tags: img.Names(),
- }
-
+func printTree(imageInfo *image.InfoImage, layerInfoMap map[string]*image.LayerInfo, img *adapter.ContainerImage, whatRequires bool) error {
size, err := img.Size(context.Background())
if err != nil {
- return errors.Wrapf(err, "error while retriving image size")
+ return err
}
- fmt.Printf("Image ID: %s\n", imageInfo.id[:12])
- fmt.Printf("Tags:\t %s\n", imageInfo.tags)
+
+ fmt.Printf("Image ID: %s\n", imageInfo.ID[:12])
+ fmt.Printf("Tags:\t %s\n", imageInfo.Tags)
fmt.Printf("Size:\t %v\n", units.HumanSizeWithPrecision(float64(*size), 4))
fmt.Printf(fmt.Sprintf("Image Layers\n"))
- if !c.WhatRequires {
+ if !whatRequires {
// fill imageInfo with layers associated with image.
// the layers will be filled such that
// (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End)
- err := buildImageHierarchyMap(imageInfo, layerInfoMap, img.TopLayer())
- if err != nil {
- return err
- }
// Build output from imageInfo into buffer
printImageHierarchy(imageInfo)
@@ -108,30 +85,8 @@ func treeCmd(c *cliconfig.TreeValues) error {
// the layers will be filled such that
// (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End)
// (Forks)... intermediate Child Layer(s) -> Child Top Layer(End)
- err := printImageChildren(layerInfoMap, img.TopLayer(), "", true)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// Stores hierarchy of images such that all parent layers using which image is built are stored in imageInfo
-// Layers are added such that (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End)
-func buildImageHierarchyMap(imageInfo *infoImage, layerMap map[string]*image.LayerInfo, layerID string) error {
- if layerID == "" {
- return nil
- }
- ll, ok := layerMap[layerID]
- if !ok {
- return fmt.Errorf("lookup error: layerid %s not found", layerID)
+ return printImageChildren(layerInfoMap, img.TopLayer(), "", true)
}
- if err := buildImageHierarchyMap(imageInfo, layerMap, ll.ParentID); err != nil {
- return err
- }
-
- imageInfo.layers = append(imageInfo.layers, *ll)
return nil
}
@@ -175,14 +130,14 @@ func printImageChildren(layerMap map[string]*image.LayerInfo, layerID string, pr
}
// prints the layers info of image
-func printImageHierarchy(imageInfo *infoImage) {
- for count, l := range imageInfo.layers {
+func printImageHierarchy(imageInfo *image.InfoImage) {
+ for count, l := range imageInfo.Layers {
var tags string
intend := middleItem
if len(l.RepoTags) > 0 {
tags = fmt.Sprintf(" Top Layer of: %s", l.RepoTags)
}
- if count == len(imageInfo.layers)-1 {
+ if count == len(imageInfo.Layers)-1 {
intend = lastItem
}
fmt.Printf("%s ID: %s Size: %7v%s\n", intend, l.ID[:12], units.HumanSizeWithPrecision(float64(l.Size), 4), tags)
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 2ff06a6f6..92fdcd20f 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -68,7 +68,8 @@ type Image (
virtualSize: int,
containers: int,
labels: [string]string,
- isParent: bool
+ isParent: bool,
+ topLayer: string
)
# ImageHistory describes the returned structure from ImageHistory.
@@ -1161,8 +1162,15 @@ method LoadImage(name: string, inputFile: string, quiet: bool, deleteFile: bool)
# GetEvents returns known libpod events filtered by the options provided.
method GetEvents(filter: []string, since: string, until: string) -> (events: Event)
+# Diff returns a diff between libpod objects
method Diff(name: string) -> (diffs: []DiffInfo)
+# GetLayersMapWithImageInfo is for the development of Podman and should not be used.
+method GetLayersMapWithImageInfo() -> (layerMap: string)
+
+# BuildImageHierarchyMap is for the development of Podman and should not be used.
+method BuildImageHierarchyMap(name: string) -> (imageInfo: string)
+
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
error ImageNotFound (id: string, reason: string)
diff --git a/docs/podman-volume-rm.1.md b/docs/podman-volume-rm.1.md
index c23d7675c..8c3765235 100644
--- a/docs/podman-volume-rm.1.md
+++ b/docs/podman-volume-rm.1.md
@@ -21,7 +21,8 @@ Remove all volumes.
**-f**, **--force**=""
-Remove a volume by force, even if it is being used by a container
+Remove a volume by force.
+If it is being used by containers, the containers will be removed first.
**--help**
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index 92a7b1538..d8cfa2bda 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -1358,56 +1358,6 @@ func (s *BoltState) AddVolume(volume *Volume) error {
return err
}
-// RemoveVolCtrDep updates the container dependencies sub bucket of the given volume.
-// It deletes it from the bucket when found.
-// This is important when force removing a volume and we want to get rid of the dependencies.
-func (s *BoltState) RemoveVolCtrDep(volume *Volume, ctrID string) error {
- if ctrID == "" {
- return nil
- }
-
- if !s.valid {
- return ErrDBBadConfig
- }
-
- if !volume.valid {
- return ErrVolumeRemoved
- }
-
- volName := []byte(volume.Name())
-
- db, err := s.getDBCon()
- if err != nil {
- return err
- }
- defer s.closeDBCon(db)
-
- err = db.Update(func(tx *bolt.Tx) error {
- volBkt, err := getVolBucket(tx)
- if err != nil {
- return err
- }
-
- volDB := volBkt.Bucket(volName)
- if volDB == nil {
- volume.valid = false
- return errors.Wrapf(ErrNoSuchVolume, "no volume with name %s found in database", volume.Name())
- }
-
- // Make a subbucket for the containers using the volume
- ctrDepsBkt := volDB.Bucket(volDependenciesBkt)
- depCtrID := []byte(ctrID)
- if depExists := ctrDepsBkt.Get(depCtrID); depExists != nil {
- if err := ctrDepsBkt.Delete(depCtrID); err != nil {
- return errors.Wrapf(err, "error deleting container dependencies %q for volume %s in ctrDependencies bucket in DB", ctrID, volume.Name())
- }
- }
-
- return nil
- })
- return err
-}
-
// RemoveVolume removes the given volume from the state
func (s *BoltState) RemoveVolume(volume *Volume) error {
if !s.valid {
@@ -1433,6 +1383,11 @@ func (s *BoltState) RemoveVolume(volume *Volume) error {
return err
}
+ ctrBkt, err := getCtrBucket(tx)
+ if err != nil {
+ return err
+ }
+
// Check if the volume exists
volDB := volBkt.Bucket(volName)
if volDB == nil {
@@ -1448,6 +1403,18 @@ func (s *BoltState) RemoveVolume(volume *Volume) error {
if volCtrsBkt != nil {
var deps []string
err = volCtrsBkt.ForEach(func(id, value []byte) error {
+ // Alright, this is ugly.
+ // But we need it to work around the change in
+ // volume dependency handling, to make sure that
+ // older Podman versions don't cause DB
+ // corruption.
+ // Look up all dependencies and see that they
+ // still exist before appending.
+ ctrExists := ctrBkt.Bucket(id)
+ if ctrExists == nil {
+ return nil
+ }
+
deps = append(deps, string(id))
return nil
})
@@ -1629,6 +1596,11 @@ func (s *BoltState) VolumeInUse(volume *Volume) ([]string, error) {
return err
}
+ ctrBucket, err := getCtrBucket(tx)
+ if err != nil {
+ return err
+ }
+
volDB := volBucket.Bucket([]byte(volume.Name()))
if volDB == nil {
volume.valid = false
@@ -1642,6 +1614,13 @@ func (s *BoltState) VolumeInUse(volume *Volume) ([]string, error) {
// Iterate through and add dependencies
err = dependsBkt.ForEach(func(id, value []byte) error {
+ // Look up all dependencies and see that they
+ // still exist before appending.
+ ctrExists := ctrBucket.Bucket(id)
+ if ctrExists == nil {
+ return nil
+ }
+
depCtrs = append(depCtrs, string(id))
return nil
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index b6a0759b1..a6900a6d3 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -564,23 +564,17 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
}
}
- // Add container to volume dependencies bucket if container is using a named volume
- if ctr.runtime.config.VolumePath == "" {
- return nil
- }
- for _, vol := range ctr.config.Spec.Mounts {
- if strings.Contains(vol.Source, ctr.runtime.config.VolumePath) {
- volName := strings.Split(vol.Source[len(ctr.runtime.config.VolumePath)+1:], "/")[0]
- volDB := volBkt.Bucket([]byte(volName))
- if volDB == nil {
- return errors.Wrapf(ErrNoSuchVolume, "no volume with name %s found in database", volName)
- }
+ // Add container to named volume dependencies buckets
+ for _, vol := range ctr.config.NamedVolumes {
+ volDB := volBkt.Bucket([]byte(vol.Name))
+ if volDB == nil {
+ return errors.Wrapf(ErrNoSuchVolume, "no volume with name %s found in database when adding container %s", vol.Name, ctr.ID())
+ }
- ctrDepsBkt := volDB.Bucket(volDependenciesBkt)
- if depExists := ctrDepsBkt.Get(ctrID); depExists == nil {
- if err := ctrDepsBkt.Put(ctrID, ctrID); err != nil {
- return errors.Wrapf(err, "error storing container dependencies %q for volume %s in ctrDependencies bucket in DB", ctr.ID(), volName)
- }
+ ctrDepsBkt := volDB.Bucket(volDependenciesBkt)
+ if depExists := ctrDepsBkt.Get(ctrID); depExists == nil {
+ if err := ctrDepsBkt.Put(ctrID, ctrID); err != nil {
+ return errors.Wrapf(err, "error adding container %s to volume %s dependencies", ctr.ID(), vol.Name)
}
}
}
@@ -745,22 +739,19 @@ func (s *BoltState) removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error
}
}
- // Remove container from volume dependencies bucket if container is using a named volume
- for _, vol := range ctr.config.Spec.Mounts {
- if strings.Contains(vol.Source, ctr.runtime.config.VolumePath) {
- volName := strings.Split(vol.Source[len(ctr.runtime.config.VolumePath)+1:], "/")[0]
-
- volDB := volBkt.Bucket([]byte(volName))
- if volDB == nil {
- // Let's assume the volume was already deleted and continue to remove the container
- continue
- }
+ // Remove container from named volume dependencies buckets
+ for _, vol := range ctr.config.NamedVolumes {
+ volDB := volBkt.Bucket([]byte(vol.Name))
+ if volDB == nil {
+ // Let's assume the volume was already deleted and
+ // continue to remove the container
+ continue
+ }
- ctrDepsBkt := volDB.Bucket(volDependenciesBkt)
- if depExists := ctrDepsBkt.Get(ctrID); depExists != nil {
- if err := ctrDepsBkt.Delete(ctrID); err != nil {
- return errors.Wrapf(err, "error deleting container dependencies %q for volume %s in ctrDependencies bucket in DB", ctr.ID(), volName)
- }
+ ctrDepsBkt := volDB.Bucket(volDependenciesBkt)
+ if depExists := ctrDepsBkt.Get(ctrID); depExists == nil {
+ if err := ctrDepsBkt.Delete(ctrID); err != nil {
+ return errors.Wrapf(err, "error deleting container %s dependency on volume %s", ctr.ID(), vol.Name)
}
}
}
diff --git a/libpod/container.go b/libpod/container.go
index 739406e42..6d5e063ab 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -234,6 +234,8 @@ type ContainerConfig struct {
// These include the SHM mount.
// These must be unmounted before the container's rootfs is unmounted.
Mounts []string `json:"mounts,omitempty"`
+ // NamedVolumes lists the named volumes to mount into the container.
+ NamedVolumes []*ContainerNamedVolume `json:"namedVolumes,omitempty"`
// Security Config
@@ -354,9 +356,6 @@ type ContainerConfig struct {
// ExitCommand is the container's exit command.
// This Command will be executed when the container exits
ExitCommand []string `json:"exitCommand,omitempty"`
- // LocalVolumes are the built-in volumes we get from the --volumes-from flag
- // It picks up the built-in volumes of the container used by --volumes-from
- LocalVolumes []spec.Mount
// IsInfra is a bool indicating whether this container is an infra container used for
// sharing kernel namespaces in a pod
IsInfra bool `json:"pause"`
@@ -368,6 +367,18 @@ type ContainerConfig struct {
HealthCheckConfig *manifest.Schema2HealthConfig `json:"healthcheck"`
}
+// ContainerNamedVolume is a named volume that will be mounted into the
+// container. Each named volume is a libpod Volume present in the state.
+type ContainerNamedVolume struct {
+ // Name is the name of the volume to mount in.
+ // Must resolve to a valid volume present in this Podman.
+ Name string `json:"volumeName"`
+ // Dest is the mount's destination
+ Dest string `json:"dest"`
+ // Options are fstab style mount options
+ Options []string `json:"options,omitempty"`
+}
+
// ContainerStatus returns a string representation for users
// of a container state
func (t ContainerStatus) String() string {
@@ -488,6 +499,22 @@ func (c *Container) StaticDir() string {
return c.config.StaticDir
}
+// NamedVolumes returns the container's named volumes.
+// The name of each is guaranteed to point to a valid libpod Volume present in
+// the state.
+func (c *Container) NamedVolumes() []*ContainerNamedVolume {
+ volumes := []*ContainerNamedVolume{}
+ for _, vol := range c.config.NamedVolumes {
+ newVol := new(ContainerNamedVolume)
+ newVol.Name = vol.Name
+ newVol.Dest = vol.Dest
+ newVol.Options = vol.Options
+ volumes = append(volumes, newVol)
+ }
+
+ return volumes
+}
+
// Privileged returns whether the container is privileged
func (c *Container) Privileged() bool {
return c.config.Privileged
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index daa32007a..22df36c11 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -1403,22 +1403,6 @@ func getExcludedCGroups() (excludes []string) {
return
}
-// namedVolumes returns named volumes for the container
-func (c *Container) namedVolumes() ([]string, error) {
- var volumes []string
- for _, vol := range c.config.Spec.Mounts {
- if strings.HasPrefix(vol.Source, c.runtime.config.VolumePath) {
- volume := strings.TrimPrefix(vol.Source, c.runtime.config.VolumePath+"/")
- split := strings.Split(volume, "/")
- volume = split[0]
- if _, err := c.runtime.state.Volume(volume); err == nil {
- volumes = append(volumes, volume)
- }
- }
- }
- return volumes, nil
-}
-
// this should be from chrootarchive.
func (c *Container) copyWithTarFromImage(src, dest string) error {
mountpoint, err := c.mount()
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 504d6c135..4d6bf61a3 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -195,6 +195,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
if err := c.makeBindMounts(); err != nil {
return nil, err
}
+
// Check if the spec file mounts contain the label Relabel flags z or Z.
// If they do, relabel the source directory and then remove the option.
for i := range g.Config.Mounts {
@@ -218,6 +219,23 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
g.SetProcessSelinuxLabel(c.ProcessLabel())
g.SetLinuxMountLabel(c.MountLabel())
+
+ // Add named volumes
+ for _, namedVol := range c.config.NamedVolumes {
+ volume, err := c.runtime.GetVolume(namedVol.Name)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error retrieving volume %s to add to container %s", namedVol.Name, c.ID())
+ }
+ mountPoint := volume.MountPoint()
+ volMount := spec.Mount{
+ Type: "bind",
+ Source: mountPoint,
+ Destination: namedVol.Dest,
+ Options: namedVol.Options,
+ }
+ g.AddMount(volMount)
+ }
+
// Add bind mounts to container
for dstPath, srcPath := range c.state.BindMounts {
newMount := spec.Mount{
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 4862bf1d6..cc056b816 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -68,6 +68,16 @@ type Runtime struct {
EventsLogFilePath string
}
+// InfoImage keep information of Image along with all associated layers
+type InfoImage struct {
+ // ID of image
+ ID string
+ // Tags of image
+ Tags []string
+ // Layers stores all layers of image.
+ Layers []LayerInfo
+}
+
// ErrRepoTagNotFound is the error returned when the image id given doesn't match a rep tag in store
var ErrRepoTagNotFound = errors.New("unable to match user input to any specific repotag")
@@ -1277,3 +1287,21 @@ func GetLayersMapWithImageInfo(imageruntime *Runtime) (map[string]*LayerInfo, er
}
return layerInfoMap, nil
}
+
+// BuildImageHierarchyMap stores hierarchy of images such that all parent layers using which image is built are stored in imageInfo
+// Layers are added such that (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End)
+func BuildImageHierarchyMap(imageInfo *InfoImage, layerMap map[string]*LayerInfo, layerID string) error {
+ if layerID == "" {
+ return nil
+ }
+ ll, ok := layerMap[layerID]
+ if !ok {
+ return fmt.Errorf("lookup error: layerid %s not found", layerID)
+ }
+ if err := BuildImageHierarchyMap(imageInfo, layerMap, ll.ParentID); err != nil {
+ return err
+ }
+
+ imageInfo.Layers = append(imageInfo.Layers, *ll)
+ return nil
+}
diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go
index ab4fc8ba7..2669206df 100644
--- a/libpod/in_memory_state.go
+++ b/libpod/in_memory_state.go
@@ -249,11 +249,8 @@ func (s *InMemoryState) AddContainer(ctr *Container) error {
}
// Add container to volume dependencies
- for _, vol := range ctr.config.Spec.Mounts {
- if strings.Contains(vol.Source, ctr.runtime.config.VolumePath) {
- volName := strings.Split(vol.Source[len(ctr.runtime.config.VolumePath)+1:], "/")[0]
- s.addCtrToVolDependsMap(ctr.ID(), volName)
- }
+ for _, vol := range ctr.config.NamedVolumes {
+ s.addCtrToVolDependsMap(ctr.ID(), vol.Name)
}
return nil
@@ -306,12 +303,9 @@ func (s *InMemoryState) RemoveContainer(ctr *Container) error {
s.removeCtrFromDependsMap(ctr.ID(), depCtr)
}
- // Remove container from volume dependencies
- for _, vol := range ctr.config.Spec.Mounts {
- if strings.Contains(vol.Source, ctr.runtime.config.VolumePath) {
- volName := strings.Split(vol.Source[len(ctr.runtime.config.VolumePath)+1:], "/")[0]
- s.removeCtrFromVolDependsMap(ctr.ID(), volName)
- }
+ // Remove this container from volume dependencies
+ for _, vol := range ctr.config.NamedVolumes {
+ s.removeCtrFromVolDependsMap(ctr.ID(), vol.Name)
}
return nil
@@ -492,22 +486,6 @@ func (s *InMemoryState) RemoveVolume(volume *Volume) error {
return nil
}
-// RemoveVolCtrDep updates the container dependencies of the volume
-func (s *InMemoryState) RemoveVolCtrDep(volume *Volume, ctrID string) error {
- if !volume.valid {
- return errors.Wrapf(ErrVolumeRemoved, "volume with name %s is not valid", volume.Name())
- }
-
- if _, ok := s.volumes[volume.Name()]; !ok {
- return errors.Wrapf(ErrNoSuchVolume, "volume with name %s doesn't exists in state", volume.Name())
- }
-
- // Remove container that is using this volume
- s.removeCtrFromVolDependsMap(ctrID, volume.Name())
-
- return nil
-}
-
// VolumeInUse checks if the given volume is being used by at least one container
func (s *InMemoryState) VolumeInUse(volume *Volume) ([]string, error) {
if !volume.valid {
diff --git a/libpod/options.go b/libpod/options.go
index 24f126e66..9326e54e4 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -13,7 +13,6 @@ import (
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
"github.com/cri-o/ocicni/pkg/ocicni"
- spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
@@ -1111,24 +1110,6 @@ func WithUserVolumes(volumes []string) CtrCreateOption {
}
}
-// WithLocalVolumes sets the built-in volumes of the container retrieved
-// from a container passed in to the --volumes-from flag.
-// This stores the built-in volume information in the Config so we can
-// add them when creating the container.
-func WithLocalVolumes(volumes []spec.Mount) CtrCreateOption {
- return func(ctr *Container) error {
- if ctr.valid {
- return ErrCtrFinalized
- }
-
- if volumes != nil {
- ctr.config.LocalVolumes = append(ctr.config.LocalVolumes, volumes...)
- }
-
- return nil
- }
-}
-
// WithEntrypoint sets the entrypoint of the container.
// This is not used to change the container's spec, but will instead be used
// during commit to populate the entrypoint of the new image.
@@ -1255,6 +1236,35 @@ func withIsInfra() CtrCreateOption {
}
}
+// WithNamedVolumes adds the given named volumes to the container.
+func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return ErrCtrFinalized
+ }
+
+ destinations := make(map[string]bool)
+
+ for _, vol := range volumes {
+ // Don't check if they already exist.
+ // If they don't we will automatically create them.
+
+ if _, ok := destinations[vol.Dest]; ok {
+ return errors.Wrapf(ErrInvalidArg, "two volumes found with destination %s", vol.Dest)
+ }
+ destinations[vol.Dest] = true
+
+ ctr.config.NamedVolumes = append(ctr.config.NamedVolumes, &ContainerNamedVolume{
+ Name: vol.Name,
+ Dest: vol.Dest,
+ Options: vol.Options,
+ })
+ }
+
+ return nil
+ }
+}
+
// Volume Creation Options
// WithVolumeName sets the name of the volume.
@@ -1274,68 +1284,72 @@ func WithVolumeName(name string) VolumeCreateOption {
}
}
-// WithVolumeUID sets the uid of the owner.
-func WithVolumeUID(uid int) VolumeCreateOption {
+// WithVolumeLabels sets the labels of the volume.
+func WithVolumeLabels(labels map[string]string) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return ErrVolumeFinalized
}
- volume.config.UID = uid
+
+ volume.config.Labels = make(map[string]string)
+ for key, value := range labels {
+ volume.config.Labels[key] = value
+ }
+
return nil
}
}
-// WithVolumeGID sets the gid of the owner.
-func WithVolumeGID(gid int) VolumeCreateOption {
+// WithVolumeDriver sets the driver of the volume.
+func WithVolumeDriver(driver string) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return ErrVolumeFinalized
}
- volume.config.GID = gid
+
+ volume.config.Driver = driver
+
return nil
}
}
-// WithVolumeLabels sets the labels of the volume.
-func WithVolumeLabels(labels map[string]string) VolumeCreateOption {
+// WithVolumeOptions sets the options of the volume.
+func WithVolumeOptions(options map[string]string) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return ErrVolumeFinalized
}
- volume.config.Labels = make(map[string]string)
- for key, value := range labels {
- volume.config.Labels[key] = value
+ volume.config.Options = make(map[string]string)
+ for key, value := range options {
+ volume.config.Options[key] = value
}
return nil
}
}
-// WithVolumeDriver sets the driver of the volume.
-func WithVolumeDriver(driver string) VolumeCreateOption {
+// WithVolumeUID sets the UID that the volume will be created as.
+func WithVolumeUID(uid int) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return ErrVolumeFinalized
}
- volume.config.Driver = driver
+ volume.config.UID = uid
return nil
}
}
-// WithVolumeOptions sets the options of the volume.
-func WithVolumeOptions(options map[string]string) VolumeCreateOption {
+// WithVolumeGID sets the GID that the volume will be created as.
+func WithVolumeGID(gid int) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return ErrVolumeFinalized
}
- volume.config.Options = make(map[string]string)
- for key, value := range options {
- volume.config.Options[key] = value
- }
+ volume.config.GID = gid
return nil
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 6e54de558..4dd2707e8 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -4,7 +4,6 @@ import (
"fmt"
"io/ioutil"
"os"
- "os/exec"
"path/filepath"
"sync"
"syscall"
@@ -742,7 +741,7 @@ func makeRuntime(runtime *Runtime) (err error) {
// Set up containers/storage
var store storage.Store
- if rootless.SkipStorageSetup() {
+ if os.Geteuid() != 0 {
logrus.Debug("Not configuring container store")
} else {
store, err = storage.GetStore(runtime.config.StorageConfig)
@@ -926,16 +925,8 @@ func makeRuntime(runtime *Runtime) (err error) {
// If we need to refresh the state, do it now - things are guaranteed to
// be set up by now.
if doRefresh {
- if os.Geteuid() != 0 {
- aliveLock.Unlock()
- locked = false
- if err2 := runtime.refreshRootless(); err2 != nil {
- return err2
- }
- } else {
- if err2 := runtime.refresh(runtimeAliveFile); err2 != nil {
- return err2
- }
+ if err2 := runtime.refresh(runtimeAliveFile); err2 != nil {
+ return err2
}
}
@@ -1009,21 +1000,6 @@ func (r *Runtime) Shutdown(force bool) error {
return lastError
}
-// Reconfigures the runtime after a reboot for a rootless process
-func (r *Runtime) refreshRootless() error {
- // Take advantage of a command that requires a new userns
- // so that we are running as the root user and able to use refresh()
- cmd := exec.Command(os.Args[0], "info")
-
- if output, err := cmd.CombinedOutput(); err != nil {
- if _, ok := err.(*exec.ExitError); !ok {
- return errors.Wrapf(err, "Error waiting for info while refreshing state: %s", os.Args[0])
- }
- return errors.Wrapf(err, "Error running %s info while refreshing state: %s", os.Args[0], output)
- }
- return nil
-}
-
// Reconfigures the runtime after a reboot
// Refreshes the state, recreating temporary files
// Does not check validity as the runtime is not valid until after this has run
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 506aee477..800b42851 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -2,11 +2,9 @@ package libpod
import (
"context"
- "io/ioutil"
"os"
"path"
"path/filepath"
- "strconv"
"strings"
"time"
@@ -101,9 +99,6 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
ctr.state.State = ContainerStateConfigured
ctr.runtime = r
- ctr.valid = true
- ctr.state.State = ContainerStateConfigured
-
var pod *Pod
if ctr.config.Pod != "" {
// Get the pod from state
@@ -175,24 +170,29 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
ctr.config.ConmonPidFile = filepath.Join(ctr.config.StaticDir, "conmon.pid")
}
- // Go through the volume mounts and check for named volumes
- // If the named volme already exists continue, otherwise create
- // the storage for the named volume.
- for i, vol := range ctr.config.Spec.Mounts {
- if vol.Source[0] != '/' && isNamedVolume(vol.Source) {
- volInfo, err := r.state.Volume(vol.Source)
- if err != nil {
- newVol, err := r.newVolume(ctx, WithVolumeName(vol.Source), withSetCtrSpecific(), WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID()))
- if err != nil {
- return nil, errors.Wrapf(err, "error creating named volume %q", vol.Source)
- }
- ctr.config.Spec.Mounts[i].Source = newVol.MountPoint()
- if err := ctr.copyWithTarFromImage(ctr.config.Spec.Mounts[i].Destination, ctr.config.Spec.Mounts[i].Source); err != nil && !os.IsNotExist(err) {
- return nil, errors.Wrapf(err, "failed to copy content into new volume mount %q", vol.Source)
- }
- continue
- }
- ctr.config.Spec.Mounts[i].Source = volInfo.MountPoint()
+ // Go through named volumes and add them.
+ // If they don't exist they will be created using basic options.
+ for _, vol := range ctr.config.NamedVolumes {
+ // Check if it exists already
+ _, err := r.state.Volume(vol.Name)
+ if err == nil {
+ // The volume exists, we're good
+ continue
+ } else if errors.Cause(err) != ErrNoSuchVolume {
+ return nil, errors.Wrapf(err, "error retrieving named volume %s for new container", vol.Name)
+ }
+
+ logrus.Debugf("Creating new volume %s for container", vol.Name)
+
+ // The volume does not exist, so we need to create it.
+ newVol, err := r.newVolume(ctx, WithVolumeName(vol.Name), withSetCtrSpecific(),
+ WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID()))
+ if err != nil {
+ return nil, errors.Wrapf(err, "error creating named volume %q", vol.Name)
+ }
+
+ if err := ctr.copyWithTarFromImage(vol.Dest, newVol.MountPoint()); err != nil && !os.IsNotExist(err) {
+ return nil, errors.Wrapf(err, "Failed to copy content into new volume mount %q", vol.Name)
}
}
@@ -346,13 +346,6 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool,
return errors.Wrapf(ErrCtrExists, "container %s has dependent containers which must be removed before it: %s", c.ID(), depsStr)
}
- var volumes []string
- if removeVolume {
- volumes, err = c.namedVolumes()
- if err != nil {
- logrus.Errorf("unable to retrieve builtin volumes for container %v: %v", c.ID(), err)
- }
- }
var cleanupErr error
// Remove the container from the state
if c.config.Pod != "" {
@@ -417,8 +410,12 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool,
}
}
- for _, v := range volumes {
- if volume, err := runtime.state.Volume(v); err == nil {
+ if !removeVolume {
+ return cleanupErr
+ }
+
+ for _, v := range c.config.NamedVolumes {
+ if volume, err := runtime.state.Volume(v.Name); err == nil {
if !volume.IsCtrSpecific() {
continue
}
@@ -550,51 +547,12 @@ func (r *Runtime) GetLatestContainer() (*Container, error) {
return ctrs[lastCreatedIndex], nil
}
-// Check if volName is a named volume and not one of the default mounts we add to containers
-func isNamedVolume(volName string) bool {
- if volName != "proc" && volName != "tmpfs" && volName != "devpts" && volName != "shm" && volName != "mqueue" && volName != "sysfs" && volName != "cgroup" {
- return true
- }
- return false
-}
-
// Export is the libpod portion of exporting a container to a tar file
func (r *Runtime) Export(name string, path string) error {
ctr, err := r.LookupContainer(name)
if err != nil {
return err
}
- if os.Geteuid() != 0 {
- state, err := ctr.State()
- if err != nil {
- return errors.Wrapf(err, "cannot read container state %q", ctr.ID())
- }
- if state == ContainerStateRunning || state == ContainerStatePaused {
- data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile)
- if err != nil {
- return errors.Wrapf(err, "cannot read conmon PID file %q", ctr.Config().ConmonPidFile)
- }
- conmonPid, err := strconv.Atoi(string(data))
- if err != nil {
- return errors.Wrapf(err, "cannot parse PID %q", data)
- }
- became, ret, err := rootless.JoinDirectUserAndMountNS(uint(conmonPid))
- if err != nil {
- return err
- }
- if became {
- os.Exit(ret)
- }
- } else {
- became, ret, err := rootless.BecomeRootInUserNS()
- if err != nil {
- return err
- }
- if became {
- os.Exit(ret)
- }
- }
- }
return ctr.Export(path)
}
diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go
index db5c29242..40040fc52 100644
--- a/libpod/runtime_volume_linux.go
+++ b/libpod/runtime_volume_linux.go
@@ -98,12 +98,26 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool) error
if !force {
return errors.Wrapf(ErrVolumeBeingUsed, "volume %s is being used by the following container(s): %s", v.Name(), depsStr)
}
- // If using force, log the warning that the volume is being used by at least one container
- logrus.Warnf("volume %s is being used by the following container(s): %s", v.Name(), depsStr)
- // Remove the container dependencies so we can go ahead and delete the volume
+
+ // We need to remove all containers using the volume
for _, dep := range deps {
- if err := r.state.RemoveVolCtrDep(v, dep); err != nil {
- return errors.Wrapf(err, "unable to remove container dependency %q from volume %q while trying to delete volume by force", dep, v.Name())
+ ctr, err := r.state.Container(dep)
+ if err != nil {
+ // If the container's removed, no point in
+ // erroring.
+ if errors.Cause(err) == ErrNoSuchCtr || errors.Cause(err) == ErrCtrRemoved {
+ continue
+ }
+
+ return errors.Wrapf(err, "error removing container %s that depends on volume %s", dep, v.Name())
+ }
+
+ // TODO: do we want to set force here when removing
+ // containers?
+ // I'm inclined to say no, in case someone accidentally
+ // wipes a container they're using...
+ if err := r.removeContainer(ctx, ctr, false, false); err != nil {
+ return errors.Wrapf(err, "error removing container %s that depends on volume %s", ctr.ID(), v.Name())
}
}
}
diff --git a/libpod/state.go b/libpod/state.go
index 4296fc3cd..d0ad1a1f8 100644
--- a/libpod/state.go
+++ b/libpod/state.go
@@ -192,10 +192,6 @@ type State interface {
// AddVolume adds the specified volume to state. The volume's name
// must be unique within the list of existing volumes
AddVolume(volume *Volume) error
- // RemoveVolCtrDep updates the list of container dependencies that the
- // volume has. It either deletes the dependent container ID from
- // the sub-bucket
- RemoveVolCtrDep(volume *Volume, ctrID string) error
// RemoveVolume removes the specified volume.
// Only volumes that have no container dependencies can be removed
RemoveVolume(volume *Volume) error
diff --git a/pkg/adapter/images.go b/pkg/adapter/images.go
new file mode 100644
index 000000000..c8ea1cdea
--- /dev/null
+++ b/pkg/adapter/images.go
@@ -0,0 +1,34 @@
+// +build !remoteclient
+
+package adapter
+
+import (
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/pkg/errors"
+)
+
+// Tree ...
+func (r *LocalRuntime) Tree(c *cliconfig.TreeValues) (*image.InfoImage, map[string]*image.LayerInfo, *ContainerImage, error) {
+ img, err := r.NewImageFromLocal(c.InputArgs[0])
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ // Fetch map of image-layers, which is used for printing output.
+ layerInfoMap, err := image.GetLayersMapWithImageInfo(r.Runtime.ImageRuntime())
+ if err != nil {
+ return nil, nil, nil, errors.Wrapf(err, "error while retrieving layers of image %q", img.InputName)
+ }
+
+ // Create an imageInfo and fill the image and layer info
+ imageInfo := &image.InfoImage{
+ ID: img.ID(),
+ Tags: img.Names(),
+ }
+
+ if err := image.BuildImageHierarchyMap(imageInfo, layerInfoMap, img.TopLayer()); err != nil {
+ return nil, nil, nil, err
+ }
+ return imageInfo, layerInfoMap, img, nil
+}
diff --git a/pkg/adapter/images_remote.go b/pkg/adapter/images_remote.go
index e7b38dccc..722058d4a 100644
--- a/pkg/adapter/images_remote.go
+++ b/pkg/adapter/images_remote.go
@@ -6,8 +6,11 @@ import (
"context"
"encoding/json"
+ "github.com/containers/libpod/cmd/podman/cliconfig"
iopodman "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/inspect"
+ "github.com/pkg/errors"
)
// Inspect returns returns an ImageData struct from over a varlink connection
@@ -22,3 +25,32 @@ func (i *ContainerImage) Inspect(ctx context.Context) (*inspect.ImageData, error
}
return &data, nil
}
+
+// Tree ...
+func (r *LocalRuntime) Tree(c *cliconfig.TreeValues) (*image.InfoImage, map[string]*image.LayerInfo, *ContainerImage, error) {
+ layerInfoMap := make(map[string]*image.LayerInfo)
+ imageInfo := &image.InfoImage{}
+
+ img, err := r.NewImageFromLocal(c.InputArgs[0])
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ reply, err := iopodman.GetLayersMapWithImageInfo().Call(r.Conn)
+ if err != nil {
+ return nil, nil, nil, errors.Wrap(err, "failed to obtain image layers")
+ }
+ if err := json.Unmarshal([]byte(reply), &layerInfoMap); err != nil {
+ return nil, nil, nil, errors.Wrap(err, "failed to unmarshal image layers")
+ }
+
+ reply, err = iopodman.BuildImageHierarchyMap().Call(r.Conn, c.InputArgs[0])
+ if err != nil {
+ return nil, nil, nil, errors.Wrap(err, "failed to get build image map")
+ }
+ if err := json.Unmarshal([]byte(reply), imageInfo); err != nil {
+ return nil, nil, nil, errors.Wrap(err, "failed to unmarshal build image map")
+ }
+
+ return imageInfo, layerInfoMap, img, nil
+}
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index dd51c7233..182a04044 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -8,7 +8,6 @@ import (
"io"
"io/ioutil"
"os"
- "strconv"
"text/template"
"github.com/containers/buildah"
@@ -124,38 +123,6 @@ func (r *LocalRuntime) Export(name string, path string) error {
if err != nil {
return errors.Wrapf(err, "error looking up container %q", name)
}
- if os.Geteuid() != 0 {
- state, err := ctr.State()
- if err != nil {
- return errors.Wrapf(err, "cannot read container state %q", ctr.ID())
- }
- if state == libpod.ContainerStateRunning || state == libpod.ContainerStatePaused {
- data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile)
- if err != nil {
- return errors.Wrapf(err, "cannot read conmon PID file %q", ctr.Config().ConmonPidFile)
- }
- conmonPid, err := strconv.Atoi(string(data))
- if err != nil {
- return errors.Wrapf(err, "cannot parse PID %q", data)
- }
- became, ret, err := rootless.JoinDirectUserAndMountNS(uint(conmonPid))
- if err != nil {
- return err
- }
- if became {
- os.Exit(ret)
- }
- } else {
- became, ret, err := rootless.BecomeRootInUserNS()
- if err != nil {
- return err
- }
- if became {
- os.Exit(ret)
- }
- }
- }
-
return ctr.Export(path)
}
@@ -343,46 +310,6 @@ func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (libpod.Healt
return r.Runtime.HealthCheck(c.InputArgs[0])
}
-// JoinOrCreateRootlessPod joins the specified pod if it is running or it creates a new user namespace
-// if the pod is stopped
-func (r *LocalRuntime) JoinOrCreateRootlessPod(pod *Pod) (bool, int, error) {
- if os.Geteuid() == 0 {
- return false, 0, nil
- }
- opts := rootless.Opts{
- Argument: pod.ID(),
- }
-
- inspect, err := pod.Inspect()
- if err != nil {
- return false, 0, err
- }
- for _, ctr := range inspect.Containers {
- prevCtr, err := r.LookupContainer(ctr.ID)
- if err != nil {
- return false, -1, err
- }
- s, err := prevCtr.State()
- if err != nil {
- return false, -1, err
- }
- if s != libpod.ContainerStateRunning && s != libpod.ContainerStatePaused {
- continue
- }
- data, err := ioutil.ReadFile(prevCtr.Config().ConmonPidFile)
- if err != nil {
- return false, -1, errors.Wrapf(err, "cannot read conmon PID file %q", prevCtr.Config().ConmonPidFile)
- }
- conmonPid, err := strconv.Atoi(string(data))
- if err != nil {
- return false, -1, errors.Wrapf(err, "cannot parse PID %q", data)
- }
- return rootless.JoinDirectUserAndMountNSWithOpts(uint(conmonPid), &opts)
- }
-
- return rootless.BecomeRootInUserNSWithOpts(&opts)
-}
-
// Events is a wrapper to libpod to obtain libpod/podman events
func (r *LocalRuntime) Events(c *cliconfig.EventValues) error {
var (
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index c3a4f322d..807a9ad8f 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -82,6 +82,7 @@ type remoteImage struct {
Digest digest.Digest
isParent bool
Runtime *LocalRuntime
+ TopLayer string
}
// Container ...
@@ -147,6 +148,7 @@ func imageInListToContainerImage(i iopodman.Image, name string, runtime *LocalRu
Names: i.RepoTags,
isParent: i.IsParent,
Runtime: runtime,
+ TopLayer: i.TopLayer,
}
return &ContainerImage{ri}, nil
}
@@ -280,6 +282,11 @@ func (ci *ContainerImage) Dangling() bool {
return len(ci.Names()) == 0
}
+// TopLayer returns an images top layer as a string
+func (ci *ContainerImage) TopLayer() string {
+ return ci.remoteImage.TopLayer
+}
+
// TagImage ...
func (ci *ContainerImage) TagImage(tag string) error {
_, err := iopodman.TagImage().Call(ci.Runtime.Conn, ci.ID(), tag)
@@ -755,13 +762,6 @@ func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (libpod.Healt
return -1, libpod.ErrNotImplemented
}
-// JoinOrCreateRootlessPod joins the specified pod if it is running or it creates a new user namespace
-// if the pod is stopped
-func (r *LocalRuntime) JoinOrCreateRootlessPod(pod *Pod) (bool, int, error) {
- // Nothing to do in the remote case
- return true, 0, nil
-}
-
// Events monitors libpod/podman events over a varlink connection
func (r *LocalRuntime) Events(c *cliconfig.EventValues) error {
var more uint64
diff --git a/pkg/annotations/annotations.go b/pkg/annotations/annotations.go
index 008cca7ee..fe2591a0c 100644
--- a/pkg/annotations/annotations.go
+++ b/pkg/annotations/annotations.go
@@ -19,9 +19,18 @@ const (
// HostName is the container host name annotation
HostName = "io.kubernetes.cri-o.HostName"
+ // CgroupParent is the sandbox cgroup parent
+ CgroupParent = "io.kubernetes.cri-o.CgroupParent"
+
// IP is the container ipv4 or ipv6 address
IP = "io.kubernetes.cri-o.IP"
+ // NamespaceOptions store the options for namespaces
+ NamespaceOptions = "io.kubernetes.cri-o.NamespaceOptions"
+
+ // SeccompProfilePath is the node seccomp profile path
+ SeccompProfilePath = "io.kubernetes.cri-o.SeccompProfilePath"
+
// Image is the container image ID annotation
Image = "io.kubernetes.cri-o.Image"
@@ -34,6 +43,9 @@ const (
// KubeName is the kubernetes name annotation
KubeName = "io.kubernetes.cri-o.KubeName"
+ // PortMappings holds the port mappings for the sandbox
+ PortMappings = "io.kubernetes.cri-o.PortMappings"
+
// Labels are the kubernetes labels annotation
Labels = "io.kubernetes.cri-o.Labels"
@@ -46,6 +58,9 @@ const (
// Name is the pod name annotation
Name = "io.kubernetes.cri-o.Name"
+ // Namespace is the pod namespace annotation
+ Namespace = "io.kubernetes.cri-o.Namespace"
+
// PrivilegedRuntime is the annotation for the privileged runtime path
PrivilegedRuntime = "io.kubernetes.cri-o.PrivilegedRuntime"
@@ -67,8 +82,8 @@ const (
// MountPoint is the mount point of the container rootfs
MountPoint = "io.kubernetes.cri-o.MountPoint"
- // TrustedSandbox is the annotation for trusted sandboxes
- TrustedSandbox = "io.kubernetes.cri-o.TrustedSandbox"
+ // RuntimeHandler is the annotation for runtime handler
+ RuntimeHandler = "io.kubernetes.cri-o.RuntimeHandler"
// TTY is the terminal path annotation
TTY = "io.kubernetes.cri-o.TTY"
@@ -79,8 +94,14 @@ const (
// StdinOnce is the stdin_once annotation
StdinOnce = "io.kubernetes.cri-o.StdinOnce"
- // Volumes is the volumes annotation
+ // Volumes is the volumes annotatoin
Volumes = "io.kubernetes.cri-o.Volumes"
+
+ // HostNetwork indicates whether the host network namespace is used or not
+ HostNetwork = "io.kubernetes.cri-o.HostNetwork"
+
+ // CNIResult is the JSON string representation of the Result from CNI
+ CNIResult = "io.kubernetes.cri-o.CNIResult"
)
// ContainerType values
diff --git a/pkg/rootless/rootless.go b/pkg/rootless/rootless.go
deleted file mode 100644
index a531e43ce..000000000
--- a/pkg/rootless/rootless.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package rootless
-
-// Opts allows to customize how re-execing to a rootless process is done
-type Opts struct {
- // Argument overrides the arguments on the command line
- // for the re-execed process. The process in the namespace
- // must use rootless.Argument() to read its value.
- Argument string
-}
diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c
index 2e2c3acac..9cb79ed4d 100644
--- a/pkg/rootless/rootless_linux.c
+++ b/pkg/rootless/rootless_linux.c
@@ -13,10 +13,36 @@
#include <sys/wait.h>
#include <string.h>
#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/prctl.h>
+#include <dirent.h>
static const char *_max_user_namespaces = "/proc/sys/user/max_user_namespaces";
static const char *_unprivileged_user_namespaces = "/proc/sys/kernel/unprivileged_userns_clone";
+static int n_files;
+
+static void __attribute__((constructor)) init()
+{
+ DIR *d;
+
+ /* Store how many FDs were open before the Go runtime kicked in. */
+ d = opendir ("/proc/self/fd");
+ if (d)
+ {
+ struct dirent *ent;
+
+ for (ent = readdir (d); ent; ent = readdir (d))
+ {
+ int fd = atoi (ent->d_name);
+ if (fd > n_files && fd != dirfd (d))
+ n_files = fd;
+ }
+ closedir (d);
+ }
+}
+
+
static int
syscall_setresuid (uid_t ruid, uid_t euid, uid_t suid)
{
@@ -133,12 +159,25 @@ reexec_userns_join (int userns, int mountns)
pid = fork ();
if (pid < 0)
fprintf (stderr, "cannot fork: %s\n", strerror (errno));
+
if (pid)
- return pid;
+ {
+ /* We passed down these fds, close them. */
+ int f;
+ for (f = 3; f < n_files; f++)
+ close (f);
+ return pid;
+ }
setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1);
setenv ("_CONTAINERS_ROOTLESS_UID", uid, 1);
+ if (prctl (PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0) < 0)
+ {
+ fprintf (stderr, "cannot prctl(PR_SET_PDEATHSIG): %s\n", strerror (errno));
+ _exit (EXIT_FAILURE);
+ }
+
if (setns (userns, 0) < 0)
{
fprintf (stderr, "cannot setns: %s\n", strerror (errno));
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index 0be0e08bf..1d1b1713d 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -46,25 +46,6 @@ func IsRootless() bool {
return isRootless
}
-var (
- skipStorageSetup = false
-)
-
-// SetSkipStorageSetup tells the runtime to not setup containers/storage
-func SetSkipStorageSetup(v bool) {
- skipStorageSetup = v
-}
-
-// SkipStorageSetup tells if we should skip the containers/storage setup
-func SkipStorageSetup() bool {
- return skipStorageSetup
-}
-
-// Argument returns the argument that was set for the rootless session.
-func Argument() string {
- return os.Getenv("_CONTAINERS_ROOTLESS_ARG")
-}
-
// GetRootlessUID returns the UID of the user in the parent userNS
func GetRootlessUID() int {
uidEnv := os.Getenv("_CONTAINERS_ROOTLESS_UID")
@@ -104,51 +85,86 @@ func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap)
return nil
}
-// JoinNS re-exec podman in a new userNS and join the user namespace of the specified
-// PID.
-func JoinNS(pid uint, preserveFDs int) (bool, int, error) {
- if os.Geteuid() == 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" {
- return false, -1, nil
+func readUserNs(path string) (string, error) {
+ b := make([]byte, 256)
+ _, err := syscall.Readlink(path, b)
+ if err != nil {
+ return "", err
}
+ return string(b), nil
+}
- userNS, err := getUserNSForPid(pid)
+func readUserNsFd(fd uintptr) (string, error) {
+ return readUserNs(fmt.Sprintf("/proc/self/fd/%d", fd))
+}
+
+func getParentUserNs(fd uintptr) (uintptr, error) {
+ const nsGetParent = 0xb702
+ ret, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(nsGetParent), 0)
+ if errno != 0 {
+ return 0, errno
+ }
+ return (uintptr)(unsafe.Pointer(ret)), nil
+}
+
+// getUserNSFirstChild returns an open FD for the first direct child user namespace that created the process
+// Each container creates a new user namespace where the runtime runs. The current process in the container
+// might have created new user namespaces that are child of the initial namespace we created.
+// This function finds the initial namespace created for the container that is a child of the current namespace.
+//
+// current ns
+// / \
+// TARGET -> a [other containers]
+// /
+// b
+// /
+// NS READ USING THE PID -> c
+func getUserNSFirstChild(fd uintptr) (*os.File, error) {
+ currentNS, err := readUserNs("/proc/self/ns/user")
if err != nil {
- return false, -1, err
+ return nil, err
}
- defer userNS.Close()
- pidC := C.reexec_userns_join(C.int(userNS.Fd()), -1)
- if int(pidC) < 0 {
- return false, -1, errors.Errorf("cannot re-exec process")
+ ns, err := readUserNsFd(fd)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot read user namespace")
}
- if preserveFDs > 0 {
- for fd := 3; fd < 3+preserveFDs; fd++ {
- // These fds were passed down to the runtime. Close them
- // and not interfere
- os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd)).Close()
- }
+ if ns == currentNS {
+ return nil, errors.New("process running in the same user namespace")
}
- ret := C.reexec_in_user_namespace_wait(pidC)
- if ret < 0 {
- return false, -1, errors.New("error waiting for the re-exec process")
- }
+ for {
+ nextFd, err := getParentUserNs(fd)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot get parent user namespace")
+ }
- return true, int(ret), nil
-}
+ ns, err = readUserNsFd(nextFd)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot read user namespace")
+ }
-// JoinDirectUserAndMountNS re-exec podman in a new userNS and join the user and mount
-// namespace of the specified PID without looking up its parent. Useful to join directly
-// the conmon process. It is a convenience function for JoinDirectUserAndMountNSWithOpts
-// with a default configuration.
-func JoinDirectUserAndMountNS(pid uint) (bool, int, error) {
- return JoinDirectUserAndMountNSWithOpts(pid, nil)
+ if ns == currentNS {
+ syscall.Close(int(nextFd))
+
+ // Drop O_CLOEXEC for the fd.
+ _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_SETFD, 0)
+ if errno != 0 {
+ syscall.Close(int(fd))
+ return nil, errno
+ }
+
+ return os.NewFile(fd, "userns child"), nil
+ }
+ syscall.Close(int(fd))
+ fd = nextFd
+ }
}
-// JoinDirectUserAndMountNSWithOpts re-exec podman in a new userNS and join the user and
-// mount namespace of the specified PID without looking up its parent. Useful to join
-// directly the conmon process.
-func JoinDirectUserAndMountNSWithOpts(pid uint, opts *Opts) (bool, int, error) {
+// JoinUserAndMountNS re-exec podman in a new userNS and join the user and mount
+// namespace of the specified PID without looking up its parent. Useful to join directly
+// the conmon process.
+func JoinUserAndMountNS(pid uint) (bool, int, error) {
if os.Geteuid() == 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" {
return false, -1, nil
}
@@ -165,39 +181,11 @@ func JoinDirectUserAndMountNSWithOpts(pid uint, opts *Opts) (bool, int, error) {
}
defer userNS.Close()
- if opts != nil && opts.Argument != "" {
- if err := os.Setenv("_CONTAINERS_ROOTLESS_ARG", opts.Argument); err != nil {
- return false, -1, err
- }
- }
-
- pidC := C.reexec_userns_join(C.int(userNS.Fd()), C.int(mountNS.Fd()))
- if int(pidC) < 0 {
- return false, -1, errors.Errorf("cannot re-exec process")
- }
-
- ret := C.reexec_in_user_namespace_wait(pidC)
- if ret < 0 {
- return false, -1, errors.New("error waiting for the re-exec process")
- }
-
- return true, int(ret), nil
-}
-
-// JoinNSPath re-exec podman in a new userNS and join the owner user namespace of the
-// specified path.
-func JoinNSPath(path string) (bool, int, error) {
- if os.Geteuid() == 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" {
- return false, -1, nil
- }
-
- userNS, err := getUserNSForPath(path)
+ fd, err := getUserNSFirstChild(userNS.Fd())
if err != nil {
return false, -1, err
}
- defer userNS.Close()
-
- pidC := C.reexec_userns_join(C.int(userNS.Fd()), -1)
+ pidC := C.reexec_userns_join(C.int(fd.Fd()), C.int(mountNS.Fd()))
if int(pidC) < 0 {
return false, -1, errors.Errorf("cannot re-exec process")
}
@@ -213,16 +201,8 @@ func JoinNSPath(path string) (bool, int, error) {
// BecomeRootInUserNS re-exec podman in a new userNS. It returns whether podman was re-executed
// into a new user namespace and the return code from the re-executed podman process.
// If podman was re-executed the caller needs to propagate the error code returned by the child
-// process. It is a convenience function for BecomeRootInUserNSWithOpts with a default configuration.
-func BecomeRootInUserNS() (bool, int, error) {
- return BecomeRootInUserNSWithOpts(nil)
-}
-
-// BecomeRootInUserNSWithOpts re-exec podman in a new userNS. It returns whether podman was
-// re-execute into a new user namespace and the return code from the re-executed podman process.
-// If podman was re-executed the caller needs to propagate the error code returned by the child
// process.
-func BecomeRootInUserNSWithOpts(opts *Opts) (bool, int, error) {
+func BecomeRootInUserNS() (bool, int, error) {
if os.Geteuid() == 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" {
if os.Getenv("_CONTAINERS_USERNS_CONFIGURED") == "init" {
return false, 0, runInUser()
@@ -241,12 +221,6 @@ func BecomeRootInUserNSWithOpts(opts *Opts) (bool, int, error) {
defer w.Close()
defer w.Write([]byte("0"))
- if opts != nil && opts.Argument != "" {
- if err := os.Setenv("_CONTAINERS_ROOTLESS_ARG", opts.Argument); err != nil {
- return false, -1, err
- }
- }
-
pidC := C.reexec_in_user_namespace(C.int(r.Fd()))
pid := int(pidC)
if pid < 0 {
@@ -328,112 +302,3 @@ func BecomeRootInUserNSWithOpts(opts *Opts) (bool, int, error) {
return true, int(ret), nil
}
-
-func readUserNs(path string) (string, error) {
- b := make([]byte, 256)
- _, err := syscall.Readlink(path, b)
- if err != nil {
- return "", err
- }
- return string(b), nil
-}
-
-func readUserNsFd(fd uintptr) (string, error) {
- return readUserNs(fmt.Sprintf("/proc/self/fd/%d", fd))
-}
-
-func getOwner(fd uintptr) (uintptr, error) {
- const nsGetUserns = 0xb701
- ret, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(nsGetUserns), 0)
- if errno != 0 {
- return 0, errno
- }
- return (uintptr)(unsafe.Pointer(ret)), nil
-}
-
-func getParentUserNs(fd uintptr) (uintptr, error) {
- const nsGetParent = 0xb702
- ret, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(nsGetParent), 0)
- if errno != 0 {
- return 0, errno
- }
- return (uintptr)(unsafe.Pointer(ret)), nil
-}
-
-func getUserNSForPath(path string) (*os.File, error) {
- u, err := os.Open(path)
- if err != nil {
- return nil, errors.Wrapf(err, "cannot open %s", path)
- }
- defer u.Close()
- fd, err := getOwner(u.Fd())
- if err != nil {
- return nil, err
- }
-
- return getUserNSFirstChild(fd)
-}
-
-func getUserNSForPid(pid uint) (*os.File, error) {
- path := fmt.Sprintf("/proc/%d/ns/user", pid)
- u, err := os.Open(path)
- if err != nil {
- return nil, errors.Wrapf(err, "cannot open %s", path)
- }
-
- return getUserNSFirstChild(u.Fd())
-}
-
-// getUserNSFirstChild returns an open FD for the first direct child user namespace that created the process
-// Each container creates a new user namespace where the runtime runs. The current process in the container
-// might have created new user namespaces that are child of the initial namespace we created.
-// This function finds the initial namespace created for the container that is a child of the current namespace.
-//
-// current ns
-// / \
-// TARGET -> a [other containers]
-// /
-// b
-// /
-// NS READ USING THE PID -> c
-func getUserNSFirstChild(fd uintptr) (*os.File, error) {
- currentNS, err := readUserNs("/proc/self/ns/user")
- if err != nil {
- return nil, err
- }
-
- ns, err := readUserNsFd(fd)
- if err != nil {
- return nil, errors.Wrapf(err, "cannot read user namespace")
- }
- if ns == currentNS {
- return nil, errors.New("process running in the same user namespace")
- }
-
- for {
- nextFd, err := getParentUserNs(fd)
- if err != nil {
- return nil, errors.Wrapf(err, "cannot get parent user namespace")
- }
-
- ns, err = readUserNsFd(nextFd)
- if err != nil {
- return nil, errors.Wrapf(err, "cannot read user namespace")
- }
-
- if ns == currentNS {
- syscall.Close(int(nextFd))
-
- // Drop O_CLOEXEC for the fd.
- _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_SETFD, 0)
- if errno != 0 {
- syscall.Close(int(fd))
- return nil, errno
- }
-
- return os.NewFile(fd, "userns child"), nil
- }
- syscall.Close(int(fd))
- fd = nextFd
- }
-}
diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go
index e01d7855c..47b5dd7cc 100644
--- a/pkg/rootless/rootless_unsupported.go
+++ b/pkg/rootless/rootless_unsupported.go
@@ -19,54 +19,15 @@ func BecomeRootInUserNS() (bool, int, error) {
return false, -1, errors.New("this function is not supported on this os")
}
-// BecomeRootInUserNS is a stub function that always returns false and an
-// error on unsupported OS's
-func BecomeRootInUserNSWithOpts(opts *Opts) (bool, int, error) {
- return false, -1, errors.New("this function is not supported on this os")
-}
-
// GetRootlessUID returns the UID of the user in the parent userNS
func GetRootlessUID() int {
return -1
}
-// SetSkipStorageSetup tells the runtime to not setup containers/storage
-func SetSkipStorageSetup(bool) {
-}
-
-// SkipStorageSetup tells if we should skip the containers/storage setup
-func SkipStorageSetup() bool {
- return false
-}
-
-// JoinNS re-exec podman in a new userNS and join the user namespace of the specified
-// PID.
-func JoinNS(pid uint, preserveFDs int) (bool, int, error) {
- return false, -1, errors.New("this function is not supported on this os")
-}
-
-// JoinNSPath re-exec podman in a new userNS and join the owner user namespace of the
-// specified path.
-func JoinNSPath(path string) (bool, int, error) {
- return false, -1, errors.New("this function is not supported on this os")
-}
-
-// JoinDirectUserAndMountNSWithOpts re-exec podman in a new userNS and join the user and
-// mount namespace of the specified PID without looking up its parent. Useful to join
-// directly the conmon process.
-func JoinDirectUserAndMountNSWithOpts(pid uint, opts *Opts) (bool, int, error) {
- return false, -1, errors.New("this function is not supported on this os")
-}
-
-// JoinDirectUserAndMountNS re-exec podman in a new userNS and join the user and mount
+// JoinUserAndMountNS re-exec podman in a new userNS and join the user and mount
// namespace of the specified PID without looking up its parent. Useful to join directly
-// the conmon process. It is a convenience function for JoinDirectUserAndMountNSWithOpts
+// the conmon process. It is a convenience function for JoinUserAndMountNSWithOpts
// with a default configuration.
-func JoinDirectUserAndMountNS(pid uint) (bool, int, error) {
+func JoinUserAndMountNS(pid uint) (bool, int, error) {
return false, -1, errors.New("this function is not supported on this os")
}
-
-// Argument returns the argument that was set for the rootless session.
-func Argument() string {
- return ""
-}
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 0a12e3dca..e71d9d3db 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -1,7 +1,6 @@
package createconfig
import (
- "encoding/json"
"fmt"
"net"
"os"
@@ -12,7 +11,6 @@ import (
"github.com/containers/image/manifest"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/namespaces"
- "github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
"github.com/containers/storage/pkg/stringid"
"github.com/cri-o/ocicni/pkg/ocicni"
@@ -24,18 +22,16 @@ import (
"golang.org/x/sys/unix"
)
-type mountType string
-
// Type constants
const (
bps = iota
iops
// TypeBind is the type for mounting host dir
- TypeBind mountType = "bind"
+ TypeBind = "bind"
// TypeVolume is the type for remote storage volumes
- // TypeVolume mountType = "volume" // re-enable upon use
+ // TypeVolume = "volume" // re-enable upon use
// TypeTmpfs is the type for mounting tmpfs
- TypeTmpfs mountType = "tmpfs"
+ TypeTmpfs = "tmpfs"
)
// CreateResourceConfig represents resource elements in CreateConfig
@@ -131,15 +127,15 @@ type CreateConfig struct {
Mounts []spec.Mount //mounts
Volumes []string //volume
VolumesFrom []string
- WorkDir string //workdir
- LabelOpts []string //SecurityOpts
- NoNewPrivs bool //SecurityOpts
- ApparmorProfile string //SecurityOpts
- SeccompProfilePath string //SecurityOpts
+ NamedVolumes []*libpod.ContainerNamedVolume // Filled in by CreateConfigToOCISpec
+ WorkDir string //workdir
+ LabelOpts []string //SecurityOpts
+ NoNewPrivs bool //SecurityOpts
+ ApparmorProfile string //SecurityOpts
+ SeccompProfilePath string //SecurityOpts
SecurityOpts []string
Rootfs string
- LocalVolumes []spec.Mount //Keeps track of the built-in volumes of container used in the --volumes-from flag
- Syslog bool // Whether to enable syslog on exit commands
+ Syslog bool // Whether to enable syslog on exit commands
}
func u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
@@ -173,9 +169,9 @@ func (c *CreateConfig) AddContainerInitBinary(path string) error {
c.Command = append([]string{"/dev/init", "--"}, c.Command...)
c.Mounts = append(c.Mounts, spec.Mount{
Destination: "/dev/init",
- Type: "bind",
+ Type: TypeBind,
Source: path,
- Options: []string{"bind", "ro"},
+ Options: []string{TypeBind, "ro"},
})
return nil
}
@@ -218,9 +214,9 @@ func (c *CreateConfig) initFSMounts() []spec.Mount {
return mounts
}
-//GetVolumeMounts takes user provided input for bind mounts and creates Mount structs
+// GetVolumeMounts takes user provided input for bind mounts and creates Mount structs
func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, error) {
- m := c.LocalVolumes
+ m := []spec.Mount{}
for _, i := range c.Volumes {
var options []string
spliti := strings.Split(i, ":")
@@ -256,9 +252,11 @@ func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, e
mount.Source = "tmpfs"
mount.Options = append(mount.Options, "tmpcopyup")
} else {
+ // TODO: Move support for this and tmpfs into libpod
+ // Should tmpfs also be handled as named volumes? Wouldn't be hard
// This will cause a new local Volume to be created on your system
mount.Source = stringid.GenerateNonCryptoID()
- mount.Options = append(mount.Options, "bind")
+ mount.Options = append(mount.Options, TypeBind)
}
m = append(m, mount)
}
@@ -269,13 +267,12 @@ func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, e
// GetVolumesFrom reads the create-config artifact of the container to get volumes from
// and adds it to c.Volumes of the current container.
func (c *CreateConfig) GetVolumesFrom() error {
- var options string
-
- if rootless.SkipStorageSetup() {
+ if os.Geteuid() != 0 {
return nil
}
for _, vol := range c.VolumesFrom {
+ options := ""
splitVol := strings.SplitN(vol, ":", 2)
if len(splitVol) == 2 {
options = splitVol[1]
@@ -284,41 +281,60 @@ func (c *CreateConfig) GetVolumesFrom() error {
if err != nil {
return errors.Wrapf(err, "error looking up container %q", splitVol[0])
}
- inspect, err := ctr.Inspect(false)
- if err != nil {
- return errors.Wrapf(err, "error inspecting %q", splitVol[0])
- }
- var createArtifact CreateConfig
- artifact, err := ctr.GetArtifact("create-config")
- if err != nil {
- return errors.Wrapf(err, "error getting create-config artifact for %q", splitVol[0])
+
+ logrus.Debugf("Adding volumes from container %s", ctr.ID())
+
+ // Look up the container's user volumes. This gets us the
+ // destinations of all mounts the user added to the container.
+ userVolumesArr := ctr.UserVolumes()
+
+ // We're going to need to access them a lot, so convert to a map
+ // to reduce looping.
+ // We'll also use the map to indicate if we missed any volumes along the way.
+ userVolumes := make(map[string]bool)
+ for _, dest := range userVolumesArr {
+ userVolumes[dest] = false
}
- if err := json.Unmarshal(artifact, &createArtifact); err != nil {
- return err
+
+ // Now we get the container's spec and loop through its volumes
+ // and append them in if we can find them.
+ spec := ctr.Spec()
+ if spec == nil {
+ return errors.Errorf("error retrieving container %s spec", ctr.ID())
}
- for key := range createArtifact.BuiltinImgVolumes {
- for _, m := range inspect.Mounts {
- if m.Destination == key {
- c.LocalVolumes = append(c.LocalVolumes, m)
- break
+ for _, mnt := range spec.Mounts {
+ if mnt.Type != TypeBind {
+ continue
+ }
+ if _, exists := userVolumes[mnt.Destination]; exists {
+ userVolumes[mnt.Destination] = true
+ localOptions := options
+ if localOptions == "" {
+ localOptions = strings.Join(mnt.Options, ",")
}
+ c.Volumes = append(c.Volumes, fmt.Sprintf("%s:%s:%s", mnt.Source, mnt.Destination, localOptions))
}
}
- for _, i := range createArtifact.Volumes {
- // Volumes format is host-dir:ctr-dir[:options], so get the host and ctr dir
- // and add on the options given by the user to the flag.
- spliti := strings.SplitN(i, ":", 3)
- // Throw error if mounting volume from container with Z option (private label)
- // Override this by adding 'z' to options.
- if len(spliti) > 2 && strings.Contains(spliti[2], "Z") && !strings.Contains(options, "z") {
- return errors.Errorf("volume mounted with private option 'Z' in %q. Use option 'z' to mount in current container", ctr.ID())
+ // We're done with the spec mounts. Add named volumes.
+ // Add these unconditionally - none of them are automatically
+ // part of the container, as some spec mounts are.
+ namedVolumes := ctr.NamedVolumes()
+ for _, namedVol := range namedVolumes {
+ if _, exists := userVolumes[namedVol.Dest]; exists {
+ userVolumes[namedVol.Dest] = true
}
- if options == "" {
- // Mount the volumes with the default options
- c.Volumes = append(c.Volumes, createArtifact.Volumes...)
- } else {
- c.Volumes = append(c.Volumes, spliti[0]+":"+spliti[1]+":"+options)
+ localOptions := options
+ if localOptions == "" {
+ localOptions = strings.Join(namedVol.Options, ",")
+ }
+ c.Volumes = append(c.Volumes, fmt.Sprintf("%s:%s:%s", namedVol.Name, namedVol.Dest, localOptions))
+ }
+
+ // Check if we missed any volumes
+ for volDest, found := range userVolumes {
+ if !found {
+ logrus.Warnf("Unable to match volume %s from container %s for volumes-from", volDest, ctr.ID())
}
}
}
@@ -418,14 +434,20 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime, pod *l
// others, if they are included
volumes := make([]string, 0, len(c.Volumes))
for _, vol := range c.Volumes {
- volumes = append(volumes, strings.SplitN(vol, ":", 2)[0])
+ // We always want the volume destination
+ splitVol := strings.SplitN(vol, ":", 3)
+ if len(splitVol) > 1 {
+ volumes = append(volumes, splitVol[1])
+ } else {
+ volumes = append(volumes, splitVol[0])
+ }
}
options = append(options, libpod.WithUserVolumes(volumes))
}
- if len(c.LocalVolumes) != 0 {
- options = append(options, libpod.WithLocalVolumes(c.LocalVolumes))
+ if len(c.NamedVolumes) != 0 {
+ options = append(options, libpod.WithNamedVolumes(c.NamedVolumes))
}
if len(c.Command) != 0 {
@@ -539,7 +561,7 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime, pod *l
options = append(options, libpod.WithPrivileged(c.Privileged))
- useImageVolumes := c.ImageVolumeType == "bind"
+ useImageVolumes := c.ImageVolumeType == TypeBind
// Gather up the options for NewContainer which consist of With... funcs
options = append(options, libpod.WithRootFSFromImage(c.ImageID, c.Image, useImageVolumes))
options = append(options, libpod.WithSecLabels(c.LabelOpts))
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index a61741f73..9b6bd089e 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -6,6 +6,7 @@ import (
"path/filepath"
"strings"
+ "github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage/pkg/mount"
pmount "github.com/containers/storage/pkg/mount"
@@ -48,6 +49,33 @@ func supercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.M
return configMount
}
+// Split named volumes from normal volumes
+func splitNamedVolumes(mounts []spec.Mount) ([]spec.Mount, []*libpod.ContainerNamedVolume) {
+ newMounts := make([]spec.Mount, 0)
+ namedVolumes := make([]*libpod.ContainerNamedVolume, 0)
+ for _, mount := range mounts {
+ // If it's not a named volume, append unconditionally
+ if mount.Type != TypeBind {
+ newMounts = append(newMounts, mount)
+ continue
+ }
+ // Volumes that are not named volumes must be an absolute or
+ // relative path.
+ // Volume names may not begin with a non-alphanumeric character
+ // so the HasPrefix() check is safe here.
+ if strings.HasPrefix(mount.Source, "/") || strings.HasPrefix(mount.Source, ".") {
+ newMounts = append(newMounts, mount)
+ } else {
+ namedVolume := new(libpod.ContainerNamedVolume)
+ namedVolume.Name = mount.Source
+ namedVolume.Dest = mount.Destination
+ namedVolume.Options = mount.Options
+ namedVolumes = append(namedVolumes, namedVolume)
+ }
+ }
+ return newMounts, namedVolumes
+}
+
func getAvailableGids() (int64, error) {
idMap, err := user.ParseIDMapFile("/proc/self/gid_map")
if err != nil {
@@ -99,7 +127,7 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
}
sysMnt := spec.Mount{
Destination: "/sys",
- Type: "bind",
+ Type: TypeBind,
Source: "/sys",
Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"},
}
@@ -126,7 +154,7 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
g.RemoveMount("/dev/mqueue")
devMqueue := spec.Mount{
Destination: "/dev/mqueue",
- Type: "bind",
+ Type: TypeBind,
Source: "/dev/mqueue",
Options: []string{"bind", "nosuid", "noexec", "nodev"},
}
@@ -136,7 +164,7 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
g.RemoveMount("/proc")
procMount := spec.Mount{
Destination: "/proc",
- Type: "bind",
+ Type: TypeBind,
Source: "/proc",
Options: []string{"rbind", "nosuid", "noexec", "nodev"},
}
@@ -377,6 +405,12 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
configSpec.Mounts = supercedeUserMounts(volumeMounts, configSpec.Mounts)
//--mount
configSpec.Mounts = supercedeUserMounts(config.initFSMounts(), configSpec.Mounts)
+
+ // Split normal mounts and named volumes
+ newMounts, namedVolumes := splitNamedVolumes(configSpec.Mounts)
+ configSpec.Mounts = newMounts
+ config.NamedVolumes = namedVolumes
+
// BLOCK IO
blkio, err := config.CreateBlockIO()
if err != nil {
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index 63d500204..8cd13e251 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -103,6 +103,7 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, id string) error {
VirtualSize: newImage.VirtualSize,
Containers: int64(len(containers)),
Labels: labels,
+ TopLayer: newImage.TopLayer(),
}
return call.ReplyGetImage(il)
}
@@ -923,3 +924,40 @@ func (i *LibpodAPI) Diff(call iopodman.VarlinkCall, name string) error {
}
return call.ReplyDiff(response)
}
+
+// GetLayersMapWithImageInfo is a development only endpoint to obtain layer information for an image.
+func (i *LibpodAPI) GetLayersMapWithImageInfo(call iopodman.VarlinkCall) error {
+ layerInfo, err := image.GetLayersMapWithImageInfo(i.Runtime.ImageRuntime())
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ b, err := json.Marshal(layerInfo)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyGetLayersMapWithImageInfo(string(b))
+}
+
+// BuildImageHierarchyMap ...
+func (i *LibpodAPI) BuildImageHierarchyMap(call iopodman.VarlinkCall, name string) error {
+ img, err := i.Runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ imageInfo := &image.InfoImage{
+ ID: img.ID(),
+ Tags: img.Names(),
+ }
+ layerInfo, err := image.GetLayersMapWithImageInfo(i.Runtime.ImageRuntime())
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ if err := image.BuildImageHierarchyMap(imageInfo, layerInfo, img.TopLayer()); err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ b, err := json.Marshal(imageInfo)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyBuildImageHierarchyMap(string(b))
+}
diff --git a/test/e2e/commit_test.go b/test/e2e/commit_test.go
index bf9c88de5..fe4ae64cf 100644
--- a/test/e2e/commit_test.go
+++ b/test/e2e/commit_test.go
@@ -144,7 +144,7 @@ var _ = Describe("Podman commit", func() {
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
image := inspect.InspectImageJSON()
- _, ok := image[0].Config.Volumes["/tmp"]
+ _, ok := image[0].Config.Volumes["/foo"]
Expect(ok).To(BeTrue())
r := podmanTest.Podman([]string{"run", "newimage"})
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 2daf2fe5b..a89ee491b 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -611,7 +611,6 @@ USER mail`
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("data"))
-
})
It("podman run --volumes flag with multiple volumes", func() {
diff --git a/test/e2e/volume_rm_test.go b/test/e2e/volume_rm_test.go
index 888474670..39628d56f 100644
--- a/test/e2e/volume_rm_test.go
+++ b/test/e2e/volume_rm_test.go
@@ -32,7 +32,7 @@ var _ = Describe("Podman volume rm", func() {
})
- It("podman rm volume", func() {
+ It("podman volume rm", func() {
session := podmanTest.Podman([]string{"volume", "create", "myvol"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -47,7 +47,7 @@ var _ = Describe("Podman volume rm", func() {
Expect(len(session.OutputToStringArray())).To(Equal(0))
})
- It("podman rm with --force flag", func() {
+ It("podman volume rm with --force flag", func() {
SkipIfRemote()
session := podmanTest.Podman([]string{"create", "-v", "myvol:/myvol", ALPINE, "ls"})
cid := session.OutputToString()