summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/boltdb_state.go293
-rw-r--r--libpod/boltdb_state_internal.go62
-rw-r--r--libpod/container.go70
-rw-r--r--libpod/container_config.go39
-rw-r--r--libpod/container_inspect.go75
-rw-r--r--libpod/container_internal.go36
-rw-r--r--libpod/container_internal_linux.go250
-rw-r--r--libpod/container_validate.go13
-rw-r--r--libpod/define/annotations.go12
-rw-r--r--libpod/define/container_inspect.go40
-rw-r--r--libpod/define/pod_inspect.go2
-rw-r--r--libpod/healthcheck_linux.go32
-rw-r--r--libpod/kube.go2
-rw-r--r--libpod/network/internal/util/util.go2
-rw-r--r--libpod/network/types/network.go3
-rw-r--r--libpod/networking_linux.go221
-rw-r--r--libpod/options.go94
-rw-r--r--libpod/pod_api.go17
-rw-r--r--libpod/runtime_ctr.go64
-rw-r--r--libpod/runtime_img.go17
-rw-r--r--libpod/runtime_volume_linux.go42
-rw-r--r--libpod/state.go13
-rw-r--r--libpod/state_test.go18
-rw-r--r--libpod/volume_internal.go2
-rw-r--r--libpod/volume_internal_linux.go35
25 files changed, 747 insertions, 707 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index 1242a8d6b..9669cf921 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -2,11 +2,14 @@ package libpod
import (
"bytes"
+ "fmt"
+ "net"
"os"
"strings"
"sync"
"github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/podman/v3/libpod/network/types"
jsoniter "github.com/json-iterator/go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -971,7 +974,7 @@ func (s *BoltState) AllContainers() ([]*Container, error) {
}
// GetNetworks returns the CNI networks this container is a part of.
-func (s *BoltState) GetNetworks(ctr *Container) ([]string, error) {
+func (s *BoltState) GetNetworks(ctr *Container) (map[string]types.PerNetworkOptions, error) {
if !s.valid {
return nil, define.ErrDBClosed
}
@@ -984,61 +987,9 @@ func (s *BoltState) GetNetworks(ctr *Container) ([]string, error) {
return nil, errors.Wrapf(define.ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace)
}
- ctrID := []byte(ctr.ID())
-
- db, err := s.getDBCon()
- if err != nil {
- return nil, err
- }
- defer s.deferredCloseDBCon(db)
-
- networks := []string{}
-
- err = db.View(func(tx *bolt.Tx) error {
- ctrBucket, err := getCtrBucket(tx)
- if err != nil {
- return err
- }
-
- dbCtr := ctrBucket.Bucket(ctrID)
- if dbCtr == nil {
- ctr.valid = false
- return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID())
- }
-
- ctrNetworkBkt := dbCtr.Bucket(networksBkt)
- if ctrNetworkBkt == nil {
- return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not joined to any CNI networks", ctr.ID())
- }
-
- return ctrNetworkBkt.ForEach(func(network, v []byte) error {
- networks = append(networks, string(network))
- return nil
- })
- })
- if err != nil {
- return nil, err
- }
- return networks, nil
-}
-
-// GetNetworkAliases retrieves the network aliases for the given container in
-// the given CNI network.
-func (s *BoltState) GetNetworkAliases(ctr *Container, network string) ([]string, error) {
- if !s.valid {
- return nil, define.ErrDBClosed
- }
-
- if !ctr.valid {
- return nil, define.ErrCtrRemoved
- }
-
- if network == "" {
- return nil, errors.Wrapf(define.ErrInvalidArg, "network names must not be empty")
- }
-
- if s.namespace != "" && s.namespace != ctr.config.Namespace {
- return nil, errors.Wrapf(define.ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace)
+ // if the network mode is not bridge return no networks
+ if !ctr.config.NetMode.IsBridge() {
+ return nil, nil
}
ctrID := []byte(ctr.ID())
@@ -1049,7 +1000,9 @@ func (s *BoltState) GetNetworkAliases(ctr *Container, network string) ([]string,
}
defer s.deferredCloseDBCon(db)
- aliases := []string{}
+ networks := make(map[string]types.PerNetworkOptions)
+
+ var convertDB bool
err = db.View(func(tx *bolt.Tx) error {
ctrBucket, err := getCtrBucket(tx)
@@ -1065,115 +1018,137 @@ func (s *BoltState) GetNetworkAliases(ctr *Container, network string) ([]string,
ctrNetworkBkt := dbCtr.Bucket(networksBkt)
if ctrNetworkBkt == nil {
- // No networks joined, so no aliases
+ // convert if needed
+ convertDB = true
return nil
}
- inNetwork := ctrNetworkBkt.Get([]byte(network))
- if inNetwork == nil {
- return errors.Wrapf(define.ErrNoAliases, "container %s is not part of network %s, no aliases found", ctr.ID(), network)
- }
-
- ctrAliasesBkt := dbCtr.Bucket(aliasesBkt)
- if ctrAliasesBkt == nil {
- // No aliases
- return nil
- }
-
- netAliasesBkt := ctrAliasesBkt.Bucket([]byte(network))
- if netAliasesBkt == nil {
- // No aliases for this specific network.
- return nil
- }
-
- return netAliasesBkt.ForEach(func(alias, v []byte) error {
- aliases = append(aliases, string(alias))
+ return ctrNetworkBkt.ForEach(func(network, v []byte) error {
+ opts := types.PerNetworkOptions{}
+ if err := json.Unmarshal(v, &opts); err != nil {
+ // special case for backwards compat
+ // earlier version used the container id as value so we set a
+ // special error to indicate the we have to migrate the db
+ if !bytes.Equal(v, ctrID) {
+ return err
+ }
+ convertDB = true
+ }
+ networks[string(network)] = opts
return nil
})
})
if err != nil {
return nil, err
}
+ if convertDB {
+ err = db.Update(func(tx *bolt.Tx) error {
+ ctrBucket, err := getCtrBucket(tx)
+ if err != nil {
+ return err
+ }
- return aliases, nil
-}
-
-// GetAllNetworkAliases retrieves the network aliases for the given container in
-// all CNI networks.
-func (s *BoltState) GetAllNetworkAliases(ctr *Container) (map[string][]string, error) {
- if !s.valid {
- return nil, define.ErrDBClosed
- }
-
- if !ctr.valid {
- return nil, define.ErrCtrRemoved
- }
-
- if s.namespace != "" && s.namespace != ctr.config.Namespace {
- return nil, errors.Wrapf(define.ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace)
- }
-
- ctrID := []byte(ctr.ID())
+ dbCtr := ctrBucket.Bucket(ctrID)
+ if dbCtr == nil {
+ ctr.valid = false
+ return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID())
+ }
- db, err := s.getDBCon()
- if err != nil {
- return nil, err
- }
- defer s.deferredCloseDBCon(db)
+ var networkList []string
- aliases := make(map[string][]string)
+ ctrNetworkBkt := dbCtr.Bucket(networksBkt)
+ if ctrNetworkBkt == nil {
+ ctrNetworkBkt, err = dbCtr.CreateBucket(networksBkt)
+ if err != nil {
+ return errors.Wrapf(err, "error creating networks bucket for container %s", ctr.ID())
+ }
+ // the container has no networks in the db lookup config and write to the db
+ networkList = ctr.config.NetworksDeprecated
+ // if there are no networks we have to add the default
+ if len(networkList) == 0 {
+ networkList = []string{ctr.runtime.config.Network.DefaultNetwork}
+ }
+ } else {
+ err = ctrNetworkBkt.ForEach(func(network, v []byte) error {
+ networkList = append(networkList, string(network))
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+ }
- err = db.View(func(tx *bolt.Tx) error {
- ctrBucket, err := getCtrBucket(tx)
- if err != nil {
- return err
- }
+ // the container has no networks in the db lookup config and write to the db
+ for i, network := range networkList {
+ var intName string
+ if ctr.state.NetInterfaceDescriptions != nil {
+ eth, exists := ctr.state.NetInterfaceDescriptions.getInterfaceByName(network)
+ if !exists {
+ return errors.Errorf("no network interface name for container %s on network %s", ctr.config.ID, network)
+ }
+ intName = eth
+ } else {
+ intName = fmt.Sprintf("eth%d", i)
+ }
+ getAliases := func(network string) []string {
+ var aliases []string
+ ctrAliasesBkt := dbCtr.Bucket(aliasesBkt)
+ if ctrAliasesBkt == nil {
+ return nil
+ }
+ netAliasesBkt := ctrAliasesBkt.Bucket([]byte(network))
+ if netAliasesBkt == nil {
+ // No aliases for this specific network.
+ return nil
+ }
- dbCtr := ctrBucket.Bucket(ctrID)
- if dbCtr == nil {
- ctr.valid = false
- return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID())
- }
+ // lets ignore the error here there is nothing we can do
+ _ = netAliasesBkt.ForEach(func(alias, v []byte) error {
+ aliases = append(aliases, string(alias))
+ return nil
+ })
+ // also add the short container id as alias
+ return aliases
+ }
- ctrAliasesBkt := dbCtr.Bucket(aliasesBkt)
- if ctrAliasesBkt == nil {
- // No aliases present
- return nil
- }
+ netOpts := &types.PerNetworkOptions{
+ InterfaceName: intName,
+ // we have to add the short id as alias for docker compat
+ Aliases: append(getAliases(network), ctr.config.ID[:12]),
+ }
+ // only set the static ip/mac on the first network
+ if i == 0 {
+ if ctr.config.StaticIP != nil {
+ netOpts.StaticIPs = []net.IP{ctr.config.StaticIP}
+ }
+ netOpts.StaticMAC = ctr.config.StaticMAC
+ }
- ctrNetworkBkt := dbCtr.Bucket(networksBkt)
- if ctrNetworkBkt == nil {
- // No networks joined, so no aliases
- return nil
- }
+ optsBytes, err := json.Marshal(netOpts)
+ if err != nil {
+ return err
+ }
+ // insert into network map because we need to return this
+ networks[network] = *netOpts
- return ctrNetworkBkt.ForEach(func(network, v []byte) error {
- netAliasesBkt := ctrAliasesBkt.Bucket(network)
- if netAliasesBkt == nil {
- return nil
+ err = ctrNetworkBkt.Put([]byte(network), optsBytes)
+ if err != nil {
+ return err
+ }
}
-
- netAliases := []string{}
-
- _ = netAliasesBkt.ForEach(func(alias, v []byte) error {
- netAliases = append(netAliases, string(alias))
- return nil
- })
-
- aliases[string(network)] = netAliases
return nil
})
- })
- if err != nil {
- return nil, err
+ if err != nil {
+ return nil, err
+ }
}
- return aliases, nil
+ return networks, nil
}
// NetworkConnect adds the given container to the given network. If aliases are
// specified, those will be added to the given network.
-func (s *BoltState) NetworkConnect(ctr *Container, network string, aliases []string) error {
+func (s *BoltState) NetworkConnect(ctr *Container, network string, opts types.PerNetworkOptions) error {
if !s.valid {
return define.ErrDBClosed
}
@@ -1190,6 +1165,11 @@ func (s *BoltState) NetworkConnect(ctr *Container, network string, aliases []str
return errors.Wrapf(define.ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace)
}
+ optBytes, err := json.Marshal(opts)
+ if err != nil {
+ return errors.Wrapf(err, "error marshalling network options JSON for container %s", ctr.ID())
+ }
+
ctrID := []byte(ctr.ID())
db, err := s.getDBCon()
@@ -1210,47 +1190,20 @@ func (s *BoltState) NetworkConnect(ctr *Container, network string, aliases []str
return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID())
}
- ctrAliasesBkt, err := dbCtr.CreateBucketIfNotExists(aliasesBkt)
- if err != nil {
- return errors.Wrapf(err, "error creating aliases bucket for container %s", ctr.ID())
- }
-
ctrNetworksBkt := dbCtr.Bucket(networksBkt)
if ctrNetworksBkt == nil {
- ctrNetworksBkt, err = dbCtr.CreateBucket(networksBkt)
- if err != nil {
- return errors.Wrapf(err, "error creating networks bucket for container %s", ctr.ID())
- }
- ctrNetworks := ctr.config.Networks
- if len(ctrNetworks) == 0 {
- ctrNetworks = []string{ctr.runtime.config.Network.DefaultNetwork}
- }
- // Copy in all the container's CNI networks
- for _, net := range ctrNetworks {
- if err := ctrNetworksBkt.Put([]byte(net), ctrID); err != nil {
- return errors.Wrapf(err, "error adding container %s network %s to DB", ctr.ID(), net)
- }
- }
+ return errors.Wrapf(define.ErrNoSuchNetwork, "container %s does not have a network bucket", ctr.ID())
}
netConnected := ctrNetworksBkt.Get([]byte(network))
if netConnected != nil {
- return errors.Wrapf(define.ErrNetworkExists, "container %s is already connected to CNI network %q", ctr.ID(), network)
+ return errors.Wrapf(define.ErrNetworkExists, "container %s is already connected to network %q", ctr.ID(), network)
}
// Add the network
- if err := ctrNetworksBkt.Put([]byte(network), ctrID); err != nil {
+ if err := ctrNetworksBkt.Put([]byte(network), optBytes); err != nil {
return errors.Wrapf(err, "error adding container %s to network %s in DB", ctr.ID(), network)
}
- ctrNetAliasesBkt, err := ctrAliasesBkt.CreateBucketIfNotExists([]byte(network))
- if err != nil {
- return errors.Wrapf(err, "error adding container %s network aliases bucket for network %s", ctr.ID(), network)
- }
- for _, alias := range aliases {
- if err := ctrNetAliasesBkt.Put([]byte(alias), ctrID); err != nil {
- return errors.Wrapf(err, "error adding container %s network alias %s for network %s", ctr.ID(), alias, network)
- }
- }
return nil
})
}
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index e71d82736..5f0f6ba7d 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -563,6 +563,28 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
ctrNamespace = []byte(ctr.config.Namespace)
}
+ // make sure to marshal the network options before we get the db lock
+ networks := make(map[string][]byte, len(ctr.config.Networks))
+ for net, opts := range ctr.config.Networks {
+ // Check that we don't have any empty network names
+ if net == "" {
+ return errors.Wrapf(define.ErrInvalidArg, "network names cannot be an empty string")
+ }
+ if opts.InterfaceName == "" {
+ return errors.Wrapf(define.ErrInvalidArg, "network interface name cannot be an empty string")
+ }
+ // always add the short id as alias for docker compat
+ opts.Aliases = append(opts.Aliases, ctr.config.ID[:12])
+ optBytes, err := json.Marshal(opts)
+ if err != nil {
+ return errors.Wrapf(err, "error marshalling network options JSON for container %s", ctr.ID())
+ }
+ networks[net] = optBytes
+ }
+ // Set the original value to nil. We can safe some space by not storing it in the config
+ // since we store it in a different mutable bucket anyway.
+ ctr.config.Networks = nil
+
db, err := s.getDBCon()
if err != nil {
return err
@@ -646,23 +668,6 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
return errors.Wrapf(err, "name \"%s\" is in use", ctr.Name())
}
- allNets := make(map[string]bool)
-
- // Check that we don't have any empty network names
- for _, net := range ctr.config.Networks {
- if net == "" {
- return errors.Wrapf(define.ErrInvalidArg, "network names cannot be an empty string")
- }
- allNets[net] = true
- }
-
- // Each network we have aliases for, must exist in networks
- for net := range ctr.config.NetworkAliases {
- if !allNets[net] {
- return errors.Wrapf(define.ErrNoSuchNetwork, "container %s has network aliases for network %q but is not part of that network", ctr.ID(), net)
- }
- }
-
// No overlapping containers
// Add the new container to the DB
if err := idsBucket.Put(ctrID, ctrName); err != nil {
@@ -706,34 +711,17 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
return errors.Wrapf(err, "error adding container %s netns path to DB", ctr.ID())
}
}
- if ctr.config.Networks != nil {
+ if len(networks) > 0 {
ctrNetworksBkt, err := newCtrBkt.CreateBucket(networksBkt)
if err != nil {
return errors.Wrapf(err, "error creating networks bucket for container %s", ctr.ID())
}
- for _, network := range ctr.config.Networks {
- if err := ctrNetworksBkt.Put([]byte(network), ctrID); err != nil {
+ for network, opts := range networks {
+ if err := ctrNetworksBkt.Put([]byte(network), opts); err != nil {
return errors.Wrapf(err, "error adding network %q to networks bucket for container %s", network, ctr.ID())
}
}
}
- if ctr.config.NetworkAliases != nil {
- ctrAliasesBkt, err := newCtrBkt.CreateBucket(aliasesBkt)
- if err != nil {
- return errors.Wrapf(err, "error creating network aliases bucket for container %s", ctr.ID())
- }
- for net, aliases := range ctr.config.NetworkAliases {
- netAliasesBkt, err := ctrAliasesBkt.CreateBucket([]byte(net))
- if err != nil {
- return errors.Wrapf(err, "error creating network aliases bucket for network %q in container %s", net, ctr.ID())
- }
- for _, alias := range aliases {
- if err := netAliasesBkt.Put([]byte(alias), ctrID); err != nil {
- return errors.Wrapf(err, "error creating network alias %q in network %q for container %s", alias, net, ctr.ID())
- }
- }
- }
- }
if _, err := newCtrBkt.CreateBucket(dependenciesBkt); err != nil {
return errors.Wrapf(err, "error creating dependencies bucket for container %s", ctr.ID())
diff --git a/libpod/container.go b/libpod/container.go
index 2b74a1943..c746f97c7 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -213,6 +213,15 @@ type ContainerState struct {
// containerPlatformState holds platform-specific container state.
containerPlatformState
+
+ // Following checkpoint/restore related information is displayed
+ // if the container has been checkpointed or restored.
+ CheckpointedTime time.Time `json:"checkpointedTime,omitempty"`
+ RestoredTime time.Time `json:"restoredTime,omitempty"`
+ CheckpointLog string `json:"checkpointLog,omitempty"`
+ CheckpointPath string `json:"checkpointPath,omitempty"`
+ RestoreLog string `json:"restoreLog,omitempty"`
+ Restored bool `json:"restored,omitempty"`
}
// ContainerNamedVolume is a named volume that will be mounted into the
@@ -1166,17 +1175,28 @@ func (c *Container) Secrets() []*ContainerSecret {
// is joining the default CNI network - the network name will be included in the
// returned array of network names, but the container did not explicitly join
// this network.
-func (c *Container) Networks() ([]string, bool, error) {
+func (c *Container) Networks() ([]string, error) {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
if err := c.syncContainer(); err != nil {
- return nil, false, err
+ return nil, err
}
}
- return c.networks()
+ networks, err := c.networks()
+ if err != nil {
+ return nil, err
+ }
+
+ names := make([]string, 0, len(networks))
+
+ for name := range networks {
+ names = append(names, name)
+ }
+
+ return names, nil
}
// NetworkMode gets the configured network mode for the container.
@@ -1220,36 +1240,8 @@ func (c *Container) NetworkMode() string {
}
// Unlocked accessor for networks
-func (c *Container) networks() ([]string, bool, error) {
- networks, err := c.runtime.state.GetNetworks(c)
- if err != nil && errors.Cause(err) == define.ErrNoSuchNetwork {
- if len(c.config.Networks) == 0 && c.config.NetMode.IsBridge() {
- return []string{c.runtime.config.Network.DefaultNetwork}, true, nil
- }
- return c.config.Networks, false, nil
- }
-
- return networks, false, err
-}
-
-// networksByNameIndex provides us with a map of container networks where key
-// is network name and value is the index position
-func (c *Container) networksByNameIndex() (map[string]int, error) {
- networks, _, err := c.networks()
- if err != nil {
- return nil, err
- }
- networkNamesByIndex := make(map[string]int, len(networks))
- for index, name := range networks {
- networkNamesByIndex[name] = index
- }
- return networkNamesByIndex, nil
-}
-
-// add puts the new given CNI network name into the tracking map
-// and assigns it a new integer based on the map length
-func (d ContainerNetworkDescriptions) add(networkName string) {
- d[networkName] = len(d)
+func (c *Container) networks() (map[string]types.PerNetworkOptions, error) {
+ return c.runtime.state.GetNetworks(c)
}
// getInterfaceByName returns a formatted interface name for a given
@@ -1270,9 +1262,7 @@ func (c *Container) getNetworkStatus() map[string]types.StatusBlock {
return c.state.NetworkStatus
}
if c.state.NetworkStatusOld != nil {
- // Note: NetworkStatusOld does not contain the network names so we get them extra
- // Generally the order should be the same
- networks, _, err := c.networks()
+ networks, err := c.networks()
if err != nil {
return nil
}
@@ -1280,12 +1270,16 @@ func (c *Container) getNetworkStatus() map[string]types.StatusBlock {
return nil
}
result := make(map[string]types.StatusBlock, len(c.state.NetworkStatusOld))
- for i := range c.state.NetworkStatusOld {
+ i := 0
+ // Note: NetworkStatusOld does not contain the network names so we get them extra
+ // We cannot guarantee the same order but after a state refresh it should work
+ for netName := range networks {
status, err := cni.CNIResultToStatus(c.state.NetworkStatusOld[i])
if err != nil {
return nil
}
- result[networks[i]] = status
+ result[netName] = status
+ i++
}
c.state.NetworkStatus = result
_ = c.save()
diff --git a/libpod/container_config.go b/libpod/container_config.go
index 57f5b92ac..288524dbd 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -163,6 +163,8 @@ type ContainerRootFSConfig struct {
// Volatile specifies whether the container storage can be optimized
// at the cost of not syncing all the dirty files in memory.
Volatile bool `json:"volatile,omitempty"`
+ // Passwd allows to user to override podman's passwd/group file setup
+ Passwd *bool `json:"passwd,omitempty"`
}
// ContainerSecurityConfig is an embedded sub-config providing security configuration
@@ -196,6 +198,8 @@ type ContainerSecurityConfig struct {
// Groups are additional groups to add the container's user to. These
// are resolved within the container using the container's /etc/passwd.
Groups []string `json:"groups,omitempty"`
+ // HostUsers are a list of host user accounts to add to /etc/passwd
+ HostUsers []string `json:"HostUsers,omitempty"`
// AddCurrentUserPasswdEntry indicates that Libpod should ensure that
// the container's /etc/passwd contains an entry for the user running
// Libpod - mostly used in rootless containers where the user running
@@ -229,10 +233,12 @@ type ContainerNetworkConfig struct {
// StaticIP is a static IP to request for the container.
// This cannot be set unless CreateNetNS is set.
// If not set, the container will be dynamically assigned an IP by CNI.
+ // Deprecated: Do no use this anymore, this is only for DB backwards compat.
StaticIP net.IP `json:"staticIP"`
// StaticMAC is a static MAC to request for the container.
// This cannot be set unless CreateNetNS is set.
// If not set, the container will be dynamically assigned a MAC by CNI.
+ // Deprecated: Do no use this anymore, this is only for DB backwards compat.
StaticMAC types.HardwareAddr `json:"staticMAC"`
// PortMappings are the ports forwarded to the container's network
// namespace
@@ -269,24 +275,24 @@ type ContainerNetworkConfig struct {
// Hosts to add in container
// Will be appended to host's host file
HostAdd []string `json:"hostsAdd,omitempty"`
- // Network names (CNI) to add container to. Empty to use default network.
+ // Network names with the network specific options.
+ // Please note that these can be altered at runtime. The actual list is
+ // stored in the DB and should be retrieved from there via c.networks()
+ // this value is only used for container create.
+ // Added in podman 4.0, previously NetworksDeprecated was used. Make
+ // sure to not change the json tags.
+ Networks map[string]types.PerNetworkOptions `json:"newNetworks,omitempty"`
+ // Network names to add container to. Empty to use default network.
// Please note that these can be altered at runtime. The actual list is
// stored in the DB and should be retrieved from there; this is only the
// set of networks the container was *created* with.
- Networks []string `json:"networks,omitempty"`
+ // Deprecated: Do no use this anymore, this is only for DB backwards compat.
+ // Also note that we need to keep the old json tag to decode from DB correctly
+ NetworksDeprecated []string `json:"networks,omitempty"`
// Network mode specified for the default network.
NetMode namespaces.NetworkMode `json:"networkMode,omitempty"`
// NetworkOptions are additional options for each network
NetworkOptions map[string][]string `json:"network_options,omitempty"`
- // NetworkAliases are aliases that will be added to each network.
- // These are additional names that this container can be accessed as via
- // DNS when the CNI dnsname plugin is in use.
- // Please note that these can be altered at runtime. As such, the actual
- // list is stored in the database and should be retrieved from there;
- // this is only the set of aliases the container was *created with*.
- // Formatted as map of network name to aliases. All network names must
- // be present in the Networks list above.
- NetworkAliases map[string][]string `json:"network_alises,omitempty"`
}
// ContainerImageConfig is an embedded sub-config providing image configuration
@@ -394,3 +400,14 @@ type ContainerMiscConfig struct {
// and if so, what type: always or once are possible non-nil entries
InitContainerType string `json:"init_container_type,omitempty"`
}
+
+type InfraInherit struct {
+ InfraSecurity ContainerSecurityConfig
+ InfraLabels []string `json:"labelopts,omitempty"`
+ InfraVolumes []*ContainerNamedVolume `json:"namedVolumes,omitempty"`
+ InfraOverlay []*ContainerOverlayVolume `json:"overlayVolumes,omitempty"`
+ InfraImageVolumes []*ContainerImageVolume `json:"ctrImageVolumes,omitempty"`
+ InfraUserVolumes []string `json:"userVolumes,omitempty"`
+ InfraResources *spec.LinuxResources `json:"resources,omitempty"`
+ InfraDevices []spec.LinuxDevice `json:"device_host_src,omitempty"`
+}
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 83b643266..792dfc58e 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -113,20 +113,26 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
Path: path,
Args: args,
State: &define.InspectContainerState{
- OciVersion: ctrSpec.Version,
- Status: runtimeInfo.State.String(),
- Running: runtimeInfo.State == define.ContainerStateRunning,
- Paused: runtimeInfo.State == define.ContainerStatePaused,
- OOMKilled: runtimeInfo.OOMKilled,
- Dead: runtimeInfo.State.String() == "bad state",
- Pid: runtimeInfo.PID,
- ConmonPid: runtimeInfo.ConmonPID,
- ExitCode: runtimeInfo.ExitCode,
- Error: "", // can't get yet
- StartedAt: runtimeInfo.StartedTime,
- FinishedAt: runtimeInfo.FinishedTime,
- Checkpointed: runtimeInfo.Checkpointed,
- CgroupPath: cgroupPath,
+ OciVersion: ctrSpec.Version,
+ Status: runtimeInfo.State.String(),
+ Running: runtimeInfo.State == define.ContainerStateRunning,
+ Paused: runtimeInfo.State == define.ContainerStatePaused,
+ OOMKilled: runtimeInfo.OOMKilled,
+ Dead: runtimeInfo.State.String() == "bad state",
+ Pid: runtimeInfo.PID,
+ ConmonPid: runtimeInfo.ConmonPID,
+ ExitCode: runtimeInfo.ExitCode,
+ Error: "", // can't get yet
+ StartedAt: runtimeInfo.StartedTime,
+ FinishedAt: runtimeInfo.FinishedTime,
+ Checkpointed: runtimeInfo.Checkpointed,
+ CgroupPath: cgroupPath,
+ RestoredAt: runtimeInfo.RestoredTime,
+ CheckpointedAt: runtimeInfo.CheckpointedTime,
+ Restored: runtimeInfo.Restored,
+ CheckpointPath: runtimeInfo.CheckpointPath,
+ CheckpointLog: runtimeInfo.CheckpointLog,
+ RestoreLog: runtimeInfo.RestoreLog,
},
Image: config.RootfsImageID,
ImageName: config.RootfsImageName,
@@ -267,6 +273,27 @@ func (c *Container) GetInspectMounts(namedVolumes []*ContainerNamedVolume, image
return inspectMounts, nil
}
+// GetSecurityOptions retrives and returns the security related annotations and process information upon inspection
+func (c *Container) GetSecurityOptions() []string {
+ ctrSpec := c.config.Spec
+ SecurityOpt := []string{}
+ if ctrSpec.Process != nil {
+ if ctrSpec.Process.NoNewPrivileges {
+ SecurityOpt = append(SecurityOpt, "no-new-privileges")
+ }
+ }
+ if label, ok := ctrSpec.Annotations[define.InspectAnnotationLabel]; ok {
+ SecurityOpt = append(SecurityOpt, fmt.Sprintf("label=%s", label))
+ }
+ if seccomp, ok := ctrSpec.Annotations[define.InspectAnnotationSeccomp]; ok {
+ SecurityOpt = append(SecurityOpt, fmt.Sprintf("seccomp=%s", seccomp))
+ }
+ if apparmor, ok := ctrSpec.Annotations[define.InspectAnnotationApparmor]; ok {
+ SecurityOpt = append(SecurityOpt, fmt.Sprintf("apparmor=%s", apparmor))
+ }
+ return SecurityOpt
+}
+
// Parse mount options so we can populate them in the mount structure.
// The mount passed in will be modified.
func parseMountOptionsForInspect(options []string, mount *define.InspectMount) {
@@ -371,6 +398,8 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
ctrConfig.Umask = c.config.Umask
}
+ ctrConfig.Passwd = c.config.Passwd
+
return ctrConfig
}
@@ -414,16 +443,14 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
hostConfig.GroupAdd = make([]string, 0, len(c.config.Groups))
hostConfig.GroupAdd = append(hostConfig.GroupAdd, c.config.Groups...)
- hostConfig.SecurityOpt = []string{}
if ctrSpec.Process != nil {
if ctrSpec.Process.OOMScoreAdj != nil {
hostConfig.OomScoreAdj = *ctrSpec.Process.OOMScoreAdj
}
- if ctrSpec.Process.NoNewPrivileges {
- hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, "no-new-privileges")
- }
}
+ hostConfig.SecurityOpt = c.GetSecurityOptions()
+
hostConfig.ReadonlyRootfs = ctrSpec.Root.Readonly
hostConfig.ShmSize = c.config.ShmSize
hostConfig.Runtime = "oci"
@@ -448,15 +475,6 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
if ctrSpec.Annotations[define.InspectAnnotationInit] == define.InspectResponseTrue {
hostConfig.Init = true
}
- if label, ok := ctrSpec.Annotations[define.InspectAnnotationLabel]; ok {
- hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("label=%s", label))
- }
- if seccomp, ok := ctrSpec.Annotations[define.InspectAnnotationSeccomp]; ok {
- hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("seccomp=%s", seccomp))
- }
- if apparmor, ok := ctrSpec.Annotations[define.InspectAnnotationApparmor]; ok {
- hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("apparmor=%s", apparmor))
- }
}
// Resource limits
@@ -485,9 +503,6 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
if ctrSpec.Linux.Resources.Memory.Limit != nil {
hostConfig.Memory = *ctrSpec.Linux.Resources.Memory.Limit
}
- if ctrSpec.Linux.Resources.Memory.Kernel != nil {
- hostConfig.KernelMemory = *ctrSpec.Linux.Resources.Memory.Kernel
- }
if ctrSpec.Linux.Resources.Memory.Reservation != nil {
hostConfig.MemoryReservation = *ctrSpec.Linux.Resources.Memory.Reservation
}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index d8187c609..2d12a90d1 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -634,6 +634,12 @@ func resetState(state *ContainerState) {
state.RestartPolicyMatch = false
state.RestartCount = 0
state.Checkpointed = false
+ state.Restored = false
+ state.CheckpointedTime = time.Time{}
+ state.RestoredTime = time.Time{}
+ state.CheckpointPath = ""
+ state.CheckpointLog = ""
+ state.RestoreLog = ""
}
// Refresh refreshes the container's state after a restart.
@@ -756,7 +762,7 @@ func (c *Container) export(path string) error {
if !c.state.Mounted {
containerMount, err := c.runtime.store.Mount(c.ID(), c.config.MountLabel)
if err != nil {
- return errors.Wrapf(err, "error mounting container %q", c.ID())
+ return errors.Wrapf(err, "mounting container %q", c.ID())
}
mountPoint = containerMount
defer func() {
@@ -1111,6 +1117,12 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error {
}
c.state.Checkpointed = false
+ c.state.Restored = false
+ c.state.CheckpointedTime = time.Time{}
+ c.state.RestoredTime = time.Time{}
+ c.state.CheckpointPath = ""
+ c.state.CheckpointLog = ""
+ c.state.RestoreLog = ""
c.state.ExitCode = 0
c.state.Exited = false
c.state.State = define.ContainerStateCreated
@@ -1877,7 +1889,7 @@ func (c *Container) cleanupStorage() error {
return cleanupErr
}
-// Unmount the a container and free its resources
+// Unmount the container and free its resources
func (c *Container) cleanup(ctx context.Context) error {
var lastError error
@@ -1885,7 +1897,7 @@ func (c *Container) cleanup(ctx context.Context) error {
// Remove healthcheck unit/timer file if it execs
if c.config.HealthCheckConfig != nil {
- if err := c.removeTimer(); err != nil {
+ if err := c.removeTransientFiles(ctx); err != nil {
logrus.Errorf("Removing timer for container %s healthcheck: %v", c.ID(), err)
}
}
@@ -2206,6 +2218,24 @@ func (c *Container) canWithPrevious() error {
// prepareCheckpointExport writes the config and spec to
// JSON files for later export
func (c *Container) prepareCheckpointExport() error {
+ networks, err := c.networks()
+ if err != nil {
+ return err
+ }
+ // make sure to exclude the short ID alias since the container gets a new ID on restore
+ for net, opts := range networks {
+ newAliases := make([]string, 0, len(opts.Aliases))
+ for _, alias := range opts.Aliases {
+ if alias != c.config.ID[:12] {
+ newAliases = append(newAliases, alias)
+ }
+ }
+ opts.Aliases = newAliases
+ networks[net] = opts
+ }
+
+ // add the networks from the db to the config so that the exported checkpoint still stores all current networks
+ c.config.Networks = networks
// save live config
if _, err := metadata.WriteJSONFile(c.config, c.bundlePath(), metadata.ConfigDumpFile); err != nil {
return err
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index ef9f13f44..7745646b6 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -305,13 +305,40 @@ func (c *Container) getUserOverrides() *lookup.Overrides {
return &overrides
}
+func lookupHostUser(name string) (*runcuser.ExecUser, error) {
+ var execUser runcuser.ExecUser
+ // Lookup User on host
+ u, err := util.LookupUser(name)
+ if err != nil {
+ return &execUser, err
+ }
+ uid, err := strconv.ParseUint(u.Uid, 8, 32)
+ if err != nil {
+ return &execUser, err
+ }
+
+ gid, err := strconv.ParseUint(u.Gid, 8, 32)
+ if err != nil {
+ return &execUser, err
+ }
+ execUser.Uid = int(uid)
+ execUser.Gid = int(gid)
+ execUser.Home = u.HomeDir
+ return &execUser, nil
+}
+
// Generate spec for a container
// Accepts a map of the container's dependencies
func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
overrides := c.getUserOverrides()
execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, c.config.User, overrides)
if err != nil {
- return nil, err
+ if util.StringInSlice(c.config.User, c.config.HostUsers) {
+ execUser, err = lookupHostUser(c.config.User)
+ }
+ if err != nil {
+ return nil, err
+ }
}
g := generate.NewFromSpec(c.config.Spec)
@@ -990,6 +1017,7 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
includeFiles := []string{
"artifacts",
+ metadata.DevShmCheckpointTar,
metadata.ConfigDumpFile,
metadata.SpecDumpFile,
metadata.NetworkStatusFile,
@@ -1134,11 +1162,38 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
return nil, 0, err
}
+ // Setting CheckpointLog early in case there is a failure.
+ c.state.CheckpointLog = path.Join(c.bundlePath(), "dump.log")
+ c.state.CheckpointPath = c.CheckpointPath()
+
runtimeCheckpointDuration, err := c.ociRuntime.CheckpointContainer(c, options)
if err != nil {
return nil, 0, err
}
+ // Keep the content of /dev/shm directory
+ if c.config.ShmDir != "" && c.state.BindMounts["/dev/shm"] == c.config.ShmDir {
+ shmDirTarFileFullPath := filepath.Join(c.bundlePath(), metadata.DevShmCheckpointTar)
+
+ shmDirTarFile, err := os.Create(shmDirTarFileFullPath)
+ if err != nil {
+ return nil, 0, err
+ }
+ defer shmDirTarFile.Close()
+
+ input, err := archive.TarWithOptions(c.config.ShmDir, &archive.TarOptions{
+ Compression: archive.Uncompressed,
+ IncludeSourceDir: true,
+ })
+ if err != nil {
+ return nil, 0, err
+ }
+
+ if _, err = io.Copy(shmDirTarFile, input); err != nil {
+ return nil, 0, err
+ }
+ }
+
// Save network.status. This is needed to restore the container with
// the same IP. Currently limited to one IP address in a container
// with one interface.
@@ -1169,6 +1224,9 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
if !options.KeepRunning && !options.PreCheckPoint {
c.state.State = define.ContainerStateStopped
c.state.Checkpointed = true
+ c.state.CheckpointedTime = time.Now()
+ c.state.Restored = false
+ c.state.RestoredTime = time.Time{}
// Cleanup Storage and Network
if err := c.cleanup(ctx); err != nil {
@@ -1216,6 +1274,8 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
logrus.Debugf("Unable to remove file %s", file)
}
}
+ // The file has been deleted. Do not mention it.
+ c.state.CheckpointLog = ""
}
c.state.FinishedTime = time.Now()
@@ -1293,22 +1353,9 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
return nil, 0, err
}
- // If a container is restored multiple times from an exported checkpoint with
- // the help of '--import --name', the restore will fail if during 'podman run'
- // a static container IP was set with '--ip'. The user can tell the restore
- // process to ignore the static IP with '--ignore-static-ip'
- if options.IgnoreStaticIP {
- c.config.StaticIP = nil
- }
-
- // If a container is restored multiple times from an exported checkpoint with
- // the help of '--import --name', the restore will fail if during 'podman run'
- // a static container MAC address was set with '--mac-address'. The user
- // can tell the restore process to ignore the static MAC with
- // '--ignore-static-mac'
- if options.IgnoreStaticMAC {
- c.config.StaticMAC = nil
- }
+ // Setting RestoreLog early in case there is a failure.
+ c.state.RestoreLog = path.Join(c.bundlePath(), "restore.log")
+ c.state.CheckpointPath = c.CheckpointPath()
// Read network configuration from checkpoint
var netStatus map[string]types.StatusBlock
@@ -1325,19 +1372,19 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
if err == nil && options.Name == "" && (!options.IgnoreStaticIP || !options.IgnoreStaticMAC) {
// The file with the network.status does exist. Let's restore the
// container with the same networks settings as during checkpointing.
- aliases, err := c.GetAllNetworkAliases()
+ networkOpts, err := c.networks()
if err != nil {
return nil, 0, err
}
+
netOpts := make(map[string]types.PerNetworkOptions, len(netStatus))
- for network, status := range netStatus {
- perNetOpts := types.PerNetworkOptions{}
- for name, netInt := range status.Interfaces {
- perNetOpts = types.PerNetworkOptions{
- InterfaceName: name,
- Aliases: aliases[network],
- }
- if !options.IgnoreStaticMAC {
+ for network, perNetOpts := range networkOpts {
+ // unset mac and ips before we start adding the ones from the status
+ perNetOpts.StaticMAC = nil
+ perNetOpts.StaticIPs = nil
+ for name, netInt := range netStatus[network].Interfaces {
+ perNetOpts.InterfaceName = name
+ if !options.IgnoreStaticIP {
perNetOpts.StaticMAC = netInt.MacAddress
}
if !options.IgnoreStaticIP {
@@ -1349,13 +1396,6 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
// For now just use the first interface to get the ips this should be good enough for most cases.
break
}
- if perNetOpts.InterfaceName == "" {
- eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(network)
- if !exists {
- return nil, 0, errors.Errorf("no network interface name for container %s on network %s", c.config.ID, network)
- }
- perNetOpts.InterfaceName = eth
- }
netOpts[network] = perNetOpts
}
c.perNetworkOpts = netOpts
@@ -1497,6 +1537,24 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
}
}
+ // Restore /dev/shm content
+ if c.config.ShmDir != "" && c.state.BindMounts["/dev/shm"] == c.config.ShmDir {
+ shmDirTarFileFullPath := filepath.Join(c.bundlePath(), metadata.DevShmCheckpointTar)
+ if _, err := os.Stat(shmDirTarFileFullPath); err != nil {
+ logrus.Debug("Container checkpoint doesn't contain dev/shm: ", err.Error())
+ } else {
+ shmDirTarFile, err := os.Open(shmDirTarFileFullPath)
+ if err != nil {
+ return nil, 0, err
+ }
+ defer shmDirTarFile.Close()
+
+ if err := archive.UntarUncompressed(shmDirTarFile, c.config.ShmDir, nil); err != nil {
+ return nil, 0, err
+ }
+ }
+ }
+
// Cleanup for a working restore.
if err := c.removeConmonFiles(); err != nil {
return nil, 0, err
@@ -1583,6 +1641,9 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
c.state.State = define.ContainerStateRunning
c.state.Checkpointed = false
+ c.state.Restored = true
+ c.state.CheckpointedTime = time.Time{}
+ c.state.RestoredTime = time.Now()
if !options.Keep {
// Delete all checkpoint related files. At this point, in theory, all files
@@ -1593,6 +1654,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
if err != nil {
logrus.Debugf("Non-fatal: removal of checkpoint directory (%s) failed: %v", c.CheckpointPath(), err)
}
+ c.state.CheckpointPath = ""
err = os.RemoveAll(c.PreCheckPointPath())
if err != nil {
logrus.Debugf("Non-fatal: removal of pre-checkpoint directory (%s) failed: %v", c.PreCheckPointPath(), err)
@@ -1613,6 +1675,8 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
logrus.Debugf("Non-fatal: removal of checkpoint file (%s) failed: %v", file, err)
}
}
+ c.state.CheckpointLog = ""
+ c.state.RestoreLog = ""
}
return criuStatistics, runtimeRestoreDuration, c.save()
@@ -1740,11 +1804,9 @@ func (c *Container) makeBindMounts() error {
}
if !c.config.UseImageHosts {
- newHosts, err := c.generateHosts("/etc/hosts")
- if err != nil {
+ if err := c.updateHosts("/etc/hosts"); err != nil {
return errors.Wrapf(err, "error creating hosts file for container %s", c.ID())
}
- c.state.BindMounts["/etc/hosts"] = newHosts
}
}
@@ -1761,32 +1823,32 @@ func (c *Container) makeBindMounts() error {
}
} else {
if !c.config.UseImageHosts && c.state.BindMounts["/etc/hosts"] == "" {
- newHosts, err := c.generateHosts("/etc/hosts")
- if err != nil {
+ if err := c.updateHosts("/etc/hosts"); err != nil {
return errors.Wrapf(err, "error creating hosts file for container %s", c.ID())
}
- c.state.BindMounts["/etc/hosts"] = newHosts
}
}
// SHM is always added when we mount the container
c.state.BindMounts["/dev/shm"] = c.config.ShmDir
- newPasswd, newGroup, err := c.generatePasswdAndGroup()
- if err != nil {
- return errors.Wrapf(err, "error creating temporary passwd file for container %s", c.ID())
- }
- if newPasswd != "" {
- // Make /etc/passwd
- // If it already exists, delete so we can recreate
- delete(c.state.BindMounts, "/etc/passwd")
- c.state.BindMounts["/etc/passwd"] = newPasswd
- }
- if newGroup != "" {
- // Make /etc/group
- // If it already exists, delete so we can recreate
- delete(c.state.BindMounts, "/etc/group")
- c.state.BindMounts["/etc/group"] = newGroup
+ if c.config.Passwd == nil || *c.config.Passwd {
+ newPasswd, newGroup, err := c.generatePasswdAndGroup()
+ if err != nil {
+ return errors.Wrapf(err, "error creating temporary passwd file for container %s", c.ID())
+ }
+ if newPasswd != "" {
+ // Make /etc/passwd
+ // If it already exists, delete so we can recreate
+ delete(c.state.BindMounts, "/etc/passwd")
+ c.state.BindMounts["/etc/passwd"] = newPasswd
+ }
+ if newGroup != "" {
+ // Make /etc/group
+ // If it already exists, delete so we can recreate
+ delete(c.state.BindMounts, "/etc/group")
+ c.state.BindMounts["/etc/group"] = newGroup
+ }
}
// Make /etc/hostname
@@ -2053,18 +2115,29 @@ func (c *Container) generateResolvConf() (string, error) {
return destPath, nil
}
-// generateHosts creates a containers hosts file
-func (c *Container) generateHosts(path string) (string, error) {
+// updateHosts updates the container's hosts file
+func (c *Container) updateHosts(path string) error {
+ var hosts string
+
orig, err := ioutil.ReadFile(path)
if err != nil {
- return "", err
+ // Ignore if the path does not exist
+ if !os.IsNotExist(err) {
+ return err
+ }
+ } else {
+ hosts = string(orig)
}
- hosts := string(orig)
- hosts += c.getHosts()
+ hosts += c.getHosts()
hosts = c.appendLocalhost(hosts)
- return c.writeStringToRundir("hosts", hosts)
+ newHosts, err := c.writeStringToRundir("hosts", hosts)
+ if err != nil {
+ return err
+ }
+ c.state.BindMounts["/etc/hosts"] = newHosts
+ return nil
}
// based on networking mode we may want to append the localhost
@@ -2159,11 +2232,24 @@ func (c *Container) getHosts() string {
}
}
} else if c.config.NetMode.IsSlirp4netns() {
- gatewayIP, err := GetSlirp4netnsGateway(c.slirp4netnsSubnet)
- if err != nil {
- logrus.Warn("Failed to determine gatewayIP: ", err.Error())
- } else {
- hosts += fmt.Sprintf("%s host.containers.internal\n", gatewayIP.String())
+ // getLocalIP returns the non loopback local IP of the host
+ getLocalIP := func() string {
+ addrs, err := net.InterfaceAddrs()
+ if err != nil {
+ return ""
+ }
+ for _, address := range addrs {
+ // check the address type and if it is not a loopback the display it
+ if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
+ if ipnet.IP.To4() != nil {
+ return ipnet.IP.String()
+ }
+ }
+ }
+ return ""
+ }
+ if ip := getLocalIP(); ip != "" {
+ hosts += fmt.Sprintf("%s\t%s\n", ip, "host.containers.internal")
}
} else {
logrus.Debug("Network configuration does not support host.containers.internal address")
@@ -2289,12 +2375,25 @@ func (c *Container) generateUserGroupEntry(addedGID int) (string, int, error) {
// /etc/passwd via AddCurrentUserPasswdEntry (though this does not trigger if
// the user in question already exists in /etc/passwd) or the UID to be added
// is 0).
+// 3. The user specified additional host user accounts to add the the /etc/passwd file
// Returns password entry (as a string that can be appended to /etc/passwd) and
// any error that occurred.
func (c *Container) generatePasswdEntry() (string, error) {
passwdString := ""
addedUID := 0
+ for _, userid := range c.config.HostUsers {
+ // Lookup User on host
+ u, err := util.LookupUser(userid)
+ if err != nil {
+ return "", err
+ }
+ entry, err := c.userPasswdEntry(u)
+ if err != nil {
+ return "", err
+ }
+ passwdString += entry
+ }
if c.config.AddCurrentUserPasswdEntry {
entry, uid, _, err := c.generateCurrentUserPasswdEntry()
if err != nil {
@@ -2327,17 +2426,25 @@ func (c *Container) generateCurrentUserPasswdEntry() (string, int, int, error) {
if err != nil {
return "", 0, 0, errors.Wrapf(err, "failed to get current user")
}
+ pwd, err := c.userPasswdEntry(u)
+ if err != nil {
+ return "", 0, 0, err
+ }
+ return pwd, uid, rootless.GetRootlessGID(), nil
+}
+
+func (c *Container) userPasswdEntry(u *user.User) (string, error) {
// Lookup the user to see if it exists in the container image.
- _, err = lookup.GetUser(c.state.Mountpoint, u.Username)
+ _, err := lookup.GetUser(c.state.Mountpoint, u.Username)
if err != runcuser.ErrNoPasswdEntries {
- return "", 0, 0, err
+ return "", err
}
// Lookup the UID to see if it exists in the container image.
_, err = lookup.GetUser(c.state.Mountpoint, u.Uid)
if err != runcuser.ErrNoPasswdEntries {
- return "", 0, 0, err
+ return "", err
}
// If the user's actual home directory exists, or was mounted in - use
@@ -2371,7 +2478,7 @@ func (c *Container) generateCurrentUserPasswdEntry() (string, int, int, error) {
c.config.Spec.Process.Env = append(c.config.Spec.Process.Env, fmt.Sprintf("HOME=%s", homeDir))
}
- return fmt.Sprintf("%s:*:%s:%s:%s:%s:/bin/sh\n", u.Username, u.Uid, u.Gid, u.Name, homeDir), uid, rootless.GetRootlessGID(), nil
+ return fmt.Sprintf("%s:*:%s:%s:%s:%s:/bin/sh\n", u.Username, u.Uid, u.Gid, u.Name, homeDir), nil
}
// generateUserPasswdEntry generates an /etc/passwd entry for the container user
@@ -2426,7 +2533,7 @@ func (c *Container) generateUserPasswdEntry(addedUID int) (string, int, int, err
// generatePasswdAndGroup generates container-specific passwd and group files
// iff g.config.User is a number or we are configured to make a passwd entry for
-// the current user.
+// the current user or the user specified HostsUsers
// Returns path to file to mount at /etc/passwd, path to file to mount at
// /etc/group, and any error that occurred. If no passwd/group file were
// required, the empty string will be returned for those path (this may occur
@@ -2437,7 +2544,8 @@ func (c *Container) generateUserPasswdEntry(addedUID int) (string, int, int, err
// with a bind mount). This is done in cases where the container is *not*
// read-only. In this case, the function will return nothing ("", "", nil).
func (c *Container) generatePasswdAndGroup() (string, string, error) {
- if !c.config.AddCurrentUserPasswdEntry && c.config.User == "" {
+ if !c.config.AddCurrentUserPasswdEntry && c.config.User == "" &&
+ len(c.config.HostUsers) == 0 {
return "", "", nil
}
diff --git a/libpod/container_validate.go b/libpod/container_validate.go
index 91ebe93fb..ca5ce8b2a 100644
--- a/libpod/container_validate.go
+++ b/libpod/container_validate.go
@@ -74,7 +74,7 @@ func (c *Container) validate() error {
// Cannot set static IP or MAC if joining >1 CNI network.
if len(c.config.Networks) > 1 && (c.config.StaticIP != nil || c.config.StaticMAC != nil) {
- return errors.Wrapf(define.ErrInvalidArg, "cannot set static IP or MAC address if joining more than one CNI network")
+ return errors.Wrapf(define.ErrInvalidArg, "cannot set static IP or MAC address if joining more than one network")
}
// Using image resolv.conf conflicts with various DNS settings.
@@ -115,17 +115,6 @@ func (c *Container) validate() error {
destinations[vol.Dest] = true
}
- // Check that networks and network aliases match up.
- ctrNets := make(map[string]bool)
- for _, net := range c.config.Networks {
- ctrNets[net] = true
- }
- for net := range c.config.NetworkAliases {
- if _, ok := ctrNets[net]; !ok {
- return errors.Wrapf(define.ErrNoSuchNetwork, "container tried to set network aliases for network %s but is not connected to the network", net)
- }
- }
-
// If User in the OCI spec is set, require that c.config.User is set for
// security reasons (a lot of our code relies on c.config.User).
if c.config.User == "" && (c.config.Spec.Process.User.UID != 0 || c.config.Spec.Process.User.GID != 0) {
diff --git a/libpod/define/annotations.go b/libpod/define/annotations.go
index f6b1c06ea..3964a1237 100644
--- a/libpod/define/annotations.go
+++ b/libpod/define/annotations.go
@@ -66,3 +66,15 @@ const (
// annotation.
InspectResponseFalse = "FALSE"
)
+
+// IsReservedAnnotation returns true if the specified value corresponds to an
+// already reserved annotation that Podman sets during container creation.
+func IsReservedAnnotation(value string) bool {
+ switch value {
+ case InspectAnnotationCIDFile, InspectAnnotationAutoremove, InspectAnnotationVolumesFrom, InspectAnnotationPrivileged, InspectAnnotationPublishAll, InspectAnnotationInit, InspectAnnotationLabel, InspectAnnotationSeccomp, InspectAnnotationApparmor, InspectResponseTrue, InspectResponseFalse:
+ return true
+
+ default:
+ return false
+ }
+}
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 677b39218..ba73e4196 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -68,6 +68,8 @@ type InspectContainerConfig struct {
Timeout uint `json:"Timeout"`
// StopTimeout is time before container is stopped when calling stop
StopTimeout uint `json:"StopTimeout"`
+ // Passwd determines whether or not podman can add entries to /etc/passwd and /etc/group
+ Passwd *bool `json:"Passwd,omitempty"`
}
// InspectRestartPolicy holds information about the container's restart policy.
@@ -189,22 +191,28 @@ type InspectMount struct {
// Docker, but here we see more fields that are unused (nonsensical in the
// context of Libpod).
type InspectContainerState struct {
- OciVersion string `json:"OciVersion"`
- Status string `json:"Status"`
- Running bool `json:"Running"`
- Paused bool `json:"Paused"`
- Restarting bool `json:"Restarting"` // TODO
- OOMKilled bool `json:"OOMKilled"`
- Dead bool `json:"Dead"`
- Pid int `json:"Pid"`
- ConmonPid int `json:"ConmonPid,omitempty"`
- ExitCode int32 `json:"ExitCode"`
- Error string `json:"Error"` // TODO
- StartedAt time.Time `json:"StartedAt"`
- FinishedAt time.Time `json:"FinishedAt"`
- Health HealthCheckResults `json:"Health,omitempty"`
- Checkpointed bool `json:"Checkpointed,omitempty"`
- CgroupPath string `json:"CgroupPath,omitempty"`
+ OciVersion string `json:"OciVersion"`
+ Status string `json:"Status"`
+ Running bool `json:"Running"`
+ Paused bool `json:"Paused"`
+ Restarting bool `json:"Restarting"` // TODO
+ OOMKilled bool `json:"OOMKilled"`
+ Dead bool `json:"Dead"`
+ Pid int `json:"Pid"`
+ ConmonPid int `json:"ConmonPid,omitempty"`
+ ExitCode int32 `json:"ExitCode"`
+ Error string `json:"Error"` // TODO
+ StartedAt time.Time `json:"StartedAt"`
+ FinishedAt time.Time `json:"FinishedAt"`
+ Health HealthCheckResults `json:"Health,omitempty"`
+ Checkpointed bool `json:"Checkpointed,omitempty"`
+ CgroupPath string `json:"CgroupPath,omitempty"`
+ CheckpointedAt time.Time `json:"CheckpointedAt,omitempty"`
+ RestoredAt time.Time `json:"RestoredAt,omitempty"`
+ CheckpointLog string `json:"CheckpointLog,omitempty"`
+ CheckpointPath string `json:"CheckpointPath,omitempty"`
+ RestoreLog string `json:"RestoreLog,omitempty"`
+ Restored bool `json:"Restored,omitempty"`
}
// Healthcheck returns the HealthCheckResults. This is used for old podman compat
diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go
index 97e7ffdfb..e7adc8700 100644
--- a/libpod/define/pod_inspect.go
+++ b/libpod/define/pod_inspect.go
@@ -65,6 +65,8 @@ type InspectPodData struct {
BlkioDeviceReadBps []InspectBlkioThrottleDevice `json:"device_read_bps,omitempty"`
// VolumesFrom contains the containers that the pod inherits mounts from
VolumesFrom []string `json:"volumes_from,omitempty"`
+ // SecurityOpt contains the specified security labels and related SELinux information
+ SecurityOpts []string `json:"security_opt,omitempty"`
}
// InspectPodInfraConfig contains the configuration of the pod's infra
diff --git a/libpod/healthcheck_linux.go b/libpod/healthcheck_linux.go
index e08214809..a1f3e8491 100644
--- a/libpod/healthcheck_linux.go
+++ b/libpod/healthcheck_linux.go
@@ -1,6 +1,7 @@
package libpod
import (
+ "context"
"fmt"
"os"
"os/exec"
@@ -59,9 +60,9 @@ func (c *Container) startTimer() error {
return err
}
-// removeTimer removes the systemd timer and unit files
+// removeTransientFiles removes the systemd timer and unit files
// for the container
-func (c *Container) removeTimer() error {
+func (c *Container) removeTransientFiles(ctx context.Context) error {
if c.disableHealthCheckSystemd() {
return nil
}
@@ -71,12 +72,29 @@ func (c *Container) removeTimer() error {
}
defer conn.Close()
timerFile := fmt.Sprintf("%s.timer", c.ID())
- _, err = conn.StopUnit(timerFile, "fail", nil)
+ serviceFile := fmt.Sprintf("%s.service", c.ID())
- // We want to ignore errors where the timer unit has already been removed. The error
- // return is generic so we have to check against the string in the error
- if err != nil && strings.HasSuffix(err.Error(), ".timer not loaded.") {
- return nil
+ // If the service has failed (the healthcheck has failed), then
+ // the .service file is not removed on stopping the unit file. If
+ // we check the properties of the service, it will automatically
+ // reset the state. But checking the state takes msecs vs usecs to
+ // blindly call reset.
+ if err := conn.ResetFailedUnitContext(ctx, serviceFile); err != nil {
+ logrus.Debugf("failed to reset unit file: %q", err)
+ }
+
+ // We want to ignore errors where the timer unit and/or service unit has already
+ // been removed. The error return is generic so we have to check against the
+ // string in the error
+ if _, err = conn.StopUnitContext(ctx, serviceFile, "fail", nil); err != nil {
+ if !strings.HasSuffix(err.Error(), ".service not loaded.") {
+ return errors.Wrapf(err, "unable to remove service file")
+ }
+ }
+ if _, err = conn.StopUnitContext(ctx, timerFile, "fail", nil); err != nil {
+ if strings.HasSuffix(err.Error(), ".timer not loaded.") {
+ return nil
+ }
}
return err
}
diff --git a/libpod/kube.go b/libpod/kube.go
index 4e61b5377..d667616d0 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -747,7 +747,7 @@ func libpodEnvVarsToKubeEnvVars(envs []string, imageEnvs []string) ([]v1.EnvVar,
defaultEnv := env.DefaultEnvVariables()
envVars := make([]v1.EnvVar, 0, len(envs))
imageMap := make(map[string]string, len(imageEnvs))
- for _, ie := range envs {
+ for _, ie := range imageEnvs {
split := strings.SplitN(ie, "=", 2)
imageMap[split[0]] = split[1]
}
diff --git a/libpod/network/internal/util/util.go b/libpod/network/internal/util/util.go
index bf9d70aba..d9b9a8dc0 100644
--- a/libpod/network/internal/util/util.go
+++ b/libpod/network/internal/util/util.go
@@ -78,7 +78,7 @@ func GetUsedSubnets(n NetUtil) ([]*net.IPNet, error) {
return append(subnets, liveSubnets...), nil
}
-// GetFreeIPv6NetworkSubnet returns a unused ipv4 subnet
+// GetFreeIPv4NetworkSubnet returns a unused ipv4 subnet
func GetFreeIPv4NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
// the default podman network is 10.88.0.0/16
// start locking for free /24 networks
diff --git a/libpod/network/types/network.go b/libpod/network/types/network.go
index 105641e70..37fa11461 100644
--- a/libpod/network/types/network.go
+++ b/libpod/network/types/network.go
@@ -204,7 +204,8 @@ type PerNetworkOptions struct {
Aliases []string `json:"aliases,omitempty"`
// StaticMac for this container. Optional.
StaticMAC HardwareAddr `json:"static_mac,omitempty"`
- // InterfaceName for this container. Required.
+ // InterfaceName for this container. Required in the backend.
+ // Optional in the frontend. Will be filled with ethX (where X is a integer) when empty.
InterfaceName string `json:"interface_name"`
}
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 5fb97dd73..a931774f8 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -53,41 +53,6 @@ const (
persistentCNIDir = "/var/lib/cni"
)
-// GetAllNetworkAliases returns all configured aliases for this container.
-// It also adds the container short ID as alias to match docker.
-func (c *Container) GetAllNetworkAliases() (map[string][]string, error) {
- allAliases, err := c.runtime.state.GetAllNetworkAliases(c)
- if err != nil {
- return nil, err
- }
-
- // get the all attached networks, we cannot use GetAllNetworkAliases()
- // since it returns nil if there are no aliases
- nets, _, err := c.networks()
- if err != nil {
- return nil, err
- }
-
- // add container short ID as alias to match docker
- for _, net := range nets {
- allAliases[net] = append(allAliases[net], c.config.ID[:12])
- }
- return allAliases, nil
-}
-
-// GetNetworkAliases returns configured aliases for this network.
-// It also adds the container short ID as alias to match docker.
-func (c *Container) GetNetworkAliases(netName string) ([]string, error) {
- aliases, err := c.runtime.state.GetNetworkAliases(c, netName)
- if err != nil {
- return nil, err
- }
-
- // add container short ID as alias to match docker
- aliases = append(aliases, c.config.ID[:12])
- return aliases, nil
-}
-
// convertPortMappings will remove the HostIP part from the ports when running inside podman machine.
// This is need because a HostIP of 127.0.0.1 would now allow the gvproxy forwarder to reach to open ports.
// For machine the HostIP must only be used by gvproxy and never in the VM.
@@ -104,53 +69,20 @@ func (c *Container) convertPortMappings() []types.PortMapping {
return newPorts
}
-func (c *Container) getNetworkOptions() (types.NetworkOptions, error) {
+func (c *Container) getNetworkOptions(networkOpts map[string]types.PerNetworkOptions) (types.NetworkOptions, error) {
opts := types.NetworkOptions{
ContainerID: c.config.ID,
ContainerName: getCNIPodName(c),
}
opts.PortMappings = c.convertPortMappings()
- networks, _, err := c.networks()
- if err != nil {
- return opts, err
- }
- aliases, err := c.GetAllNetworkAliases()
- if err != nil {
- return opts, err
- }
// If the container requested special network options use this instead of the config.
// This is the case for container restore or network reload.
if c.perNetworkOpts != nil {
opts.Networks = c.perNetworkOpts
- return opts, nil
- }
-
- // Update container map of interface descriptions
- if err := c.setupNetworkDescriptions(networks); err != nil {
- return opts, err
- }
-
- nets := make(map[string]types.PerNetworkOptions, len(networks))
- for i, network := range networks {
- eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(network)
- if !exists {
- return opts, errors.Errorf("no network interface name for container %s on network %s", c.config.ID, network)
- }
- netOpts := types.PerNetworkOptions{
- InterfaceName: eth,
- Aliases: aliases[network],
- }
- // only set the static ip/mac on the first network
- if i == 0 {
- if c.config.StaticIP != nil {
- netOpts.StaticIPs = []net.IP{c.config.StaticIP}
- }
- netOpts.StaticMAC = c.config.StaticMAC
- }
- nets[network] = netOpts
+ } else {
+ opts.Networks = networkOpts
}
- opts.Networks = nets
return opts, nil
}
@@ -697,7 +629,7 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (status map[str
if ctr.config.NetMode.IsSlirp4netns() {
return nil, r.setupSlirp4netns(ctr, ctrNS)
}
- networks, _, err := ctr.networks()
+ networks, err := ctr.networks()
if err != nil {
return nil, err
}
@@ -707,7 +639,7 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (status map[str
return nil, nil
}
- netOpts, err := ctr.getNetworkOptions()
+ netOpts, err := ctr.getNetworkOptions(networks)
if err != nil {
return nil, err
}
@@ -862,13 +794,13 @@ func (r *Runtime) teardownCNI(ctr *Container) error {
logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID())
- networks, _, err := ctr.networks()
+ networks, err := ctr.networks()
if err != nil {
return err
}
if !ctr.config.NetMode.IsSlirp4netns() && len(networks) > 0 {
- netOpts, err := ctr.getNetworkOptions()
+ netOpts, err := ctr.getNetworkOptions(networks)
if err != nil {
return err
}
@@ -960,22 +892,17 @@ func (r *Runtime) reloadContainerNetwork(ctr *Container) (map[string]types.Statu
}
}
- aliases, err := ctr.GetAllNetworkAliases()
+ networkOpts, err := ctr.networks()
if err != nil {
return nil, err
}
// Set the same network settings as before..
netStatus := ctr.getNetworkStatus()
- netOpts := make(map[string]types.PerNetworkOptions, len(netStatus))
- for network, status := range netStatus {
- perNetOpts := types.PerNetworkOptions{}
- for name, netInt := range status.Interfaces {
- perNetOpts = types.PerNetworkOptions{
- InterfaceName: name,
- Aliases: aliases[network],
- StaticMAC: netInt.MacAddress,
- }
+ for network, perNetOpts := range networkOpts {
+ for name, netInt := range netStatus[network].Interfaces {
+ perNetOpts.InterfaceName = name
+ perNetOpts.StaticMAC = netInt.MacAddress
for _, netAddress := range netInt.Subnets {
perNetOpts.StaticIPs = append(perNetOpts.StaticIPs, netAddress.IPNet.IP)
}
@@ -983,16 +910,9 @@ func (r *Runtime) reloadContainerNetwork(ctr *Container) (map[string]types.Statu
// For now just use the first interface to get the ips this should be good enough for most cases.
break
}
- if perNetOpts.InterfaceName == "" {
- eth, exists := ctr.state.NetInterfaceDescriptions.getInterfaceByName(network)
- if !exists {
- return nil, errors.Errorf("no network interface name for container %s on network %s", ctr.config.ID, network)
- }
- perNetOpts.InterfaceName = eth
- }
- netOpts[network] = perNetOpts
+ networkOpts[network] = perNetOpts
}
- ctr.perNetworkOpts = netOpts
+ ctr.perNetworkOpts = networkOpts
return r.configureNetNS(ctr, ctr.state.NetNS)
}
@@ -1049,7 +969,7 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
settings := new(define.InspectNetworkSettings)
settings.Ports = makeInspectPortBindings(c.config.PortMappings, c.config.ExposedPorts)
- networks, isDefault, err := c.networks()
+ networks, err := c.networks()
if err != nil {
return nil, err
}
@@ -1060,14 +980,10 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
// the container joined.
if len(networks) > 0 {
settings.Networks = make(map[string]*define.InspectAdditionalNetwork, len(networks))
- for _, net := range networks {
+ for net, opts := range networks {
cniNet := new(define.InspectAdditionalNetwork)
cniNet.NetworkID = net
- aliases, err := c.GetNetworkAliases(net)
- if err != nil {
- return nil, err
- }
- cniNet.Aliases = aliases
+ cniNet.Aliases = opts.Aliases
settings.Networks[net] = cniNet
}
}
@@ -1092,7 +1008,7 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
settings.Networks = make(map[string]*define.InspectAdditionalNetwork)
- for _, name := range networks {
+ for name, opts := range networks {
result := netStatus[name]
addedNet := new(define.InspectAdditionalNetwork)
addedNet.NetworkID = name
@@ -1101,19 +1017,17 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
if err != nil {
return nil, err
}
-
- aliases, err := c.GetNetworkAliases(name)
- if err != nil {
- return nil, err
- }
- addedNet.Aliases = aliases
+ addedNet.Aliases = opts.Aliases
addedNet.InspectBasicNetworkConfig = basicConfig
settings.Networks[name] = addedNet
}
- if !isDefault {
+ // if not only the default network is connected we can return here
+ // otherwise we have to populate the InspectBasicNetworkConfig settings
+ _, isDefaultNet := networks[c.runtime.config.Network.DefaultNetwork]
+ if !(len(networks) == 1 && isDefaultNet) {
return settings, nil
}
}
@@ -1135,29 +1049,6 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
return settings, nil
}
-// setupNetworkDescriptions adds networks and eth values to the container's
-// network descriptions
-func (c *Container) setupNetworkDescriptions(networks []string) error {
- // if the map is nil and we have networks
- if c.state.NetInterfaceDescriptions == nil && len(networks) > 0 {
- c.state.NetInterfaceDescriptions = make(ContainerNetworkDescriptions)
- }
- origLen := len(c.state.NetInterfaceDescriptions)
- for _, n := range networks {
- // if the network is not in the map, add it
- if _, exists := c.state.NetInterfaceDescriptions[n]; !exists {
- c.state.NetInterfaceDescriptions.add(n)
- }
- }
- // if the map changed, we need to save the container state
- if origLen != len(c.state.NetInterfaceDescriptions) {
- if err := c.save(); err != nil {
- return err
- }
- }
- return nil
-}
-
// resultToBasicNetworkConfig produces an InspectBasicNetworkConfig from a CNI
// result
func resultToBasicNetworkConfig(result types.StatusBlock) (define.InspectBasicNetworkConfig, error) {
@@ -1213,7 +1104,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
c.lock.Lock()
defer c.lock.Unlock()
- networks, err := c.networksByNameIndex()
+ networks, err := c.networks()
if err != nil {
return err
}
@@ -1254,14 +1145,8 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
ContainerName: getCNIPodName(c),
}
opts.PortMappings = c.convertPortMappings()
- eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(netName)
- if !exists {
- return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, netName)
- }
opts.Networks = map[string]types.PerNetworkOptions{
- netName: {
- InterfaceName: eth,
- },
+ netName: networks[netName],
}
if err := c.runtime.teardownNetwork(c.state.NetNS.Path(), opts); err != nil {
@@ -1285,7 +1170,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
}
// ConnectNetwork connects a container to a given network
-func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) error {
+func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNetworkOptions) error {
// only the bridge mode supports cni networks
if err := isBridgeNetMode(c.config.NetMode); err != nil {
return err
@@ -1294,7 +1179,7 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e
c.lock.Lock()
defer c.lock.Unlock()
- networks, err := c.networksByNameIndex()
+ networks, err := c.networks()
if err != nil {
return err
}
@@ -1317,11 +1202,20 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e
if err != nil {
return err
}
- if !network.DNSEnabled && len(aliases) > 0 {
+ if !network.DNSEnabled && len(netOpts.Aliases) > 0 {
return errors.Wrapf(define.ErrInvalidArg, "cannot set network aliases for network %q because dns is disabled", netName)
}
+ // always add the short id as alias for docker compat
+ netOpts.Aliases = append(netOpts.Aliases, c.config.ID[:12])
- if err := c.runtime.state.NetworkConnect(c, netName, aliases); err != nil {
+ if netOpts.InterfaceName == "" {
+ netOpts.InterfaceName = getFreeInterfaceName(networks)
+ if netOpts.InterfaceName == "" {
+ return errors.New("could not find free network interface name")
+ }
+ }
+
+ if err := c.runtime.state.NetworkConnect(c, netName, netOpts); err != nil {
return err
}
c.newNetworkEvent(events.NetworkConnect, netName)
@@ -1332,30 +1226,13 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e
return errors.Wrapf(define.ErrNoNetwork, "unable to connect %s to %s", nameOrID, netName)
}
- ctrNetworks, _, err := c.networks()
- if err != nil {
- return err
- }
- // Update network descriptions
- if err := c.setupNetworkDescriptions(ctrNetworks); err != nil {
- return err
- }
-
opts := types.NetworkOptions{
ContainerID: c.config.ID,
ContainerName: getCNIPodName(c),
}
opts.PortMappings = c.convertPortMappings()
- eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(netName)
- if !exists {
- return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, netName)
- }
- aliases = append(aliases, c.config.ID[:12])
opts.Networks = map[string]types.PerNetworkOptions{
- netName: {
- Aliases: aliases,
- InterfaceName: eth,
- },
+ netName: netOpts,
}
results, err := c.runtime.setUpNetwork(c.state.NetNS.Path(), opts)
@@ -1385,6 +1262,22 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e
return nil
}
+// get a free interface name for a new network
+// return an empty string if no free name was found
+func getFreeInterfaceName(networks map[string]types.PerNetworkOptions) string {
+ ifNames := make([]string, 0, len(networks))
+ for _, opts := range networks {
+ ifNames = append(ifNames, opts.InterfaceName)
+ }
+ for i := 0; i < 100000; i++ {
+ ifName := fmt.Sprintf("eth%d", i)
+ if !util.StringInSlice(ifName, ifNames) {
+ return ifName
+ }
+ }
+ return ""
+}
+
// DisconnectContainerFromNetwork removes a container from its CNI network
func (r *Runtime) DisconnectContainerFromNetwork(nameOrID, netName string, force bool) error {
ctr, err := r.LookupContainer(nameOrID)
@@ -1395,12 +1288,12 @@ func (r *Runtime) DisconnectContainerFromNetwork(nameOrID, netName string, force
}
// ConnectContainerToNetwork connects a container to a CNI network
-func (r *Runtime) ConnectContainerToNetwork(nameOrID, netName string, aliases []string) error {
+func (r *Runtime) ConnectContainerToNetwork(nameOrID, netName string, netOpts types.PerNetworkOptions) error {
ctr, err := r.LookupContainer(nameOrID)
if err != nil {
return err
}
- return ctr.NetworkConnect(nameOrID, netName, aliases)
+ return ctr.NetworkConnect(nameOrID, netName, netOpts)
}
// normalizeNetworkName takes a network name, a partial or a full network ID and returns the network name.
diff --git a/libpod/options.go b/libpod/options.go
index 8f2d5cb15..6edb9972b 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1058,7 +1058,7 @@ func WithDependencyCtrs(ctrs []*Container) CtrCreateOption {
// namespace with a minimal configuration.
// An optional array of port mappings can be provided.
// Conflicts with WithNetNSFrom().
-func WithNetNS(portMappings []nettypes.PortMapping, exposedPorts map[uint16][]string, postConfigureNetNS bool, netmode string, networks []string) CtrCreateOption {
+func WithNetNS(portMappings []nettypes.PortMapping, exposedPorts map[uint16][]string, postConfigureNetNS bool, netmode string, networks map[string]nettypes.PerNetworkOptions) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
@@ -1070,24 +1070,10 @@ func WithNetNS(portMappings []nettypes.PortMapping, exposedPorts map[uint16][]st
ctr.config.PortMappings = portMappings
ctr.config.ExposedPorts = exposedPorts
- ctr.config.Networks = networks
-
- return nil
- }
-}
-
-// WithStaticIP indicates that the container should request a static IP from
-// the CNI plugins.
-// It cannot be set unless WithNetNS has already been passed.
-// Further, it cannot be set if additional CNI networks to join have been
-// specified.
-func WithStaticIP(ip net.IP) CtrCreateOption {
- return func(ctr *Container) error {
- if ctr.valid {
- return define.ErrCtrFinalized
+ if !ctr.config.NetMode.IsBridge() && len(networks) > 0 {
+ return errors.New("cannot use networks when network mode is not bridge")
}
-
- ctr.config.StaticIP = ip
+ ctr.config.Networks = networks
return nil
}
@@ -1106,23 +1092,6 @@ func WithNetworkOptions(options map[string][]string) CtrCreateOption {
}
}
-// WithStaticMAC indicates that the container should request a static MAC from
-// the CNI plugins.
-// It cannot be set unless WithNetNS has already been passed.
-// Further, it cannot be set if additional CNI networks to join have been
-// specified.
-func WithStaticMAC(mac nettypes.HardwareAddr) CtrCreateOption {
- return func(ctr *Container) error {
- if ctr.valid {
- return define.ErrCtrFinalized
- }
-
- ctr.config.StaticMAC = mac
-
- return nil
- }
-}
-
// WithLogDriver sets the log driver for the container
func WithLogDriver(driver string) CtrCreateOption {
return func(ctr *Container) error {
@@ -1572,20 +1541,6 @@ func WithCreateWorkingDir() CtrCreateOption {
}
}
-// WithNetworkAliases sets network aliases for the container.
-// Accepts a map of network name to aliases.
-func WithNetworkAliases(aliases map[string][]string) CtrCreateOption {
- return func(ctr *Container) error {
- if ctr.valid {
- return define.ErrCtrFinalized
- }
-
- ctr.config.NetworkAliases = aliases
-
- return nil
- }
-}
-
// Volume Creation Options
// WithVolumeName sets the name of the volume.
@@ -1813,6 +1768,17 @@ func WithPidFile(pidFile string) CtrCreateOption {
}
}
+// WithHostUsers indicates host users to add to /etc/passwd
+func WithHostUsers(hostUsers []string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+ ctr.config.HostUsers = hostUsers
+ return nil
+ }
+}
+
// WithInitCtrType indicates the container is a initcontainer
func WithInitCtrType(containerType string) CtrCreateOption {
return func(ctr *Container) error {
@@ -1839,6 +1805,36 @@ func WithHostDevice(dev []specs.LinuxDevice) CtrCreateOption {
}
}
+// WithSelectedPasswordManagement makes it so that the container either does or does not setup /etc/passwd or /etc/group
+func WithSelectedPasswordManagement(passwd *bool) CtrCreateOption {
+ return func(c *Container) error {
+ if c.valid {
+ return define.ErrCtrFinalized
+ }
+ c.config.Passwd = passwd
+ return nil
+ }
+}
+
+// WithInfraConfig allows for inheritance of compatible config entities from the infra container
+func WithInfraConfig(compatibleOptions InfraInherit) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+ compatMarshal, err := json.Marshal(compatibleOptions)
+ if err != nil {
+ return errors.New("Could not marshal compatible options")
+ }
+
+ err = json.Unmarshal(compatMarshal, ctr.config)
+ if err != nil {
+ return errors.New("Could not unmarshal compatible options into contrainer config")
+ }
+ return nil
+ }
+}
+
// Pod Creation Options
// WithPodCreateCommand adds the full command plus arguments of the current
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index 80ecb690a..526e0c28b 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -586,6 +586,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
var inspectMounts []define.InspectMount
var devices []define.InspectDevice
var deviceLimits []define.InspectBlkioThrottleDevice
+ var infraSecurity []string
if p.state.InfraContainerID != "" {
infra, err := p.runtime.GetContainer(p.state.InfraContainerID)
if err != nil {
@@ -603,6 +604,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
infraConfig.UserNS = p.UserNSMode()
namedVolumes, mounts := infra.sortUserVolumes(infra.config.Spec)
inspectMounts, err = infra.GetInspectMounts(namedVolumes, infra.config.ImageVolumes, mounts)
+ infraSecurity = infra.GetSecurityOptions()
if err != nil {
return nil, err
}
@@ -637,9 +639,17 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
infraConfig.HostAdd = make([]string, 0, len(infra.config.HostAdd))
infraConfig.HostAdd = append(infraConfig.HostAdd, infra.config.HostAdd...)
}
- if len(infra.config.ContainerNetworkConfig.Networks) > 0 {
- infraConfig.Networks = make([]string, 0, len(infra.config.ContainerNetworkConfig.Networks))
- infraConfig.Networks = append(infraConfig.Networks, infra.config.ContainerNetworkConfig.Networks...)
+
+ networks, err := infra.networks()
+ if err != nil {
+ return nil, err
+ }
+ netNames := make([]string, 0, len(networks))
+ for name := range networks {
+ netNames = append(netNames, name)
+ }
+ if len(netNames) > 0 {
+ infraConfig.Networks = netNames
}
infraConfig.NetworkOptions = infra.config.ContainerNetworkConfig.NetworkOptions
infraConfig.PortBindings = makeInspectPortBindings(infra.config.ContainerNetworkConfig.PortMappings, nil)
@@ -670,6 +680,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
Devices: devices,
BlkioDeviceReadBps: deviceLimits,
VolumesFrom: p.VolumesFrom(),
+ SecurityOpts: infraSecurity,
}
return &inspectData, nil
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 6c46eb747..59a1fd153 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -2,6 +2,7 @@ package libpod
import (
"context"
+ "fmt"
"os"
"path"
"path/filepath"
@@ -13,10 +14,12 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/events"
+ "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/libpod/shutdown"
"github.com/containers/podman/v3/pkg/domain/entities/reports"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/specgen"
+ "github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage"
"github.com/containers/storage/pkg/stringid"
"github.com/docker/go-units"
@@ -230,39 +233,56 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Container, retErr error) {
// normalize the networks to names
- // ocicni only knows about cni names so we have to make
+ // the db backend only knows about network names so we have to make
// sure we do not use ids internally
if len(ctr.config.Networks) > 0 {
- netNames := make([]string, 0, len(ctr.config.Networks))
- for _, nameOrID := range ctr.config.Networks {
- netName, err := r.normalizeNetworkName(nameOrID)
- if err != nil {
- return nil, err
+ normalizeNetworks := make(map[string]types.PerNetworkOptions, len(ctr.config.Networks))
+ // first get the already used interface names so we do not conflict
+ usedIfNames := make([]string, 0, len(ctr.config.Networks))
+ for _, opts := range ctr.config.Networks {
+ if opts.InterfaceName != "" {
+ // check that no name is assigned to more than network
+ if util.StringInSlice(opts.InterfaceName, usedIfNames) {
+ return nil, errors.Errorf("network interface name %q is already assigned to another network", opts.InterfaceName)
+ }
+ usedIfNames = append(usedIfNames, opts.InterfaceName)
}
- netNames = append(netNames, netName)
}
- ctr.config.Networks = netNames
- }
-
- // https://github.com/containers/podman/issues/11285
- // normalize the networks aliases to use network names and never ids
- if len(ctr.config.NetworkAliases) > 0 {
- netAliases := make(map[string][]string, len(ctr.config.NetworkAliases))
- for nameOrID, aliases := range ctr.config.NetworkAliases {
+ i := 0
+ for nameOrID, opts := range ctr.config.Networks {
netName, err := r.normalizeNetworkName(nameOrID)
if err != nil {
return nil, err
}
- network, err := r.network.NetworkInspect(netName)
- if err != nil {
- return nil, err
+ if len(opts.Aliases) > 0 {
+ network, err := r.network.NetworkInspect(netName)
+ if err != nil {
+ return nil, err
+ }
+ if !network.DNSEnabled {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "cannot set network aliases for network %q because dns is disabled", netName)
+ }
}
- if !network.DNSEnabled {
- return nil, errors.Wrapf(define.ErrInvalidArg, "cannot set network aliases for network %q because dns is disabled", netName)
+ // assign interface name if empty
+ if opts.InterfaceName == "" {
+ for i < 100000 {
+ ifName := fmt.Sprintf("eth%d", i)
+ if !util.StringInSlice(ifName, usedIfNames) {
+ opts.InterfaceName = ifName
+ usedIfNames = append(usedIfNames, ifName)
+ break
+ }
+ i++
+ }
+ // if still empty we did not find a free name
+ if opts.InterfaceName == "" {
+ return nil, errors.New("failed to find free network interface name")
+ }
}
- netAliases[netName] = aliases
+
+ normalizeNetworks[netName] = opts
}
- ctr.config.NetworkAliases = netAliases
+ ctr.config.Networks = normalizeNetworks
}
// Validate the container
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index 52ac0d4d7..bf0fc4585 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -36,10 +36,21 @@ func (r *Runtime) RemoveContainersForImageCallback(ctx context.Context) libimage
return err
}
for _, ctr := range ctrs {
- if ctr.config.RootfsImageID == imageID {
- var timeout *uint
+ if ctr.config.RootfsImageID != imageID {
+ continue
+ }
+ var timeout *uint
+ if ctr.config.IsInfra {
+ pod, err := r.state.Pod(ctr.config.Pod)
+ if err != nil {
+ return errors.Wrapf(err, "container %s is in pod %s, but pod cannot be retrieved", ctr.ID(), pod.ID())
+ }
+ if err := r.removePod(ctx, pod, true, true, timeout); err != nil {
+ return errors.Wrapf(err, "removing image %s: container %s using image could not be removed", imageID, ctr.ID())
+ }
+ } else {
if err := r.removeContainer(ctx, ctr, true, false, false, timeout); err != nil {
- return errors.Wrapf(err, "error removing image %s: container %s using image could not be removed", imageID, ctr.ID())
+ return errors.Wrapf(err, "removing image %s: container %s using image could not be removed", imageID, ctr.ID())
}
}
}
diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go
index ed3cc971c..d4d9a4438 100644
--- a/libpod/runtime_volume_linux.go
+++ b/libpod/runtime_volume_linux.go
@@ -35,7 +35,7 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption)
volume := newVolume(r)
for _, option := range options {
if err := option(volume); err != nil {
- return nil, errors.Wrapf(err, "error running volume create option")
+ return nil, errors.Wrapf(err, "running volume create option")
}
}
@@ -50,7 +50,7 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption)
// Check if volume with given name exists.
exists, err := r.state.HasVolume(volume.config.Name)
if err != nil {
- return nil, errors.Wrapf(err, "error checking if volume with name %s exists", volume.config.Name)
+ return nil, errors.Wrapf(err, "checking if volume with name %s exists", volume.config.Name)
}
if exists {
return nil, errors.Wrapf(define.ErrVolumeExists, "volume with name %s already exists", volume.config.Name)
@@ -67,9 +67,15 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption)
if volume.config.Driver == define.VolumeDriverLocal {
logrus.Debugf("Validating options for local driver")
// Validate options
- for key := range volume.config.Options {
- switch key {
- case "device", "o", "type", "UID", "GID", "SIZE", "INODES":
+ for key, val := range volume.config.Options {
+ switch strings.ToLower(key) {
+ case "device":
+ if strings.ToLower(volume.config.Options["type"]) == "bind" {
+ if _, err := os.Stat(val); err != nil {
+ return nil, errors.Wrapf(err, "invalid volume option %s for driver 'local'", key)
+ }
+ }
+ case "o", "type", "uid", "gid", "size", "inodes":
// Do nothing, valid keys
default:
return nil, errors.Wrapf(define.ErrInvalidArg, "invalid mount option %s for driver 'local'", key)
@@ -92,17 +98,17 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption)
// Create the mountpoint of this volume
volPathRoot := filepath.Join(r.config.Engine.VolumePath, volume.config.Name)
if err := os.MkdirAll(volPathRoot, 0700); err != nil {
- return nil, errors.Wrapf(err, "error creating volume directory %q", volPathRoot)
+ return nil, errors.Wrapf(err, "creating volume directory %q", volPathRoot)
}
if err := os.Chown(volPathRoot, volume.config.UID, volume.config.GID); err != nil {
- return nil, errors.Wrapf(err, "error chowning volume directory %q to %d:%d", volPathRoot, volume.config.UID, volume.config.GID)
+ return nil, errors.Wrapf(err, "chowning volume directory %q to %d:%d", volPathRoot, volume.config.UID, volume.config.GID)
}
fullVolPath := filepath.Join(volPathRoot, "_data")
if err := os.MkdirAll(fullVolPath, 0755); err != nil {
- return nil, errors.Wrapf(err, "error creating volume directory %q", fullVolPath)
+ return nil, errors.Wrapf(err, "creating volume directory %q", fullVolPath)
}
if err := os.Chown(fullVolPath, volume.config.UID, volume.config.GID); err != nil {
- return nil, errors.Wrapf(err, "error chowning volume directory %q to %d:%d", fullVolPath, volume.config.UID, volume.config.GID)
+ return nil, errors.Wrapf(err, "chowning volume directory %q to %d:%d", fullVolPath, volume.config.UID, volume.config.GID)
}
if err := LabelVolumePath(fullVolPath); err != nil {
return nil, err
@@ -132,7 +138,7 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption)
lock, err := r.lockManager.AllocateLock()
if err != nil {
- return nil, errors.Wrapf(err, "error allocating lock for new volume")
+ return nil, errors.Wrapf(err, "allocating lock for new volume")
}
volume.lock = lock
volume.config.LockID = volume.lock.ID()
@@ -149,7 +155,7 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption)
// Add the volume to state
if err := r.state.AddVolume(volume); err != nil {
- return nil, errors.Wrapf(err, "error adding volume to state")
+ return nil, errors.Wrapf(err, "adding volume to state")
}
defer volume.newVolumeEvent(events.Create)
return volume, nil
@@ -181,7 +187,7 @@ func makeVolumeInPluginIfNotExist(name string, options map[string]string, plugin
createReq.Name = name
createReq.Options = options
if err := plugin.CreateVolume(createReq); err != nil {
- return errors.Wrapf(err, "error creating volume %q in plugin %s", name, plugin.Name)
+ return errors.Wrapf(err, "creating volume %q in plugin %s", name, plugin.Name)
}
}
@@ -225,13 +231,13 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool, timeo
continue
}
- return errors.Wrapf(err, "error removing container %s that depends on volume %s", dep, v.Name())
+ return errors.Wrapf(err, "removing container %s that depends on volume %s", dep, v.Name())
}
logrus.Debugf("Removing container %s (depends on volume %q)", ctr.ID(), v.Name())
if err := r.removeContainer(ctx, ctr, force, false, false, timeout); err != nil {
- return errors.Wrapf(err, "error removing container %s that depends on volume %s", ctr.ID(), v.Name())
+ return errors.Wrapf(err, "removing container %s that depends on volume %s", ctr.ID(), v.Name())
}
}
}
@@ -244,7 +250,7 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool, timeo
// them.
logrus.Errorf("Unmounting volume %s: %v", v.Name(), err)
} else {
- return errors.Wrapf(err, "error unmounting volume %s", v.Name())
+ return errors.Wrapf(err, "unmounting volume %s", v.Name())
}
}
@@ -288,13 +294,13 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool, timeo
if removalErr != nil {
logrus.Errorf("Removing volume %s from plugin %s: %v", v.Name(), v.Driver(), removalErr)
}
- return errors.Wrapf(err, "error removing volume %s", v.Name())
+ return errors.Wrapf(err, "removing volume %s", v.Name())
}
// Free the volume's lock
if err := v.lock.Free(); err != nil {
if removalErr == nil {
- removalErr = errors.Wrapf(err, "error freeing lock for volume %s", v.Name())
+ removalErr = errors.Wrapf(err, "freeing lock for volume %s", v.Name())
} else {
logrus.Errorf("Freeing lock for volume %q: %v", v.Name(), err)
}
@@ -304,7 +310,7 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool, timeo
// from /var/lib/containers/storage/volumes
if err := v.teardownStorage(); err != nil {
if removalErr == nil {
- removalErr = errors.Wrapf(err, "error cleaning up volume storage for %q", v.Name())
+ removalErr = errors.Wrapf(err, "cleaning up volume storage for %q", v.Name())
} else {
logrus.Errorf("Cleaning up volume storage for volume %q: %v", v.Name(), err)
}
diff --git a/libpod/state.go b/libpod/state.go
index 4b711bae9..21525107f 100644
--- a/libpod/state.go
+++ b/libpod/state.go
@@ -1,5 +1,7 @@
package libpod
+import "github.com/containers/podman/v3/libpod/network/types"
+
// State is a storage backend for libpod's current state.
// A State is only initialized once per instance of libpod.
// As such, initialization methods for State implementations may safely assume
@@ -99,14 +101,9 @@ type State interface {
AllContainers() ([]*Container, error)
// Get networks the container is currently connected to.
- GetNetworks(ctr *Container) ([]string, error)
- // Get network aliases for the given container in the given network.
- GetNetworkAliases(ctr *Container, network string) ([]string, error)
- // Get all network aliases for the given container.
- GetAllNetworkAliases(ctr *Container) (map[string][]string, error)
- // Add the container to the given network, adding the given aliases
- // (if present).
- NetworkConnect(ctr *Container, network string, aliases []string) error
+ GetNetworks(ctr *Container) (map[string]types.PerNetworkOptions, error)
+ // Add the container to the given network with the given options
+ NetworkConnect(ctr *Container, network string, opts types.PerNetworkOptions) error
// Remove the container from the given network, removing all aliases for
// the container in that network in the process.
NetworkDisconnect(ctr *Container, network string) error
diff --git a/libpod/state_test.go b/libpod/state_test.go
index 5c3b0d7f7..b0793127d 100644
--- a/libpod/state_test.go
+++ b/libpod/state_test.go
@@ -1299,21 +1299,9 @@ func TestAddContainerEmptyNetworkNameErrors(t *testing.T) {
testCtr, err := getTestCtr1(manager)
assert.NoError(t, err)
- testCtr.config.Networks = []string{""}
-
- err = state.AddContainer(testCtr)
- assert.Error(t, err)
- })
-}
-
-func TestAddContainerNetworkAliasesButNoMatchingNetwork(t *testing.T) {
- runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- testCtr, err := getTestCtr1(manager)
- assert.NoError(t, err)
-
- testCtr.config.Networks = []string{"test1"}
- testCtr.config.NetworkAliases = make(map[string][]string)
- testCtr.config.NetworkAliases["test2"] = []string{"alias1"}
+ testCtr.config.Networks = map[string]types.PerNetworkOptions{
+ "": {},
+ }
err = state.AddContainer(testCtr)
assert.Error(t, err)
diff --git a/libpod/volume_internal.go b/libpod/volume_internal.go
index f69f1c044..f9e1ea87d 100644
--- a/libpod/volume_internal.go
+++ b/libpod/volume_internal.go
@@ -81,7 +81,7 @@ func (v *Volume) save() error {
func (v *Volume) refresh() error {
lock, err := v.runtime.lockManager.AllocateAndRetrieveLock(v.config.LockID)
if err != nil {
- return errors.Wrapf(err, "error acquiring lock %d for volume %s", v.config.LockID, v.Name())
+ return errors.Wrapf(err, "acquiring lock %d for volume %s", v.config.LockID, v.Name())
}
v.lock = lock
diff --git a/libpod/volume_internal_linux.go b/libpod/volume_internal_linux.go
index 45cd22385..abd31df0f 100644
--- a/libpod/volume_internal_linux.go
+++ b/libpod/volume_internal_linux.go
@@ -7,7 +7,6 @@ import (
"strings"
"github.com/containers/podman/v3/libpod/define"
- "github.com/containers/podman/v3/pkg/rootless"
pluginapi "github.com/docker/go-plugins-helpers/volume"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -32,13 +31,6 @@ func (v *Volume) mount() error {
return nil
}
- // We cannot mount 'local' volumes as rootless.
- if !v.UsesVolumeDriver() && rootless.IsRootless() {
- // This check should only be applied to 'local' driver
- // so Volume Drivers must be excluded
- return errors.Wrapf(define.ErrRootless, "cannot mount volumes without root privileges")
- }
-
// Update the volume from the DB to get an accurate mount counter.
if err := v.update(); err != nil {
return err
@@ -90,22 +82,27 @@ func (v *Volume) mount() error {
// TODO: might want to cache this path in the runtime?
mountPath, err := exec.LookPath("mount")
if err != nil {
- return errors.Wrapf(err, "error locating 'mount' binary")
+ return errors.Wrapf(err, "locating 'mount' binary")
}
mountArgs := []string{}
if volOptions != "" {
mountArgs = append(mountArgs, "-o", volOptions)
}
- if volType != "" {
+ switch volType {
+ case "":
+ case "bind":
+ mountArgs = append(mountArgs, "-o", volType)
+ default:
mountArgs = append(mountArgs, "-t", volType)
}
+
mountArgs = append(mountArgs, volDevice, v.config.MountPoint)
mountCmd := exec.Command(mountPath, mountArgs...)
logrus.Debugf("Running mount command: %s %s", mountPath, strings.Join(mountArgs, " "))
if output, err := mountCmd.CombinedOutput(); err != nil {
logrus.Debugf("Mount %v failed with %v", mountCmd, err)
- return errors.Wrapf(errors.Errorf(string(output)), "error mounting volume %s", v.Name())
+ return errors.Errorf(string(output))
}
logrus.Debugf("Mounted volume %s", v.Name())
@@ -139,20 +136,6 @@ func (v *Volume) unmount(force bool) error {
return nil
}
- // We cannot unmount 'local' volumes as rootless.
- if !v.UsesVolumeDriver() && rootless.IsRootless() {
- // If force is set, just clear the counter and bail without
- // error, so we can remove volumes from the state if they are in
- // an awkward configuration.
- if force {
- logrus.Errorf("Volume %s is mounted despite being rootless - state is not sane", v.Name())
- v.state.MountCount = 0
- return v.save()
- }
-
- return errors.Wrapf(define.ErrRootless, "cannot mount or unmount volumes without root privileges")
- }
-
if !force {
v.state.MountCount--
} else {
@@ -184,7 +167,7 @@ func (v *Volume) unmount(force bool) error {
// Ignore EINVAL - the mount no longer exists.
return nil
}
- return errors.Wrapf(err, "error unmounting volume %s", v.Name())
+ return errors.Wrapf(err, "unmounting volume %s", v.Name())
}
logrus.Debugf("Unmounted volume %s", v.Name())
}