summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/inspect/inspect.go1
-rw-r--r--pkg/lookup/lookup.go168
-rw-r--r--pkg/namespaces/namespaces.go7
-rw-r--r--pkg/registries/registries.go6
-rw-r--r--pkg/resolvconf/resolvconf.go12
-rw-r--r--pkg/rootless/rootless_linux.go40
-rw-r--r--pkg/secrets/secrets.go9
-rw-r--r--pkg/spec/createconfig.go12
-rw-r--r--pkg/spec/spec.go3
-rw-r--r--pkg/util/utils.go55
-rw-r--r--pkg/varlinkapi/containers.go49
-rw-r--r--pkg/varlinkapi/containers_create.go7
-rw-r--r--pkg/varlinkapi/images.go48
-rw-r--r--pkg/varlinkapi/mount.go49
-rw-r--r--pkg/varlinkapi/system.go13
15 files changed, 454 insertions, 25 deletions
diff --git a/pkg/inspect/inspect.go b/pkg/inspect/inspect.go
index 62ba53147..5bdcf677f 100644
--- a/pkg/inspect/inspect.go
+++ b/pkg/inspect/inspect.go
@@ -126,6 +126,7 @@ type ImageData struct {
Annotations map[string]string `json:"Annotations"`
ManifestType string `json:"ManifestType"`
User string `json:"User"`
+ History []v1.History `json:"History"`
}
// RootFS holds the root fs information of an image
diff --git a/pkg/lookup/lookup.go b/pkg/lookup/lookup.go
new file mode 100644
index 000000000..70b97144f
--- /dev/null
+++ b/pkg/lookup/lookup.go
@@ -0,0 +1,168 @@
+package lookup
+
+import (
+ "os"
+ "strconv"
+
+ "github.com/cyphar/filepath-securejoin"
+ "github.com/opencontainers/runc/libcontainer/user"
+ "github.com/sirupsen/logrus"
+)
+
+const (
+ etcpasswd = "/etc/passwd"
+ etcgroup = "/etc/group"
+)
+
+// Overrides allows you to override defaults in GetUserGroupInfo
+type Overrides struct {
+ DefaultUser *user.ExecUser
+ ContainerEtcPasswdPath string
+ ContainerEtcGroupPath string
+}
+
+// GetUserGroupInfo takes string forms of the the container's mount path and the container user and
+// returns a ExecUser with uid, gid, sgids, and home. And override can be provided for defaults.
+func GetUserGroupInfo(containerMount, containerUser string, override *Overrides) (*user.ExecUser, error) {
+ var (
+ passwdDest, groupDest string
+ defaultExecUser *user.ExecUser
+ err error
+ )
+ passwdPath := etcpasswd
+ groupPath := etcgroup
+
+ if override != nil {
+ // Check for an override /etc/passwd path
+ if override.ContainerEtcPasswdPath != "" {
+ passwdPath = override.ContainerEtcPasswdPath
+ }
+ // Check for an override for /etc/group path
+ if override.ContainerEtcGroupPath != "" {
+ groupPath = override.ContainerEtcGroupPath
+ }
+ }
+
+ // Check for an override default user
+ if override != nil && override.DefaultUser != nil {
+ defaultExecUser = override.DefaultUser
+ } else {
+ // Define a default container user
+ //defaultExecUser = &user.ExecUser{
+ // Uid: 0,
+ // Gid: 0,
+ // Home: "/",
+ defaultExecUser = nil
+
+ }
+
+ // Make sure the /etc/group and /etc/passwd destinations are not a symlink to something naughty
+ if passwdDest, err = securejoin.SecureJoin(containerMount, passwdPath); err != nil {
+ logrus.Debug(err)
+ return nil, err
+ }
+ if groupDest, err = securejoin.SecureJoin(containerMount, groupPath); err != nil {
+ logrus.Debug(err)
+ return nil, err
+ }
+ return user.GetExecUserPath(containerUser, defaultExecUser, passwdDest, groupDest)
+}
+
+// GetContainerGroups uses securejoin to get a list of numerical groupids from a container. Per the runc
+// function it calls: If a group name cannot be found, an error will be returned. If a group id cannot be found,
+// or the given group data is nil, the id will be returned as-is provided it is in the legal range.
+func GetContainerGroups(groups []string, containerMount string, override *Overrides) ([]uint32, error) {
+ var (
+ groupDest string
+ err error
+ uintgids []uint32
+ )
+
+ groupPath := etcgroup
+ if override != nil && override.ContainerEtcGroupPath != "" {
+ groupPath = override.ContainerEtcGroupPath
+ }
+
+ if groupDest, err = securejoin.SecureJoin(containerMount, groupPath); err != nil {
+ logrus.Debug(err)
+ return nil, err
+ }
+
+ gids, err := user.GetAdditionalGroupsPath(groups, groupDest)
+ if err != nil {
+ return nil, err
+ }
+ // For libpod, we want []uint32s
+ for _, gid := range gids {
+ uintgids = append(uintgids, uint32(gid))
+ }
+ return uintgids, nil
+}
+
+// GetUser takes a containermount path and user name or ID and returns
+// a matching User structure from /etc/passwd. If it cannot locate a user
+// with the provided information, an ErrNoPasswdEntries is returned.
+// When the provided user name was an ID, a User structure with Uid
+// set is returned along with ErrNoPasswdEntries.
+func GetUser(containerMount, userIDorName string) (*user.User, error) {
+ var inputIsName bool
+ uid, err := strconv.Atoi(userIDorName)
+ if err != nil {
+ inputIsName = true
+ }
+ passwdDest, err := securejoin.SecureJoin(containerMount, etcpasswd)
+ if err != nil {
+ return nil, err
+ }
+ users, err := user.ParsePasswdFileFilter(passwdDest, func(u user.User) bool {
+ if inputIsName {
+ return u.Name == userIDorName
+ }
+ return u.Uid == uid
+ })
+ if err != nil && !os.IsNotExist(err) {
+ return nil, err
+ }
+ if len(users) > 0 {
+ return &users[0], nil
+ }
+ if !inputIsName {
+ return &user.User{Uid: uid}, user.ErrNoPasswdEntries
+ }
+ return nil, user.ErrNoPasswdEntries
+}
+
+// GetGroup takes a containermount path and a group name or ID and returns
+// a match Group struct from /etc/group. If it cannot locate a group,
+// an ErrNoGroupEntries error is returned. When the provided group name
+// was an ID, a Group structure with Gid set is returned along with
+// ErrNoGroupEntries.
+func GetGroup(containerMount, groupIDorName string) (*user.Group, error) {
+ var inputIsName bool
+ gid, err := strconv.Atoi(groupIDorName)
+ if err != nil {
+ inputIsName = true
+ }
+
+ groupDest, err := securejoin.SecureJoin(containerMount, etcgroup)
+ if err != nil {
+ return nil, err
+ }
+
+ groups, err := user.ParseGroupFileFilter(groupDest, func(g user.Group) bool {
+ if inputIsName {
+ return g.Name == groupIDorName
+ }
+ return g.Gid == gid
+ })
+ if err != nil && !os.IsNotExist(err) {
+ return nil, err
+ }
+ if len(groups) > 0 {
+ return &groups[0], nil
+ }
+ if !inputIsName {
+ return &user.Group{Gid: gid}, user.ErrNoGroupEntries
+ }
+ return nil, user.ErrNoGroupEntries
+}
diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go
index bee833fa9..832efd554 100644
--- a/pkg/namespaces/namespaces.go
+++ b/pkg/namespaces/namespaces.go
@@ -223,7 +223,12 @@ func (n NetworkMode) IsBridge() bool {
return n == "bridge"
}
+// IsSlirp4netns indicates if we are running a rootless network stack
+func (n NetworkMode) IsSlirp4netns() bool {
+ return n == "slirp4netns"
+}
+
// IsUserDefined indicates user-created network
func (n NetworkMode) IsUserDefined() bool {
- return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer()
+ return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer() && !n.IsSlirp4netns()
}
diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go
index 73aa93d68..c26f15cb6 100644
--- a/pkg/registries/registries.go
+++ b/pkg/registries/registries.go
@@ -38,8 +38,10 @@ func GetRegistries() ([]string, error) {
func GetInsecureRegistries() ([]string, error) {
registryConfigPath := ""
- if _, err := os.Stat(userRegistriesFile); err == nil {
- registryConfigPath = userRegistriesFile
+ if rootless.IsRootless() {
+ if _, err := os.Stat(userRegistriesFile); err == nil {
+ registryConfigPath = userRegistriesFile
+ }
}
envOverride := os.Getenv("REGISTRIES_CONFIG_PATH")
diff --git a/pkg/resolvconf/resolvconf.go b/pkg/resolvconf/resolvconf.go
index fccd60093..e85bcb377 100644
--- a/pkg/resolvconf/resolvconf.go
+++ b/pkg/resolvconf/resolvconf.go
@@ -103,13 +103,21 @@ func GetLastModified() *File {
}
// FilterResolvDNS cleans up the config in resolvConf. It has two main jobs:
-// 1. It looks for localhost (127.*|::1) entries in the provided
+// 1. If a netns is enabled, it looks for localhost (127.*|::1) entries in the provided
// resolv.conf, removing local nameserver entries, and, if the resulting
// cleaned config has no defined nameservers left, adds default DNS entries
// 2. Given the caller provides the enable/disable state of IPv6, the filter
// code will remove all IPv6 nameservers if it is not enabled for containers
//
-func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) {
+func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool, netnsEnabled bool) (*File, error) {
+ // If we're using the host netns, we have nothing to do besides hash the file.
+ if !netnsEnabled {
+ hash, err := ioutils.HashData(bytes.NewReader(resolvConf))
+ if err != nil {
+ return nil, err
+ }
+ return &File{Content: resolvConf, Hash: hash}, nil
+ }
cleanedResolvConf := localhostNSRegexp.ReplaceAll(resolvConf, []byte{})
// if IPv6 is not enabled, also clean out any IPv6 address nameserver
if !ipv6Enabled {
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index 5c45f2694..07002da3f 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -12,6 +12,7 @@ import (
"runtime"
"strconv"
"strings"
+ "sync"
"syscall"
"unsafe"
@@ -33,9 +34,17 @@ func runInUser() error {
return nil
}
+var (
+ isRootlessOnce sync.Once
+ isRootless bool
+)
+
// IsRootless tells us if we are running in rootless mode
func IsRootless() bool {
- return os.Geteuid() != 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != ""
+ isRootlessOnce.Do(func() {
+ isRootless = os.Geteuid() != 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != ""
+ })
+ return isRootless
}
var (
@@ -65,7 +74,7 @@ func GetRootlessUID() int {
func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap) error {
path, err := exec.LookPath(tool)
if err != nil {
- return err
+ return errors.Wrapf(err, "cannot find %s", tool)
}
appendTriplet := func(l []string, a, b, c int) []string {
@@ -83,7 +92,11 @@ func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap)
Path: path,
Args: args,
}
- return cmd.Run()
+
+ if err := cmd.Run(); err != nil {
+ return errors.Wrapf(err, "cannot setup namespace using %s", tool)
+ }
+ return nil
}
// JoinNS re-exec podman in a new userNS and join the user namespace of the specified
@@ -182,11 +195,16 @@ func BecomeRootInUserNS() (bool, int, error) {
return false, -1, errors.Errorf("cannot re-exec process")
}
+ allowSingleIDMapping := os.Getenv("PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS") != ""
+
var uids, gids []idtools.IDMap
username := os.Getenv("USER")
if username == "" {
user, err := user.LookupId(fmt.Sprintf("%d", os.Getuid()))
- if err != nil && os.Getenv("PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS") == "" {
+ if err != nil && !allowSingleIDMapping {
+ if os.IsNotExist(err) {
+ return false, 0, errors.Wrapf(err, "/etc/subuid or /etc/subgid does not exist, see subuid/subgid man pages for information on these files")
+ }
return false, 0, errors.Wrapf(err, "could not find user by UID nor USER env was set")
}
if err == nil {
@@ -194,7 +212,7 @@ func BecomeRootInUserNS() (bool, int, error) {
}
}
mappings, err := idtools.NewIDMappings(username, username)
- if os.Getenv("PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS") == "" {
+ if !allowSingleIDMapping {
if err != nil {
return false, -1, err
}
@@ -224,7 +242,11 @@ func BecomeRootInUserNS() (bool, int, error) {
uidsMapped := false
if mappings != nil && uids != nil {
- uidsMapped = tryMappingTool("newuidmap", pid, os.Getuid(), uids) == nil
+ err := tryMappingTool("newuidmap", pid, os.Getuid(), uids)
+ if !allowSingleIDMapping && err != nil {
+ return false, 0, err
+ }
+ uidsMapped = err == nil
}
if !uidsMapped {
setgroups := fmt.Sprintf("/proc/%d/setgroups", pid)
@@ -242,7 +264,11 @@ func BecomeRootInUserNS() (bool, int, error) {
gidsMapped := false
if mappings != nil && gids != nil {
- gidsMapped = tryMappingTool("newgidmap", pid, os.Getgid(), gids) == nil
+ err := tryMappingTool("newgidmap", pid, os.Getgid(), gids)
+ if !allowSingleIDMapping && err != nil {
+ return false, 0, err
+ }
+ gidsMapped = err == nil
}
if !gidsMapped {
gidMap := fmt.Sprintf("/proc/%d/gid_map", pid)
diff --git a/pkg/secrets/secrets.go b/pkg/secrets/secrets.go
index 7208f53b7..242953609 100644
--- a/pkg/secrets/secrets.go
+++ b/pkg/secrets/secrets.go
@@ -149,6 +149,15 @@ func SecretMountsWithUIDGID(mountLabel, containerWorkingDir, mountFile, mountPre
mountFiles = append(mountFiles, []string{OverrideMountsFile, DefaultMountsFile}...)
if rootless.IsRootless() {
mountFiles = append([]string{UserOverrideMountsFile}, mountFiles...)
+ _, err := os.Stat(UserOverrideMountsFile)
+ if err != nil && os.IsNotExist(err) {
+ os.MkdirAll(filepath.Dir(UserOverrideMountsFile), 0755)
+ if f, err := os.Create(UserOverrideMountsFile); err != nil {
+ logrus.Warnf("could not create file %s: %v", UserOverrideMountsFile, err)
+ } else {
+ f.Close()
+ }
+ }
}
} else {
mountFiles = append(mountFiles, mountFile)
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 6ac9d82da..25f8cd7a1 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -335,7 +335,6 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib
}
options = append(options, runtime.WithPod(pod))
}
-
if len(c.PortBindings) > 0 {
portBindings, err = c.CreatePortBindings()
if err != nil {
@@ -392,11 +391,11 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib
options = append(options, libpod.WithNetNSFrom(connectedCtr))
} else if !c.NetMode.IsHost() && !c.NetMode.IsNone() {
isRootless := rootless.IsRootless()
- postConfigureNetNS := isRootless || (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost()
+ postConfigureNetNS := c.NetMode.IsSlirp4netns() || (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost()
if isRootless && len(portBindings) > 0 {
return nil, errors.New("port bindings are not yet supported by rootless containers")
}
- options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, networks))
+ options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
}
if c.PidMode.IsContainer() {
@@ -497,8 +496,13 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib
// CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands
func (c *CreateConfig) CreatePortBindings() ([]ocicni.PortMapping, error) {
+ return NatToOCIPortBindings(c.PortBindings)
+}
+
+// NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice
+func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) {
var portBindings []ocicni.PortMapping
- for containerPb, hostPb := range c.PortBindings {
+ for containerPb, hostPb := range ports {
var pm ocicni.PortMapping
pm.ContainerPort = int32(containerPb.Int())
for _, i := range hostPb {
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index b1cca2c9e..05be00864 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -453,6 +453,9 @@ func addNetNS(config *CreateConfig, g *generate.Generator) error {
} else if IsPod(string(netMode)) {
logrus.Debug("Using pod netmode, unless pod is not sharing")
return nil
+ } else if netMode.IsSlirp4netns() {
+ logrus.Debug("Using slirp4netns netmode")
+ return nil
} else if netMode.IsUserDefined() {
logrus.Debug("Using user defined netmode")
return nil
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 9107eec5c..e483253a4 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -3,11 +3,13 @@ package util
import (
"fmt"
"os"
+ "os/exec"
"path/filepath"
"strconv"
"strings"
"syscall"
+ "github.com/BurntSushi/toml"
"github.com/containers/image/types"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
@@ -256,7 +258,7 @@ func GetRootlessStorageOpts() (storage.StoreOptions, error) {
if err != nil {
return opts, err
}
- opts.RunRoot = filepath.Join(rootlessRuntime, "run")
+ opts.RunRoot = rootlessRuntime
dataDir := os.Getenv("XDG_DATA_HOME")
if dataDir == "" {
@@ -273,11 +275,45 @@ func GetRootlessStorageOpts() (storage.StoreOptions, error) {
dataDir = filepath.Join(resolvedHome, ".local", "share")
}
opts.GraphRoot = filepath.Join(dataDir, "containers", "storage")
- opts.GraphDriverName = "vfs"
+ if path, err := exec.LookPath("fuse-overlayfs"); err == nil {
+ opts.GraphDriverName = "overlay"
+ opts.GraphDriverOptions = []string{fmt.Sprintf("overlay.mount_program=%s", path)}
+ } else {
+ opts.GraphDriverName = "vfs"
+ }
return opts, nil
}
-// GetDefaultStoreOptions returns the storage ops for containers
+type tomlOptionsConfig struct {
+ MountProgram string `toml:"mount_program"`
+}
+
+type tomlConfig struct {
+ Storage struct {
+ Driver string `toml:"driver"`
+ RunRoot string `toml:"runroot"`
+ GraphRoot string `toml:"graphroot"`
+ Options struct{ tomlOptionsConfig } `toml:"options"`
+ } `toml:"storage"`
+}
+
+func getTomlStorage(storeOptions *storage.StoreOptions) *tomlConfig {
+ config := new(tomlConfig)
+
+ config.Storage.Driver = storeOptions.GraphDriverName
+ config.Storage.RunRoot = storeOptions.RunRoot
+ config.Storage.GraphRoot = storeOptions.GraphRoot
+ for _, i := range storeOptions.GraphDriverOptions {
+ s := strings.Split(i, "=")
+ if s[0] == "overlay.mount_program" {
+ config.Storage.Options.MountProgram = s[1]
+ }
+ }
+
+ return config
+}
+
+// GetDefaultStoreOptions returns the default storage options for containers.
func GetDefaultStoreOptions() (storage.StoreOptions, error) {
storageOpts := storage.DefaultStoreOptions
if rootless.IsRootless() {
@@ -290,6 +326,19 @@ func GetDefaultStoreOptions() (storage.StoreOptions, error) {
storageConf := filepath.Join(os.Getenv("HOME"), ".config/containers/storage.conf")
if _, err := os.Stat(storageConf); err == nil {
storage.ReloadConfigurationFile(storageConf, &storageOpts)
+ } else if os.IsNotExist(err) {
+ os.MkdirAll(filepath.Dir(storageConf), 0755)
+ file, err := os.OpenFile(storageConf, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
+ if err != nil {
+ return storageOpts, errors.Wrapf(err, "cannot open %s", storageConf)
+ }
+
+ tomlConfiguration := getTomlStorage(&storageOpts)
+ defer file.Close()
+ enc := toml.NewEncoder(file)
+ if err := enc.Encode(tomlConfiguration); err != nil {
+ os.Remove(storageConf)
+ }
}
}
return storageOpts, nil
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index f517e9b6e..07d981786 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -278,6 +278,18 @@ func (i *LibpodAPI) RestartContainer(call iopodman.VarlinkCall, name string, tim
return call.ReplyRestartContainer(ctr.ID())
}
+// ContainerExists looks in local storage for the existence of a container
+func (i *LibpodAPI) ContainerExists(call iopodman.VarlinkCall, name string) error {
+ _, err := i.Runtime.LookupContainer(name)
+ if errors.Cause(err) == libpod.ErrNoSuchCtr {
+ return call.ReplyContainerExists(1)
+ }
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyContainerExists(0)
+}
+
// KillContainer kills a running container. If you want to use the default SIGTERM signal, just send a -1
// for the signal arg.
func (i *LibpodAPI) KillContainer(call iopodman.VarlinkCall, name string, signal int64) error {
@@ -413,3 +425,40 @@ func (i *LibpodAPI) GetAttachSockets(call iopodman.VarlinkCall, name string) err
}
return call.ReplyGetAttachSockets(s)
}
+
+// ContainerCheckpoint ...
+func (i *LibpodAPI) ContainerCheckpoint(call iopodman.VarlinkCall, name string, keep, leaveRunning, tcpEstablished bool) error {
+ ctx := getContext()
+ ctr, err := i.Runtime.LookupContainer(name)
+ if err != nil {
+ return call.ReplyContainerNotFound(name)
+ }
+
+ options := libpod.ContainerCheckpointOptions{
+ Keep: keep,
+ TCPEstablished: tcpEstablished,
+ KeepRunning: leaveRunning,
+ }
+ if err := ctr.Checkpoint(ctx, options); err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyContainerCheckpoint(ctr.ID())
+}
+
+// ContainerRestore ...
+func (i *LibpodAPI) ContainerRestore(call iopodman.VarlinkCall, name string, keep, tcpEstablished bool) error {
+ ctx := getContext()
+ ctr, err := i.Runtime.LookupContainer(name)
+ if err != nil {
+ return call.ReplyContainerNotFound(name)
+ }
+
+ options := libpod.ContainerCheckpointOptions{
+ Keep: keep,
+ TCPEstablished: tcpEstablished,
+ }
+ if err := ctr.Restore(ctx, options); err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyContainerRestore(ctr.ID())
+}
diff --git a/pkg/varlinkapi/containers_create.go b/pkg/varlinkapi/containers_create.go
index ca1a57048..f9a2db9c8 100644
--- a/pkg/varlinkapi/containers_create.go
+++ b/pkg/varlinkapi/containers_create.go
@@ -13,6 +13,7 @@ import (
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/inspect"
"github.com/containers/libpod/pkg/namespaces"
+ "github.com/containers/libpod/pkg/rootless"
cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/util"
"github.com/docker/docker/pkg/signal"
@@ -126,7 +127,11 @@ func varlinkCreateToCreateConfig(ctx context.Context, create iopodman.Create, ru
// NETWORK MODE
networkMode := create.Net_mode
if networkMode == "" {
- networkMode = "bridge"
+ if rootless.IsRootless() {
+ networkMode = "slirp4netns"
+ } else {
+ networkMode = "bridge"
+ }
}
// WORKING DIR
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index d14c61c39..6d3f19422 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -4,7 +4,9 @@ import (
"bytes"
"encoding/json"
"fmt"
+ "github.com/containers/libpod/cmd/podman/shared"
"io"
+ "os"
"path/filepath"
"strings"
"time"
@@ -19,6 +21,7 @@ import (
"github.com/containers/libpod/libpod/image"
sysreg "github.com/containers/libpod/pkg/registries"
"github.com/containers/libpod/pkg/util"
+ "github.com/containers/libpod/utils"
"github.com/docker/go-units"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runtime-spec/specs-go"
@@ -271,6 +274,9 @@ func (i *LibpodAPI) InspectImage(call iopodman.VarlinkCall, name string) error {
return call.ReplyImageNotFound(name)
}
inspectInfo, err := newImage.Inspect(getContext())
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
b, err := json.Marshal(inspectInfo)
if err != nil {
return call.ReplyErrorOccurred(fmt.Sprintf("unable to serialize"))
@@ -497,3 +503,45 @@ func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string) error {
}
return call.ReplyPullImage(newImage.ID())
}
+
+// ImageExists returns bool as to whether the input image exists in local storage
+func (i *LibpodAPI) ImageExists(call iopodman.VarlinkCall, name string) error {
+ _, err := i.Runtime.ImageRuntime().NewFromLocal(name)
+ if errors.Cause(err) == libpod.ErrNoSuchImage {
+ return call.ReplyImageExists(1)
+ }
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyImageExists(0)
+}
+
+// ContainerRunlabel ...
+func (i *LibpodAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman.Runlabel) error {
+ ctx := getContext()
+ dockerRegistryOptions := image.DockerRegistryOptions{
+ DockerCertPath: input.CertDir,
+ DockerInsecureSkipTLSVerify: !input.TlsVerify,
+ }
+
+ stdErr := os.Stderr
+ stdOut := os.Stdout
+ stdIn := os.Stdin
+
+ runLabel, imageName, err := shared.GetRunlabel(input.Label, input.Image, ctx, i.Runtime, input.Pull, input.Creds, dockerRegistryOptions, input.Authfile, input.SignaturePolicyPath, nil)
+ if err != nil {
+ return err
+ }
+ if runLabel == "" {
+ return nil
+ }
+
+ cmd, env, err := shared.GenerateRunlabelCommand(runLabel, imageName, input.Name, input.Opts, input.ExtraArgs)
+ if err != nil {
+ return err
+ }
+ if err := utils.ExecCmdWithStdStreams(stdIn, stdOut, stdErr, env, cmd[0], cmd[1:]...); err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyContainerRunlabel()
+}
diff --git a/pkg/varlinkapi/mount.go b/pkg/varlinkapi/mount.go
new file mode 100644
index 000000000..84e6b2709
--- /dev/null
+++ b/pkg/varlinkapi/mount.go
@@ -0,0 +1,49 @@
+package varlinkapi
+
+import (
+ "github.com/containers/libpod/cmd/podman/varlink"
+)
+
+// ListContainerMounts ...
+func (i *LibpodAPI) ListContainerMounts(call iopodman.VarlinkCall) error {
+ var mounts []string
+ allContainers, err := i.Runtime.GetAllContainers()
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ for _, container := range allContainers {
+ mounted, mountPoint, err := container.Mounted()
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ if mounted {
+ mounts = append(mounts, mountPoint)
+ }
+ }
+ return call.ReplyListContainerMounts(mounts)
+}
+
+// MountContainer ...
+func (i *LibpodAPI) MountContainer(call iopodman.VarlinkCall, name string) error {
+ container, err := i.Runtime.LookupContainer(name)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ path, err := container.Mount()
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyMountContainer(path)
+}
+
+// UnmountContainer ...
+func (i *LibpodAPI) UnmountContainer(call iopodman.VarlinkCall, name string, force bool) error {
+ container, err := i.Runtime.LookupContainer(name)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ if err := container.Unmount(force); err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyUnmountContainer()
+}
diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go
index 287f42209..a29d22e7d 100644
--- a/pkg/varlinkapi/system.go
+++ b/pkg/varlinkapi/system.go
@@ -34,6 +34,10 @@ func (i *LibpodAPI) Ping(call iopodman.VarlinkCall) error {
// GetInfo returns details about the podman host and its stores
func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error {
+ versionInfo, err := libpod.GetVersion()
+ if err != nil {
+ return err
+ }
var (
registries, insecureRegistries []string
)
@@ -64,11 +68,10 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error {
podmanInfo.Host = infoHost
store := info[1].Data
pmaninfo := iopodman.InfoPodmanBinary{
- Compiler: goruntime.Compiler,
- Go_version: goruntime.Version(),
- // TODO : How are we going to get this here?
- //Podman_version:
- Git_commit: libpod.GitCommit,
+ Compiler: goruntime.Compiler,
+ Go_version: goruntime.Version(),
+ Podman_version: versionInfo.Version,
+ Git_commit: versionInfo.GitCommit,
}
graphStatus := iopodman.InfoGraphStatus{