summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container.go5
-rw-r--r--libpod/container_inspect.go6
-rw-r--r--libpod/container_internal.go2
-rw-r--r--libpod/container_internal_linux.go26
-rw-r--r--libpod/events/config.go2
-rw-r--r--libpod/image/image.go25
-rw-r--r--libpod/info.go1
-rw-r--r--libpod/network/create.go52
-rw-r--r--libpod/network/files.go48
-rw-r--r--libpod/network/netconflist.go92
-rw-r--r--libpod/network/network.go11
-rw-r--r--libpod/networking_linux.go119
-rw-r--r--libpod/oci_conmon_exec_linux.go2
-rw-r--r--libpod/oci_conmon_linux.go8
-rw-r--r--libpod/options.go13
-rw-r--r--libpod/plugin/volume_api.go454
-rw-r--r--libpod/reset.go2
-rw-r--r--libpod/runtime.go15
-rw-r--r--libpod/runtime_cstorage.go6
-rw-r--r--libpod/runtime_img.go52
-rw-r--r--libpod/runtime_migrate.go6
-rw-r--r--libpod/runtime_migrate_unsupported.go2
-rw-r--r--libpod/runtime_pod_infra_linux.go67
-rw-r--r--libpod/runtime_pod_linux.go4
24 files changed, 859 insertions, 161 deletions
diff --git a/libpod/container.go b/libpod/container.go
index e954d84eb..4e0687318 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -267,6 +267,11 @@ func (c *Container) Config() *ContainerConfig {
return returnConfig
}
+// Runtime returns the container's Runtime.
+func (c *Container) Runtime() *Runtime {
+ return c.runtime
+}
+
// Spec returns the container's OCI runtime spec
// The spec returned is the one used to create the container. The running
// spec may differ slightly as mounts are added based on the image
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index f78d74ef7..2ce3e8e68 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -488,7 +488,7 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
// Weight? For now, ignore anything
// without Weight set.
if dev.Weight == nil {
- logrus.Warnf("Ignoring weight device %s as it lacks a weight", key)
+ logrus.Infof("Ignoring weight device %s as it lacks a weight", key)
continue
}
if deviceNodes == nil {
@@ -500,7 +500,7 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
}
path, ok := deviceNodes[key]
if !ok {
- logrus.Warnf("Could not locate weight device %s in system devices", key)
+ logrus.Infof("Could not locate weight device %s in system devices", key)
continue
}
weightDev := define.InspectBlkioWeightDevice{}
@@ -522,7 +522,7 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
}
path, ok := deviceNodes[key]
if !ok {
- logrus.Warnf("Could not locate throttle device %s in system devices", key)
+ logrus.Infof("Could not locate throttle device %s in system devices", key)
continue
}
throttleDev := define.InspectBlkioThrottleDevice{}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index b6a3244ea..c751d775d 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -525,7 +525,7 @@ func (c *Container) teardownStorage() error {
// Potentially another tool using containers/storage already
// removed it?
if errors.Cause(err) == storage.ErrNotAContainer || errors.Cause(err) == storage.ErrContainerUnknown {
- logrus.Warnf("Storage for container %s already removed", c.ID())
+ logrus.Infof("Storage for container %s already removed", c.ID())
return nil
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 56575c195..72eaeac8e 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -35,6 +35,7 @@ import (
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/util"
"github.com/containers/podman/v2/utils"
+ "github.com/containers/podman/v2/version"
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/idtools"
securejoin "github.com/cyphar/filepath-securejoin"
@@ -352,7 +353,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
if !MountExists(g.Mounts(), dstPath) {
g.AddMount(newMount)
} else {
- logrus.Warnf("User mount overriding libpod mount at %q", dstPath)
+ logrus.Infof("User mount overriding libpod mount at %q", dstPath)
}
}
@@ -1423,11 +1424,26 @@ func (c *Container) makeBindMounts() error {
}
}
- // Make .containerenv
- // Empty file, so no need to recreate if it exists
+ // Make .containerenv if it does not exist
if _, ok := c.state.BindMounts["/run/.containerenv"]; !ok {
- // Empty string for now, but we may consider populating this later
- containerenvPath, err := c.writeStringToRundir(".containerenv", "")
+ var containerenv string
+ isRootless := 0
+ if rootless.IsRootless() {
+ isRootless = 1
+ }
+ imageID, imageName := c.Image()
+
+ if c.Privileged() {
+ // Populate the .containerenv with container information
+ containerenv = fmt.Sprintf(`engine="podman-%s"
+name=%q
+id=%q
+image=%q
+imageid=%q
+rootless=%d
+`, version.Version.String(), c.Name(), c.ID(), imageName, imageID, isRootless)
+ }
+ containerenvPath, err := c.writeStringToRundir(".containerenv", containerenv)
if err != nil {
return errors.Wrapf(err, "error creating containerenv file for container %s", c.ID())
}
diff --git a/libpod/events/config.go b/libpod/events/config.go
index fc1457289..085fa9d52 100644
--- a/libpod/events/config.go
+++ b/libpod/events/config.go
@@ -121,6 +121,8 @@ const (
Cleanup Status = "cleanup"
// Commit ...
Commit Status = "commit"
+ // Copy ...
+ Copy Status = "copy"
// Create ...
Create Status = "create"
// Exec ...
diff --git a/libpod/image/image.go b/libpod/image/image.go
index cecd64eb7..5c3f3b9e4 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -1222,6 +1222,11 @@ func (i *Image) inspect(ctx context.Context, calculateSize bool) (*inspect.Image
}
}
+ parent, err := i.ParentID(ctx)
+ if err != nil {
+ return nil, err
+ }
+
repoTags, err := i.RepoTags()
if err != nil {
return nil, err
@@ -1248,6 +1253,7 @@ func (i *Image) inspect(ctx context.Context, calculateSize bool) (*inspect.Image
data := &inspect.ImageData{
ID: i.ID(),
+ Parent: parent,
RepoTags: repoTags,
RepoDigests: repoDigests,
Comment: comment,
@@ -1258,10 +1264,12 @@ func (i *Image) inspect(ctx context.Context, calculateSize bool) (*inspect.Image
Config: &ociv1Img.Config,
Version: info.DockerVersion,
Size: size,
- VirtualSize: size,
- Annotations: annotations,
- Digest: i.Digest(),
- Labels: info.Labels,
+ // This is good enough for now, but has to be
+ // replaced later with correct calculation logic
+ VirtualSize: size,
+ Annotations: annotations,
+ Digest: i.Digest(),
+ Labels: info.Labels,
RootFS: &inspect.RootFS{
Type: ociv1Img.RootFS.Type,
Layers: ociv1Img.RootFS.DiffIDs,
@@ -1505,6 +1513,15 @@ func (i *Image) GetParent(ctx context.Context) (*Image, error) {
return tree.parent(ctx, i)
}
+// ParentID returns the image ID of the parent. Return empty string if a parent is not found.
+func (i *Image) ParentID(ctx context.Context) (string, error) {
+ parent, err := i.GetParent(ctx)
+ if err == nil && parent != nil {
+ return parent.ID(), nil
+ }
+ return "", err
+}
+
// GetChildren returns a list of the imageIDs that depend on the image
func (i *Image) GetChildren(ctx context.Context) ([]string, error) {
children, err := i.getChildren(ctx, true)
diff --git a/libpod/info.go b/libpod/info.go
index dd7a521c1..2f64a107e 100644
--- a/libpod/info.go
+++ b/libpod/info.go
@@ -117,7 +117,6 @@ func (r *Runtime) hostInfo() (*define.HostInfo, error) {
if rootless.IsRootless() {
if path, err := exec.LookPath("slirp4netns"); err == nil {
- logrus.Warnf("Failed to retrieve program version for %s: %v", path, err)
version, err := programVersion(path)
if err != nil {
logrus.Warnf("Failed to retrieve program version for %s: %v", path, err)
diff --git a/libpod/network/create.go b/libpod/network/create.go
index 7e4fc574a..094fbe349 100644
--- a/libpod/network/create.go
+++ b/libpod/network/create.go
@@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "strconv"
"github.com/containernetworking/cni/pkg/version"
"github.com/containers/common/pkg/config"
@@ -76,6 +77,29 @@ func validateBridgeOptions(options entities.NetworkCreateOptions) error {
}
+// parseMTU parses the mtu option
+func parseMTU(mtu string) (int, error) {
+ if mtu == "" {
+ return 0, nil // default
+ }
+ m, err := strconv.Atoi(mtu)
+ if err != nil {
+ return 0, err
+ }
+ if m < 0 {
+ return 0, errors.Errorf("the value %d for mtu is less than zero", m)
+ }
+ return m, nil
+}
+
+// parseVlan parses the vlan option
+func parseVlan(vlan string) (int, error) {
+ if vlan == "" {
+ return 0, nil // default
+ }
+ return strconv.Atoi(vlan)
+}
+
// createBridge creates a CNI network
func createBridge(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) {
var (
@@ -149,6 +173,28 @@ func createBridge(name string, options entities.NetworkCreateOptions, runtimeCon
ipMasq = false
}
+ var mtu int
+ var vlan int
+ for k, v := range options.Options {
+ var err error
+ switch k {
+ case "mtu":
+ mtu, err = parseMTU(v)
+ if err != nil {
+ return "", err
+ }
+
+ case "vlan":
+ vlan, err = parseVlan(v)
+ if err != nil {
+ return "", err
+ }
+
+ default:
+ return "", errors.Errorf("unsupported option %s", k)
+ }
+ }
+
// obtain host bridge name
bridgeDeviceName, err := GetFreeDeviceName(runtimeConfig)
if err != nil {
@@ -169,10 +215,10 @@ func createBridge(name string, options entities.NetworkCreateOptions, runtimeCon
}
// create CNI plugin configuration
- ncList := NewNcList(name, version.Current())
+ ncList := NewNcList(name, version.Current(), options.Labels)
var plugins []CNIPlugins
// TODO need to iron out the role of isDefaultGW and IPMasq
- bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig)
+ bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, mtu, vlan, ipamConfig)
plugins = append(plugins, bridge)
plugins = append(plugins, NewPortMapPlugin())
plugins = append(plugins, NewFirewallPlugin())
@@ -223,7 +269,7 @@ func createMacVLAN(name string, options entities.NetworkCreateOptions, runtimeCo
return "", err
}
}
- ncList := NewNcList(name, version.Current())
+ ncList := NewNcList(name, version.Current(), options.Labels)
macvlan := NewMacVLANPlugin(options.MacVLAN)
plugins = append(plugins, macvlan)
ncList["plugins"] = plugins
diff --git a/libpod/network/files.go b/libpod/network/files.go
index 7f1e3ee18..33cf01064 100644
--- a/libpod/network/files.go
+++ b/libpod/network/files.go
@@ -12,6 +12,7 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v2/libpod/define"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// ErrNoSuchNetworkInterface indicates that no network interface exists
@@ -49,13 +50,15 @@ func LoadCNIConfsFromDir(dir string) ([]*libcni.NetworkConfigList, error) {
return configs, nil
}
-// GetCNIConfigPathByName finds a CNI network by name and
+// GetCNIConfigPathByNameOrID finds a CNI network by name and
// returns its configuration file path
-func GetCNIConfigPathByName(config *config.Config, name string) (string, error) {
+func GetCNIConfigPathByNameOrID(config *config.Config, name string) (string, error) {
files, err := libcni.ConfFiles(GetCNIConfDir(config), []string{".conflist"})
if err != nil {
return "", err
}
+ idMatch := 0
+ file := ""
for _, confFile := range files {
conf, err := libcni.ConfListFromFile(confFile)
if err != nil {
@@ -64,6 +67,16 @@ func GetCNIConfigPathByName(config *config.Config, name string) (string, error)
if conf.Name == name {
return confFile, nil
}
+ if strings.HasPrefix(GetNetworkID(conf.Name), name) {
+ idMatch++
+ file = confFile
+ }
+ }
+ if idMatch == 1 {
+ return file, nil
+ }
+ if idMatch > 1 {
+ return "", errors.Errorf("more than one result for network ID %s", name)
}
return "", errors.Wrap(define.ErrNoSuchNetwork, fmt.Sprintf("unable to find network configuration for %s", name))
}
@@ -71,7 +84,7 @@ func GetCNIConfigPathByName(config *config.Config, name string) (string, error)
// ReadRawCNIConfByName reads the raw CNI configuration for a CNI
// network by name
func ReadRawCNIConfByName(config *config.Config, name string) ([]byte, error) {
- confFile, err := GetCNIConfigPathByName(config, name)
+ confFile, err := GetCNIConfigPathByNameOrID(config, name)
if err != nil {
return nil, err
}
@@ -89,6 +102,35 @@ func GetCNIPlugins(list *libcni.NetworkConfigList) string {
return strings.Join(plugins, ",")
}
+// GetNetworkLabels returns a list of labels as a string
+func GetNetworkLabels(list *libcni.NetworkConfigList) NcLabels {
+ cniJSON := make(map[string]interface{})
+ err := json.Unmarshal(list.Bytes, &cniJSON)
+ if err != nil {
+ logrus.Errorf("failed to unmarshal network config %v %v", cniJSON["name"], err)
+ return nil
+ }
+ if args, ok := cniJSON["args"]; ok {
+ if key, ok := args.(map[string]interface{}); ok {
+ if labels, ok := key[PodmanLabelKey]; ok {
+ if labels, ok := labels.(map[string]interface{}); ok {
+ result := make(NcLabels, len(labels))
+ for k, v := range labels {
+ if v, ok := v.(string); ok {
+ result[k] = v
+ } else {
+ logrus.Errorf("network config %v invalid label value type %T should be string", cniJSON["name"], labels)
+ }
+ }
+ return result
+ }
+ logrus.Errorf("network config %v invalid label type %T should be map[string]string", cniJSON["name"], labels)
+ }
+ }
+ }
+ return nil
+}
+
// GetNetworksFromFilesystem gets all the networks from the cni configuration
// files
func GetNetworksFromFilesystem(config *config.Config) ([]*allocator.Net, error) {
diff --git a/libpod/network/netconflist.go b/libpod/network/netconflist.go
index ee9adce14..d61b96ecb 100644
--- a/libpod/network/netconflist.go
+++ b/libpod/network/netconflist.go
@@ -4,6 +4,11 @@ import (
"net"
"os"
"path/filepath"
+ "strings"
+
+ "github.com/containernetworking/cni/libcni"
+ "github.com/containers/podman/v2/pkg/util"
+ "github.com/pkg/errors"
)
const (
@@ -14,22 +19,36 @@ const (
// NcList describes a generic map
type NcList map[string]interface{}
+// NcArgs describes the cni args field
+type NcArgs map[string]NcLabels
+
+// NcLabels describes the label map
+type NcLabels map[string]string
+
+// PodmanLabelKey key used to store the podman network label in a cni config
+const PodmanLabelKey = "podman_labels"
+
// NewNcList creates a generic map of values with string
// keys and adds in version and network name
-func NewNcList(name, version string) NcList {
+func NewNcList(name, version string, labels NcLabels) NcList {
n := NcList{}
n["cniVersion"] = version
n["name"] = name
+ if len(labels) > 0 {
+ n["args"] = NcArgs{PodmanLabelKey: labels}
+ }
return n
}
// NewHostLocalBridge creates a new LocalBridge for host-local
-func NewHostLocalBridge(name string, isGateWay, isDefaultGW, ipMasq bool, ipamConf IPAMHostLocalConf) *HostLocalBridge {
+func NewHostLocalBridge(name string, isGateWay, isDefaultGW, ipMasq bool, mtu int, vlan int, ipamConf IPAMHostLocalConf) *HostLocalBridge {
hostLocalBridge := HostLocalBridge{
PluginType: "bridge",
BrName: name,
IPMasq: ipMasq,
+ MTU: mtu,
HairpinMode: true,
+ Vlan: vlan,
IPAM: ipamConf,
}
if isGateWay {
@@ -159,3 +178,72 @@ func NewMacVLANPlugin(device string) MacVLANConfig {
}
return m
}
+
+// IfPassesFilter filters NetworkListReport and returns true if the filter match the given config
+func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]string) (bool, error) {
+ result := true
+ for key, filterValues := range filters {
+ result = false
+ switch strings.ToLower(key) {
+ case "name":
+ // matches one name, regex allowed
+ result = util.StringMatchRegexSlice(netconf.Name, filterValues)
+
+ case "plugin":
+ // match one plugin
+ plugins := GetCNIPlugins(netconf)
+ for _, val := range filterValues {
+ if strings.Contains(plugins, val) {
+ result = true
+ break
+ }
+ }
+
+ case "label":
+ // matches all labels
+ labels := GetNetworkLabels(netconf)
+ outer:
+ for _, filterValue := range filterValues {
+ filterArray := strings.SplitN(filterValue, "=", 2)
+ filterKey := filterArray[0]
+ if len(filterArray) > 1 {
+ filterValue = filterArray[1]
+ } else {
+ filterValue = ""
+ }
+ for labelKey, labelValue := range labels {
+ if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
+ result = true
+ continue outer
+ }
+ }
+ result = false
+ }
+
+ case "driver":
+ // matches only for the DefaultNetworkDriver
+ for _, filterValue := range filterValues {
+ plugins := GetCNIPlugins(netconf)
+ if filterValue == DefaultNetworkDriver &&
+ strings.Contains(plugins, DefaultNetworkDriver) {
+ result = true
+ }
+ }
+
+ case "id":
+ // matches part of one id
+ for _, filterValue := range filterValues {
+ if strings.Contains(GetNetworkID(netconf.Name), filterValue) {
+ result = true
+ break
+ }
+ }
+
+ // TODO: add dangling filter
+
+ default:
+ return false, errors.Errorf("invalid filter %q", key)
+ }
+ }
+ return result, nil
+}
diff --git a/libpod/network/network.go b/libpod/network/network.go
index 0febb52f6..89f0b67ac 100644
--- a/libpod/network/network.go
+++ b/libpod/network/network.go
@@ -1,6 +1,8 @@
package network
import (
+ "crypto/sha256"
+ "encoding/hex"
"encoding/json"
"net"
"os"
@@ -175,7 +177,7 @@ func RemoveNetwork(config *config.Config, name string) error {
return err
}
defer l.releaseCNILock()
- cniPath, err := GetCNIConfigPathByName(config, name)
+ cniPath, err := GetCNIConfigPathByNameOrID(config, name)
if err != nil {
return err
}
@@ -229,3 +231,10 @@ func Exists(config *config.Config, name string) (bool, error) {
}
return true, nil
}
+
+// GetNetworkID return the network ID for a given name.
+// It is just the sha256 hash but this should be good enough.
+func GetNetworkID(name string) string {
+ hash := sha256.Sum256([]byte(name))
+ return hex.EncodeToString(hash[:])
+}
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 15e470c80..463378af7 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -245,7 +245,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
// setupSlirp4netns can be called in rootful as well as in rootless
func (r *Runtime) setupSlirp4netns(ctr *Container) error {
path := r.config.Engine.NetworkCmdPath
-
+ slirpOptions := r.config.Engine.NetworkCmdOptions
if path == "" {
var err error
path, err = exec.LookPath("slirp4netns")
@@ -273,68 +273,69 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error {
outboundAddr6 := ""
if ctr.config.NetworkOptions != nil {
- slirpOptions := ctr.config.NetworkOptions["slirp4netns"]
- for _, o := range slirpOptions {
- parts := strings.SplitN(o, "=", 2)
- if len(parts) < 2 {
- return errors.Errorf("unknown option for slirp4netns: %q", o)
+ slirpOptions = append(slirpOptions, ctr.config.NetworkOptions["slirp4netns"]...)
+ }
+
+ for _, o := range slirpOptions {
+ parts := strings.SplitN(o, "=", 2)
+ if len(parts) < 2 {
+ return errors.Errorf("unknown option for slirp4netns: %q", o)
+ }
+ option, value := parts[0], parts[1]
+ switch option {
+ case "cidr":
+ ipv4, _, err := net.ParseCIDR(value)
+ if err != nil || ipv4.To4() == nil {
+ return errors.Errorf("invalid cidr %q", value)
}
- option, value := parts[0], parts[1]
- switch option {
- case "cidr":
- ipv4, _, err := net.ParseCIDR(value)
- if err != nil || ipv4.To4() == nil {
- return errors.Errorf("invalid cidr %q", value)
- }
- cidr = value
- case "port_handler":
- switch value {
- case "slirp4netns":
- isSlirpHostForward = true
- case "rootlesskit":
- isSlirpHostForward = false
- default:
- return errors.Errorf("unknown port_handler for slirp4netns: %q", value)
- }
- case "allow_host_loopback":
- switch value {
- case "true":
- disableHostLoopback = false
- case "false":
- disableHostLoopback = true
- default:
- return errors.Errorf("invalid value of allow_host_loopback for slirp4netns: %q", value)
- }
- case "enable_ipv6":
- switch value {
- case "true":
- enableIPv6 = true
- case "false":
- enableIPv6 = false
- default:
- return errors.Errorf("invalid value of enable_ipv6 for slirp4netns: %q", value)
- }
- case "outbound_addr":
- ipv4 := net.ParseIP(value)
- if ipv4 == nil || ipv4.To4() == nil {
- _, err := net.InterfaceByName(value)
- if err != nil {
- return errors.Errorf("invalid outbound_addr %q", value)
- }
+ cidr = value
+ case "port_handler":
+ switch value {
+ case "slirp4netns":
+ isSlirpHostForward = true
+ case "rootlesskit":
+ isSlirpHostForward = false
+ default:
+ return errors.Errorf("unknown port_handler for slirp4netns: %q", value)
+ }
+ case "allow_host_loopback":
+ switch value {
+ case "true":
+ disableHostLoopback = false
+ case "false":
+ disableHostLoopback = true
+ default:
+ return errors.Errorf("invalid value of allow_host_loopback for slirp4netns: %q", value)
+ }
+ case "enable_ipv6":
+ switch value {
+ case "true":
+ enableIPv6 = true
+ case "false":
+ enableIPv6 = false
+ default:
+ return errors.Errorf("invalid value of enable_ipv6 for slirp4netns: %q", value)
+ }
+ case "outbound_addr":
+ ipv4 := net.ParseIP(value)
+ if ipv4 == nil || ipv4.To4() == nil {
+ _, err := net.InterfaceByName(value)
+ if err != nil {
+ return errors.Errorf("invalid outbound_addr %q", value)
}
- outboundAddr = value
- case "outbound_addr6":
- ipv6 := net.ParseIP(value)
- if ipv6 == nil || ipv6.To4() != nil {
- _, err := net.InterfaceByName(value)
- if err != nil {
- return errors.Errorf("invalid outbound_addr6: %q", value)
- }
+ }
+ outboundAddr = value
+ case "outbound_addr6":
+ ipv6 := net.ParseIP(value)
+ if ipv6 == nil || ipv6.To4() != nil {
+ _, err := net.InterfaceByName(value)
+ if err != nil {
+ return errors.Errorf("invalid outbound_addr6: %q", value)
}
- outboundAddr6 = value
- default:
- return errors.Errorf("unknown option for slirp4netns: %q", o)
}
+ outboundAddr6 = value
+ default:
+ return errors.Errorf("unknown option for slirp4netns: %q", o)
}
}
diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go
index 7068bf87a..f8e7020f7 100644
--- a/libpod/oci_conmon_exec_linux.go
+++ b/libpod/oci_conmon_exec_linux.go
@@ -231,7 +231,7 @@ func (r *ConmonOCIRuntime) ExecStopContainer(ctr *Container, sessionID string, t
// Wait for the PID to stop
if err := waitPidStop(pid, time.Duration(timeout)*time.Second); err != nil {
- logrus.Warnf("Timed out waiting for container %s exec session %s to stop, resorting to SIGKILL", ctr.ID(), sessionID)
+ logrus.Infof("Timed out waiting for container %s exec session %s to stop, resorting to SIGKILL: %v", ctr.ID(), sessionID, err)
} else {
// No error, container is dead
return nil
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index bd58610a2..307b9bc54 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -442,7 +442,7 @@ func (r *ConmonOCIRuntime) StopContainer(ctr *Container, timeout uint, all bool)
}
if err := waitContainerStop(ctr, time.Duration(timeout)*time.Second); err != nil {
- logrus.Warnf("Timed out stopping container %s, resorting to SIGKILL", ctr.ID())
+ logrus.Infof("Timed out stopping container %s, resorting to SIGKILL: %v", ctr.ID(), err)
} else {
// No error, the container is dead
return nil
@@ -1009,7 +1009,7 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
if ctr.config.SdNotifyMode == define.SdNotifyModeIgnore {
if err := os.Unsetenv("NOTIFY_SOCKET"); err != nil {
- logrus.Warnf("Error unsetting NOTIFY_SOCKET %s", err.Error())
+ logrus.Warnf("Error unsetting NOTIFY_SOCKET %v", err)
}
}
@@ -1155,14 +1155,14 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
conmonPID, err := readConmonPidFile(ctr.config.ConmonPidFile)
if err != nil {
- logrus.Warnf("error reading conmon pid file for container %s: %s", ctr.ID(), err.Error())
+ logrus.Warnf("error reading conmon pid file for container %s: %v", ctr.ID(), err)
} else if conmonPID > 0 {
// conmon not having a pid file is a valid state, so don't set it if we don't have it
logrus.Infof("Got Conmon PID as %d", conmonPID)
ctr.state.ConmonPID = conmonPID
if ctr.config.SdNotifyMode != define.SdNotifyModeIgnore {
if sent, err := daemon.SdNotify(false, fmt.Sprintf("MAINPID=%d", conmonPID)); err != nil {
- logrus.Errorf("Error notifying systemd of Conmon PID: %s", err.Error())
+ logrus.Errorf("Error notifying systemd of Conmon PID: %v", err)
} else if sent {
logrus.Debugf("Notify MAINPID sent successfully")
}
diff --git a/libpod/options.go b/libpod/options.go
index 0f55f34a3..bd12c0c34 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1547,12 +1547,19 @@ func WithVolumeDriver(driver string) VolumeCreateOption {
if volume.valid {
return define.ErrVolumeFinalized
}
- // only local driver is possible rn
+
+ // Uncomment when volume plugins are ready for use.
+ // if driver != define.VolumeDriverLocal {
+ // if _, err := plugin.GetVolumePlugin(driver); err != nil {
+ // return err
+ // }
+ // }
+
if driver != define.VolumeDriverLocal {
return define.ErrNotImplemented
-
}
- volume.config.Driver = define.VolumeDriverLocal
+
+ volume.config.Driver = driver
return nil
}
}
diff --git a/libpod/plugin/volume_api.go b/libpod/plugin/volume_api.go
new file mode 100644
index 000000000..2500a4f36
--- /dev/null
+++ b/libpod/plugin/volume_api.go
@@ -0,0 +1,454 @@
+package plugin
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/containers/podman/v2/libpod/define"
+ "github.com/docker/go-plugins-helpers/sdk"
+ "github.com/docker/go-plugins-helpers/volume"
+ jsoniter "github.com/json-iterator/go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+var json = jsoniter.ConfigCompatibleWithStandardLibrary
+
+// TODO: We should add syntax for specifying plugins to containers.conf, and
+// support for loading based on that.
+
+// Copied from docker/go-plugins-helpers/volume/api.go - not exported, so we
+// need to do this to get at them.
+// These are well-established paths that should not change unless the plugin API
+// version changes.
+var (
+ activatePath = "/Plugin.Activate"
+ createPath = "/VolumeDriver.Create"
+ getPath = "/VolumeDriver.Get"
+ listPath = "/VolumeDriver.List"
+ removePath = "/VolumeDriver.Remove"
+ hostVirtualPath = "/VolumeDriver.Path"
+ mountPath = "/VolumeDriver.Mount"
+ unmountPath = "/VolumeDriver.Unmount"
+ // nolint
+ capabilitiesPath = "/VolumeDriver.Capabilities"
+)
+
+const (
+ defaultTimeout = 5 * time.Second
+ defaultPath = "/run/docker/plugins"
+ volumePluginType = "VolumeDriver"
+)
+
+var (
+ ErrNotPlugin = errors.New("target does not appear to be a valid plugin")
+ ErrNotVolumePlugin = errors.New("plugin is not a volume plugin")
+ ErrPluginRemoved = errors.New("plugin is no longer available (shut down?)")
+
+ // This stores available, initialized volume plugins.
+ pluginsLock sync.Mutex
+ plugins map[string]*VolumePlugin
+)
+
+// VolumePlugin is a single volume plugin.
+type VolumePlugin struct {
+ // Name is the name of the volume plugin. This will be used to refer to
+ // it.
+ Name string
+ // SocketPath is the unix socket at which the plugin is accessed.
+ SocketPath string
+}
+
+// This is the response from the activate endpoint of the API.
+type activateResponse struct {
+ Implements []string
+}
+
+// Validate that the given plugin is good to use.
+// Add it to available plugins if so.
+func validatePlugin(newPlugin *VolumePlugin) error {
+ // It's a socket. Is it a plugin?
+ // Hit the Activate endpoint to find out if it is, and if so what kind
+ req, err := http.NewRequest("POST", activatePath, nil)
+ if err != nil {
+ return errors.Wrapf(err, "error making request to volume plugin %s activation endpoint", newPlugin.Name)
+ }
+
+ req.Header.Set("Host", newPlugin.getURI())
+ req.Header.Set("Content-Type", sdk.DefaultContentTypeV1_1)
+
+ client := new(http.Client)
+ client.Timeout = defaultTimeout
+ resp, err := client.Do(req)
+ if err != nil {
+ return errors.Wrapf(err, "error sending request to plugin %s activation endpoint", newPlugin.Name)
+ }
+ defer resp.Body.Close()
+
+ // Response code MUST be 200. Anything else, we have to assume it's not
+ // a valid plugin.
+ if resp.StatusCode != 200 {
+ return errors.Wrapf(ErrNotPlugin, "got status code %d from activation endpoint for plugin %s", resp.StatusCode, newPlugin.Name)
+ }
+
+ // Read and decode the body so we can tell if this is a volume plugin.
+ respBytes, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return errors.Wrapf(err, "error reading activation response body from plugin %s", newPlugin.Name)
+ }
+
+ respStruct := new(activateResponse)
+ if err := json.Unmarshal(respBytes, respStruct); err != nil {
+ return errors.Wrapf(err, "error unmarshalling plugin %s activation response", newPlugin.Name)
+ }
+
+ foundVolume := false
+ for _, pluginType := range respStruct.Implements {
+ if pluginType == volumePluginType {
+ foundVolume = true
+ break
+ }
+ }
+
+ if !foundVolume {
+ return errors.Wrapf(ErrNotVolumePlugin, "plugin %s does not implement volume plugin, instead provides %s", newPlugin.Name, strings.Join(respStruct.Implements, ", "))
+ }
+
+ plugins[newPlugin.Name] = newPlugin
+
+ return nil
+}
+
+// GetVolumePlugin gets a single volume plugin by path.
+// TODO: We should not be auto-completing based on a default path; we should
+// require volumes to have been pre-specified in containers.conf (will need a
+// function to pre-populate the plugins list, and we should probably do a lazy
+// initialization there to not slow things down too much).
+func GetVolumePlugin(name string) (*VolumePlugin, error) {
+ pluginsLock.Lock()
+ defer pluginsLock.Unlock()
+
+ plugin, exists := plugins[name]
+ if exists {
+ return plugin, nil
+ }
+
+ // It's not cached. We need to get it.
+
+ newPlugin := new(VolumePlugin)
+ newPlugin.Name = name
+ newPlugin.SocketPath = filepath.Join(defaultPath, fmt.Sprintf("%s.sock", name))
+
+ stat, err := os.Stat(newPlugin.SocketPath)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot access plugin %s socket %q", name, newPlugin.SocketPath)
+ }
+ if stat.Mode()&os.ModeSocket == 0 {
+ return nil, errors.Wrapf(ErrNotPlugin, "volume %s path %q is not a unix socket", name, newPlugin.SocketPath)
+ }
+
+ if err := validatePlugin(newPlugin); err != nil {
+ return nil, err
+ }
+
+ return newPlugin, nil
+}
+
+func (p *VolumePlugin) getURI() string {
+ return "unix://" + p.SocketPath
+}
+
+// Verify the plugin is still available.
+// TODO: Do we want to ping with an HTTP request? There's no ping endpoint so
+// we'd need to hit Activate or Capabilities?
+func (p *VolumePlugin) verifyReachable() error {
+ if _, err := os.Stat(p.SocketPath); err != nil {
+ if os.IsNotExist(err) {
+ pluginsLock.Lock()
+ defer pluginsLock.Unlock()
+ delete(plugins, p.Name)
+ return errors.Wrapf(ErrPluginRemoved, p.Name)
+ }
+
+ return errors.Wrapf(err, "error accessing plugin %s", p.Name)
+ }
+ return nil
+}
+
+// Send a request to the volume plugin for handling.
+func (p *VolumePlugin) sendRequest(toJSON interface{}, hasBody bool, endpoint string) (*http.Response, error) {
+ var (
+ reqJSON []byte
+ err error
+ )
+
+ if hasBody {
+ reqJSON, err = json.Marshal(toJSON)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error marshalling request JSON for volume plugin %s endpoint %s", p.Name, endpoint)
+ }
+ }
+
+ req, err := http.NewRequest("POST", endpoint, bytes.NewReader(reqJSON))
+ if err != nil {
+ return nil, errors.Wrapf(err, "error making request to volume plugin %s endpoint %s", p.Name, endpoint)
+ }
+
+ req.Header.Set("Host", p.getURI())
+ req.Header.Set("Content-Type", sdk.DefaultContentTypeV1_1)
+
+ client := new(http.Client)
+ client.Timeout = defaultTimeout
+ resp, err := client.Do(req)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error sending request to volume plugin %s endpoint %s", p.Name, endpoint)
+ }
+ defer resp.Body.Close()
+
+ return resp, nil
+}
+
+// Turn an error response from a volume plugin into a well-formatted Go error.
+func (p *VolumePlugin) makeErrorResponse(err, endpoint, volName string) error {
+ if err == "" {
+ err = "empty error from plugin"
+ }
+ if volName != "" {
+ return errors.Wrapf(errors.New(err), "error on %s on volume %s in volume plugin %s", endpoint, volName, p.Name)
+ } else {
+ return errors.Wrapf(errors.New(err), "error on %s in volume plugin %s", endpoint, p.Name)
+ }
+}
+
+// Handle error responses from plugin
+func (p *VolumePlugin) handleErrorResponse(resp *http.Response, endpoint, volName string) error {
+ // The official plugin reference implementation uses HTTP 500 for
+ // errors, but I don't think we can guarantee all plugins do that.
+ // Let's interpret anything other than 200 as an error.
+ // If there isn't an error, don't even bother decoding the response.
+ if resp.StatusCode != 200 {
+ errResp, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return errors.Wrapf(err, "error reading response body from volume plugin %s", p.Name)
+ }
+
+ errStruct := new(volume.ErrorResponse)
+ if err := json.Unmarshal(errResp, errStruct); err != nil {
+ return errors.Wrapf(err, "error unmarshalling JSON response from volume plugin %s", p.Name)
+ }
+
+ return p.makeErrorResponse(errStruct.Err, endpoint, volName)
+ }
+
+ return nil
+}
+
+// CreateVolume creates a volume in the plugin.
+func (p *VolumePlugin) CreateVolume(req *volume.CreateRequest) error {
+ if req == nil {
+ return errors.Wrapf(define.ErrInvalidArg, "must provide non-nil request to CreateVolume")
+ }
+
+ if err := p.verifyReachable(); err != nil {
+ return err
+ }
+
+ logrus.Infof("Creating volume %s using plugin %s", req.Name, p.Name)
+
+ resp, err := p.sendRequest(req, true, createPath)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return p.handleErrorResponse(resp, createPath, req.Name)
+}
+
+// ListVolumes lists volumes available in the plugin.
+func (p *VolumePlugin) ListVolumes() ([]*volume.Volume, error) {
+ if err := p.verifyReachable(); err != nil {
+ return nil, err
+ }
+
+ logrus.Infof("Listing volumes using plugin %s", p.Name)
+
+ resp, err := p.sendRequest(nil, false, listPath)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ if err := p.handleErrorResponse(resp, listPath, ""); err != nil {
+ return nil, err
+ }
+
+ // TODO: Can probably unify response reading under a helper
+ volumeRespBytes, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading response body from volume plugin %s", p.Name)
+ }
+
+ volumeResp := new(volume.ListResponse)
+ if err := json.Unmarshal(volumeRespBytes, volumeResp); err != nil {
+ return nil, errors.Wrapf(err, "error unmarshalling volume plugin %s list response", p.Name)
+ }
+
+ return volumeResp.Volumes, nil
+}
+
+// GetVolume gets a single volume from the plugin.
+func (p *VolumePlugin) GetVolume(req *volume.GetRequest) (*volume.Volume, error) {
+ if req == nil {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "must provide non-nil request to GetVolume")
+ }
+
+ if err := p.verifyReachable(); err != nil {
+ return nil, err
+ }
+
+ logrus.Infof("Getting volume %s using plugin %s", req.Name, p.Name)
+
+ resp, err := p.sendRequest(req, true, getPath)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ if err := p.handleErrorResponse(resp, getPath, req.Name); err != nil {
+ return nil, err
+ }
+
+ getRespBytes, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading response body from volume plugin %s", p.Name)
+ }
+
+ getResp := new(volume.GetResponse)
+ if err := json.Unmarshal(getRespBytes, getResp); err != nil {
+ return nil, errors.Wrapf(err, "error unmarshalling volume plugin %s get response", p.Name)
+ }
+
+ return getResp.Volume, nil
+}
+
+// RemoveVolume removes a single volume from the plugin.
+func (p *VolumePlugin) RemoveVolume(req *volume.RemoveRequest) error {
+ if req == nil {
+ return errors.Wrapf(define.ErrInvalidArg, "must provide non-nil request to RemoveVolume")
+ }
+
+ if err := p.verifyReachable(); err != nil {
+ return err
+ }
+
+ logrus.Infof("Removing volume %s using plugin %s", req.Name, p.Name)
+
+ resp, err := p.sendRequest(req, true, removePath)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return p.handleErrorResponse(resp, removePath, req.Name)
+}
+
+// GetVolumePath gets the path the given volume is mounted at.
+func (p *VolumePlugin) GetVolumePath(req *volume.PathRequest) (string, error) {
+ if req == nil {
+ return "", errors.Wrapf(define.ErrInvalidArg, "must provide non-nil request to GetVolumePath")
+ }
+
+ if err := p.verifyReachable(); err != nil {
+ return "", err
+ }
+
+ logrus.Infof("Getting volume %s path using plugin %s", req.Name, p.Name)
+
+ resp, err := p.sendRequest(req, true, hostVirtualPath)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ if err := p.handleErrorResponse(resp, hostVirtualPath, req.Name); err != nil {
+ return "", err
+ }
+
+ pathRespBytes, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return "", errors.Wrapf(err, "error reading response body from volume plugin %s", p.Name)
+ }
+
+ pathResp := new(volume.PathResponse)
+ if err := json.Unmarshal(pathRespBytes, pathResp); err != nil {
+ return "", errors.Wrapf(err, "error unmarshalling volume plugin %s path response", p.Name)
+ }
+
+ return pathResp.Mountpoint, nil
+}
+
+// MountVolume mounts the given volume. The ID argument is the ID of the
+// mounting container, used for internal record-keeping by the plugin. Returns
+// the path the volume has been mounted at.
+func (p *VolumePlugin) MountVolume(req *volume.MountRequest) (string, error) {
+ if req == nil {
+ return "", errors.Wrapf(define.ErrInvalidArg, "must provide non-nil request to MountVolume")
+ }
+
+ if err := p.verifyReachable(); err != nil {
+ return "", err
+ }
+
+ logrus.Infof("Mounting volume %s using plugin %s for container %s", req.Name, p.Name, req.ID)
+
+ resp, err := p.sendRequest(req, true, mountPath)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ if err := p.handleErrorResponse(resp, mountPath, req.Name); err != nil {
+ return "", err
+ }
+
+ mountRespBytes, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return "", errors.Wrapf(err, "error reading response body from volume plugin %s", p.Name)
+ }
+
+ mountResp := new(volume.MountResponse)
+ if err := json.Unmarshal(mountRespBytes, mountResp); err != nil {
+ return "", errors.Wrapf(err, "error unmarshalling volume plugin %s path response", p.Name)
+ }
+
+ return mountResp.Mountpoint, nil
+}
+
+// UnmountVolume unmounts the given volume. The ID argument is the ID of the
+// container that is unmounting, used for internal record-keeping by the plugin.
+func (p *VolumePlugin) UnmountVolume(req *volume.UnmountRequest) error {
+ if req == nil {
+ return errors.Wrapf(define.ErrInvalidArg, "must provide non-nil request to UnmountVolume")
+ }
+
+ if err := p.verifyReachable(); err != nil {
+ return err
+ }
+
+ logrus.Infof("Unmounting volume %s using plugin %s for container %s", req.Name, p.Name, req.ID)
+
+ resp, err := p.sendRequest(req, true, unmountPath)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return p.handleErrorResponse(resp, unmountPath, req.Name)
+}
diff --git a/libpod/reset.go b/libpod/reset.go
index f8828fed4..6d2842723 100644
--- a/libpod/reset.go
+++ b/libpod/reset.go
@@ -46,7 +46,7 @@ func (r *Runtime) Reset(ctx context.Context) error {
}
}
- if err := stopPauseProcess(); err != nil {
+ if err := r.stopPauseProcess(); err != nil {
logrus.Errorf("Error stopping pause process: %v", err)
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index df3dfae2b..72bd34a5e 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -387,8 +387,8 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
// Don't fatally error.
// This will allow us to ship configs including optional
// runtimes that might not be installed (crun, kata).
- // Only a warnf so default configs don't spec errors.
- logrus.Warnf("Error initializing configured OCI runtime %s: %v", name, err)
+ // Only a infof so default configs don't spec errors.
+ logrus.Infof("Error initializing configured OCI runtime %s: %v", name, err)
continue
}
@@ -472,7 +472,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
// we will need to access the storage.
if os.Geteuid() != 0 {
aliveLock.Unlock() // Unlock to avoid deadlock as BecomeRootInUserNS will reexec.
- pausePid, err := util.GetRootlessPauseProcessPidPath()
+ pausePid, err := util.GetRootlessPauseProcessPidPathGivenDir(runtime.config.Engine.TmpDir)
if err != nil {
return errors.Wrapf(err, "could not get pause process pid file path")
}
@@ -538,6 +538,15 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
return nil
}
+// TmpDir gets the current Libpod temporary files directory.
+func (r *Runtime) TmpDir() (string, error) {
+ if !r.valid {
+ return "", define.ErrRuntimeStopped
+ }
+
+ return r.config.Engine.TmpDir, nil
+}
+
// GetConfig returns a copy of the configuration used by the runtime
func (r *Runtime) GetConfig() (*config.Config, error) {
r.lock.RLock()
diff --git a/libpod/runtime_cstorage.go b/libpod/runtime_cstorage.go
index 61fdd42d3..6ee8a9354 100644
--- a/libpod/runtime_cstorage.go
+++ b/libpod/runtime_cstorage.go
@@ -103,7 +103,7 @@ func (r *Runtime) removeStorageContainer(idOrName string, force bool) error {
if errors.Cause(err) == storage.ErrContainerUnknown {
// Container was removed from under us.
// It's gone, so don't bother erroring.
- logrus.Warnf("Storage for container %s already removed", ctr.ID)
+ logrus.Infof("Storage for container %s already removed", ctr.ID)
return nil
}
return errors.Wrapf(err, "error looking up container %q mounts", idOrName)
@@ -114,7 +114,7 @@ func (r *Runtime) removeStorageContainer(idOrName string, force bool) error {
} else if _, err := r.store.Unmount(ctr.ID, true); err != nil {
if errors.Cause(err) == storage.ErrContainerUnknown {
// Container again gone, no error
- logrus.Warnf("Storage for container %s already removed", ctr.ID)
+ logrus.Infof("Storage for container %s already removed", ctr.ID)
return nil
}
return errors.Wrapf(err, "error unmounting container %q", idOrName)
@@ -123,7 +123,7 @@ func (r *Runtime) removeStorageContainer(idOrName string, force bool) error {
if err := r.store.DeleteContainer(ctr.ID); err != nil {
if errors.Cause(err) == storage.ErrContainerUnknown {
// Container again gone, no error
- logrus.Warnf("Storage for container %s already removed", ctr.ID)
+ logrus.Infof("Storage for container %s already removed", ctr.ID)
return nil
}
return errors.Wrapf(err, "error removing storage for container %q", idOrName)
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index e57890fa2..a2d9a875e 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -8,7 +8,6 @@ import (
"net/http"
"net/url"
"os"
- "strings"
"github.com/containers/buildah/imagebuildah"
"github.com/containers/image/v5/directory"
@@ -276,56 +275,47 @@ func DownloadFromFile(reader *os.File) (string, error) {
}
// LoadImage loads a container image into local storage
-func (r *Runtime) LoadImage(ctx context.Context, name, inputFile string, writer io.Writer, signaturePolicy string) (string, error) {
- var (
- newImages []*image.Image
- err error
- src types.ImageReference
- )
+func (r *Runtime) LoadImage(ctx context.Context, inputFile string, writer io.Writer, signaturePolicy string) (string, error) {
+ if newImages, err := r.LoadAllImageFromArchive(ctx, writer, inputFile, signaturePolicy); err == nil {
+ return newImages, nil
+ }
+ return r.LoadImageFromSingleImageArchive(ctx, writer, inputFile, signaturePolicy)
+}
- if name == "" {
- newImages, err = r.ImageRuntime().LoadAllImagesFromDockerArchive(ctx, inputFile, signaturePolicy, writer)
- if err == nil {
- return getImageNames(newImages), nil
- }
+// LoadAllImageFromArchive loads all images from the archive of multi-image that inputFile points to.
+func (r *Runtime) LoadAllImageFromArchive(ctx context.Context, writer io.Writer, inputFile, signaturePolicy string) (string, error) {
+ newImages, err := r.ImageRuntime().LoadAllImagesFromDockerArchive(ctx, inputFile, signaturePolicy, writer)
+ if err == nil {
+ return getImageNames(newImages), nil
}
+ return "", err
+}
+// LoadImageFromSingleImageArchive load image from the archive of single image that inputFile points to.
+func (r *Runtime) LoadImageFromSingleImageArchive(ctx context.Context, writer io.Writer, inputFile, signaturePolicy string) (string, error) {
+ var err error
for _, referenceFn := range []func() (types.ImageReference, error){
func() (types.ImageReference, error) {
return dockerarchive.ParseReference(inputFile)
},
func() (types.ImageReference, error) {
- return ociarchive.NewReference(inputFile, name) // name may be ""
- },
- func() (types.ImageReference, error) {
- // prepend "localhost/" to support local image saved with this semantics
- if !strings.Contains(name, "/") {
- return ociarchive.NewReference(inputFile, fmt.Sprintf("%s/%s", image.DefaultLocalRegistry, name))
- }
- return nil, nil
+ return ociarchive.NewReference(inputFile, "")
},
func() (types.ImageReference, error) {
return directory.NewReference(inputFile)
},
func() (types.ImageReference, error) {
- return layout.NewReference(inputFile, name)
- },
- func() (types.ImageReference, error) {
- // prepend "localhost/" to support local image saved with this semantics
- if !strings.Contains(name, "/") {
- return layout.NewReference(inputFile, fmt.Sprintf("%s/%s", image.DefaultLocalRegistry, name))
- }
- return nil, nil
+ return layout.NewReference(inputFile, "")
},
} {
- src, err = referenceFn()
+ src, err := referenceFn()
if err == nil && src != nil {
- if newImages, err = r.ImageRuntime().LoadFromArchiveReference(ctx, src, signaturePolicy, writer); err == nil {
+ if newImages, err := r.ImageRuntime().LoadFromArchiveReference(ctx, src, signaturePolicy, writer); err == nil {
return getImageNames(newImages), nil
}
}
}
- return "", errors.Wrapf(err, "error pulling %q", name)
+ return "", errors.Wrapf(err, "error pulling image")
}
func getImageNames(images []*image.Image) string {
diff --git a/libpod/runtime_migrate.go b/libpod/runtime_migrate.go
index 1ad32fe9c..f0f800ef0 100644
--- a/libpod/runtime_migrate.go
+++ b/libpod/runtime_migrate.go
@@ -18,9 +18,9 @@ import (
"github.com/sirupsen/logrus"
)
-func stopPauseProcess() error {
+func (r *Runtime) stopPauseProcess() error {
if rootless.IsRootless() {
- pausePidPath, err := util.GetRootlessPauseProcessPidPath()
+ pausePidPath, err := util.GetRootlessPauseProcessPidPathGivenDir(r.config.Engine.TmpDir)
if err != nil {
return errors.Wrapf(err, "could not get pause process pid file path")
}
@@ -98,5 +98,5 @@ func (r *Runtime) migrate(ctx context.Context) error {
}
}
- return stopPauseProcess()
+ return r.stopPauseProcess()
}
diff --git a/libpod/runtime_migrate_unsupported.go b/libpod/runtime_migrate_unsupported.go
index e362cca63..a9d351318 100644
--- a/libpod/runtime_migrate_unsupported.go
+++ b/libpod/runtime_migrate_unsupported.go
@@ -10,6 +10,6 @@ func (r *Runtime) migrate(ctx context.Context) error {
return nil
}
-func stopPauseProcess() error {
+func (r *Runtime) stopPauseProcess() error {
return nil
}
diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go
index 76419587a..3e4185db1 100644
--- a/libpod/runtime_pod_infra_linux.go
+++ b/libpod/runtime_pod_infra_linux.go
@@ -34,40 +34,56 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
// Set Pod hostname
g.Config.Hostname = p.config.Hostname
+ var options []CtrCreateOption
+
+ // Command: If user-specified, use that preferentially.
+ // If not set and the config file is set, fall back to that.
+ var infraCtrCommand []string
+ if p.config.InfraContainer.InfraCommand != nil {
+ logrus.Debugf("User-specified infra container entrypoint %v", p.config.InfraContainer.InfraCommand)
+ infraCtrCommand = p.config.InfraContainer.InfraCommand
+ } else if r.config.Engine.InfraCommand != "" {
+ logrus.Debugf("Config-specified infra container entrypoint %s", r.config.Engine.InfraCommand)
+ infraCtrCommand = []string{r.config.Engine.InfraCommand}
+ }
+ // Only if set by the user or containers.conf, we set entrypoint for the
+ // infra container.
+ // This is only used by commit, so it shouldn't matter... But someone
+ // may eventually want to commit an infra container?
+ // TODO: Should we actually do this if set by containers.conf?
+ if infraCtrCommand != nil {
+ // Need to duplicate the array - we are going to add Cmd later
+ // so the current array will be changed.
+ newArr := make([]string, 0, len(infraCtrCommand))
+ newArr = append(newArr, infraCtrCommand...)
+ options = append(options, WithEntrypoint(newArr))
+ }
+
isRootless := rootless.IsRootless()
- entrypointSet := len(p.config.InfraContainer.InfraCommand) > 0
- entryPoint := p.config.InfraContainer.InfraCommand
- entryCmd := []string{}
- var options []CtrCreateOption
// I've seen circumstances where config is being passed as nil.
// Let's err on the side of safety and make sure it's safe to use.
if config != nil {
- // default to entrypoint in image if there is one
- if !entrypointSet {
- if len(config.Entrypoint) > 0 {
- entrypointSet = true
- entryPoint = config.Entrypoint
- entryCmd = config.Entrypoint
+ if infraCtrCommand == nil {
+ // If we have no entrypoint and command from the image,
+ // we can't go on - the infra container has no command.
+ if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 {
+ return nil, errors.Errorf("infra container has no command")
}
- } else { // so use the InfraCommand
- entrypointSet = true
- entryCmd = entryPoint
- }
-
- if len(config.Cmd) > 0 {
- // We can't use the default pause command, since we're
- // sourcing from the image. If we didn't already set an
- // entrypoint, set one now.
- if !entrypointSet {
+ if len(config.Entrypoint) > 0 {
+ infraCtrCommand = config.Entrypoint
+ } else {
// Use the Docker default "/bin/sh -c"
// entrypoint, as we're overriding command.
// If an image doesn't want this, it can
// override entrypoint too.
- entryCmd = []string{"/bin/sh", "-c"}
+ infraCtrCommand = []string{"/bin/sh", "-c"}
}
- entryCmd = append(entryCmd, config.Cmd...)
}
+ if len(config.Cmd) > 0 {
+ infraCtrCommand = append(infraCtrCommand, config.Cmd...)
+ }
+
if len(config.Env) > 0 {
for _, nameValPair := range config.Env {
nameValSlice := strings.Split(nameValPair, "=")
@@ -127,9 +143,9 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
}
g.SetRootReadonly(true)
- g.SetProcessArgs(entryCmd)
+ g.SetProcessArgs(infraCtrCommand)
- logrus.Debugf("Using %q as infra container entrypoint", entryCmd)
+ logrus.Debugf("Using %q as infra container command", infraCtrCommand)
g.RemoveMount("/dev/shm")
if isRootless {
@@ -148,9 +164,6 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
options = append(options, WithRootFSFromImage(imgID, imgName, rawImageName))
options = append(options, WithName(containerName))
options = append(options, withIsInfra())
- if entrypointSet {
- options = append(options, WithEntrypoint(entryPoint))
- }
if len(p.config.InfraContainer.ConmonPidFile) > 0 {
options = append(options, WithConmonPidFile(p.config.InfraContainer.ConmonPidFile))
}
diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go
index 25598ce4d..1eb42660c 100644
--- a/libpod/runtime_pod_linux.go
+++ b/libpod/runtime_pod_linux.go
@@ -117,7 +117,7 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Po
return nil, errors.Errorf("Pods must have an infra container to share namespaces")
}
if pod.HasInfraContainer() && !pod.SharesNamespaces() {
- logrus.Warnf("Pod has an infra container, but shares no namespaces")
+ logrus.Infof("Pod has an infra container, but shares no namespaces")
}
if err := r.state.AddPod(pod); err != nil {
@@ -212,7 +212,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool)
// Don't try if we failed to retrieve the cgroup
if err == nil {
if err := conmonCgroup.Update(resLimits); err != nil {
- logrus.Warnf("Error updating pod %s conmon cgroup %s PID limit: %v", p.ID(), conmonCgroupPath, err)
+ logrus.Warnf("Error updating pod %s conmon cgroup PID limit: %v", p.ID(), err)
}
}
}