summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/boltdb_state.go295
-rw-r--r--libpod/boltdb_state_internal.go148
-rw-r--r--libpod/container.go28
-rw-r--r--libpod/container_config.go3
-rw-r--r--libpod/container_internal.go18
-rw-r--r--libpod/define/container_inspect.go2
-rw-r--r--libpod/define/errors.go9
-rw-r--r--libpod/in_memory_state.go281
-rw-r--r--libpod/network/config.go5
-rw-r--r--libpod/network/create.go138
-rw-r--r--libpod/network/create_test.go131
-rw-r--r--libpod/network/files.go1
-rw-r--r--libpod/network/netconflist.go21
-rw-r--r--libpod/network/netconflist_test.go70
-rw-r--r--libpod/networking_linux.go54
-rw-r--r--libpod/rootless_cni_linux.go22
-rw-r--r--libpod/runtime_ctr.go7
-rw-r--r--libpod/state.go18
-rw-r--r--libpod/state_test.go218
19 files changed, 608 insertions, 861 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index 0b9b353c7..be0adfe6a 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -94,7 +94,6 @@ func NewBoltState(path string, runtime *Runtime) (State, error) {
volBkt,
allVolsBkt,
execBkt,
- aliasesBkt,
runtimeConfigBkt,
}
@@ -972,6 +971,58 @@ func (s *BoltState) AllContainers() ([]*Container, error) {
return ctrs, nil
}
+// GetNetworks returns the CNI networks this container is a part of.
+func (s *BoltState) GetNetworks(ctr *Container) ([]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())
+
+ 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) {
@@ -1032,7 +1083,8 @@ func (s *BoltState) GetNetworkAliases(ctr *Container, network string) ([]string,
netAliasesBkt := ctrAliasesBkt.Bucket([]byte(network))
if netAliasesBkt == nil {
- return errors.Wrapf(define.ErrNoAliasesForNetwork, "container %s has no aliases for network %q", ctr.ID(), network)
+ // No aliases for this specific network.
+ return nil
}
return netAliasesBkt.ForEach(func(alias, v []byte) error {
@@ -1120,10 +1172,9 @@ func (s *BoltState) GetAllNetworkAliases(ctr *Container) (map[string][]string, e
return aliases, nil
}
-// SetNetworkAliases sets network aliases for the given container in the given
-// network. All existing aliases for that network (if any exist) will be removed,
-// to be replaced by the new aliases given.
-func (s *BoltState) SetNetworkAliases(ctr *Container, network string, aliases []string) error {
+// 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 {
if !s.valid {
return define.ErrDBClosed
}
@@ -1154,90 +1205,60 @@ func (s *BoltState) SetNetworkAliases(ctr *Container, network string, aliases []
return err
}
- allAliasesBucket, err := getAliasesBucket(tx)
- if err != nil {
- return err
- }
-
- netAllAliasesBucket, err := allAliasesBucket.CreateBucketIfNotExists([]byte(network))
- if err != nil {
- return errors.Wrapf(err, "error creating network aliases bucket for network %s", network)
- }
-
dbCtr := ctrBucket.Bucket(ctrID)
if dbCtr == nil {
ctr.valid = false
return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID())
}
- ctrAliasesBkt := dbCtr.Bucket(aliasesBkt)
- if ctrAliasesBkt == nil {
- return errors.Wrapf(define.ErrNoAliases, "container %s has no network aliases", 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 {
- return errors.Wrapf(define.ErrInvalidArg, "container %s is not connected to any CNI networks, so cannot add aliases", ctr.ID())
+ 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.netPlugin.GetDefaultNetworkName()}
+ }
+ // 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)
+ }
+ }
}
netConnected := ctrNetworksBkt.Get([]byte(network))
- if netConnected == nil {
- return errors.Wrapf(define.ErrInvalidArg, "container %s is not connected to CNI network %q, so cannot add aliases for this network", ctr.ID(), network)
- }
-
- namesBucket, err := getNamesBucket(tx)
- if err != nil {
- return err
+ if netConnected != nil {
+ return errors.Wrapf(define.ErrNetworkExists, "container %s is already connected to CNI network %q", ctr.ID(), network)
}
- // Check if the container already has network aliases for this network.
- netAliasesBkt := ctrAliasesBkt.Bucket([]byte(network))
- if netAliasesBkt != nil {
- // We have aliases. Have to remove them.
- forEachErr := netAliasesBkt.ForEach(func(alias, v []byte) error {
- // Relies on errors.Wrapf(nil, ...) returning
- // nil.
- return errors.Wrapf(netAllAliasesBucket.Delete(alias), "error removing alias %q from network %q when changing aliases for container %s", string(alias), network, ctr.ID())
- })
- if forEachErr != nil {
- return forEachErr
- }
+ // Add the network
+ if err := ctrNetworksBkt.Put([]byte(network), ctrID); err != nil {
+ return errors.Wrapf(err, "error adding container %s to network %s in DB", ctr.ID(), network)
}
- if netAliasesBkt == nil {
- newBkt, err := ctrAliasesBkt.CreateBucket([]byte(network))
- if err != nil {
- return errors.Wrapf(err, "could not create bucket for network aliases for network %q", network)
- }
- netAliasesBkt = newBkt
+ 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 {
- // Check if safe to use
- aliasExists := netAllAliasesBucket.Get([]byte(alias))
- if aliasExists != nil {
- return errors.Wrapf(define.ErrAliasExists, "network alias %q already exists in network %q (used by container %s)", alias, network, string(aliasExists))
- }
- nameExists := namesBucket.Get([]byte(alias))
- if nameExists != nil {
- return errors.Wrapf(define.ErrCtrExists, "a container or pod already uses the name %q, cannot add network alias for container %s", alias, ctr.ID())
- }
-
- // Add alias
- if err := netAliasesBkt.Put([]byte(alias), ctrID); err != nil {
- return errors.Wrapf(err, "error adding container %s network %q alias %q to DB", ctr.ID(), network, alias)
- }
- if err := netAllAliasesBucket.Put([]byte(alias), ctrID); err != nil {
- return errors.Wrapf(err, "error adding container %s network %q alias %q to all aliases in DB", ctr.ID(), network, alias)
+ 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
})
}
-// RemoveNetworkAliases removes network aliases of the given container in the
-// given network.
-func (s *BoltState) RemoveNetworkAliases(ctr *Container, network string) error {
+// NetworkDisconnect disconnects the container from the given network, also
+// removing any aliases in the network.
+func (s *BoltState) NetworkDisconnect(ctr *Container, network string) error {
if !s.valid {
return define.ErrDBClosed
}
@@ -1268,16 +1289,6 @@ func (s *BoltState) RemoveNetworkAliases(ctr *Container, network string) error {
return err
}
- allAliasesBucket, err := getAliasesBucket(tx)
- if err != nil {
- return err
- }
-
- netAllAliasesBucket, err := allAliasesBucket.CreateBucketIfNotExists([]byte(network))
- if err != nil {
- return errors.Wrapf(err, "error creating network aliases bucket for network %s", network)
- }
-
dbCtr := ctrBucket.Bucket(ctrID)
if dbCtr == nil {
ctr.valid = false
@@ -1291,141 +1302,27 @@ func (s *BoltState) RemoveNetworkAliases(ctr *Container, network string) error {
ctrNetworksBkt := dbCtr.Bucket(networksBkt)
if ctrNetworksBkt == nil {
- return errors.Wrapf(define.ErrInvalidArg, "container %s is not connected to any CNI networks, so cannot add aliases", ctr.ID())
+ return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not connected to any CNI networks, so cannot disconnect", ctr.ID())
}
netConnected := ctrNetworksBkt.Get([]byte(network))
if netConnected == nil {
- return errors.Wrapf(define.ErrInvalidArg, "container %s is not connected to CNI network %q, so cannot add aliases for this network", ctr.ID(), network)
- }
-
- // Check if the container already has network aliases for this network.
- netAliasesBkt := ctrAliasesBkt.Bucket([]byte(network))
- if netAliasesBkt != nil {
- // We have aliases. Remove them.
- forEachErr := netAliasesBkt.ForEach(func(alias, v []byte) error {
- // Relies on errors.Wrapf(nil, ...) returning
- // nil.
- return errors.Wrapf(netAllAliasesBucket.Delete(alias), "error removing alias %q from network %q when changing aliases for container %s", string(alias), network, ctr.ID())
- })
- if forEachErr != nil {
- return forEachErr
- }
+ return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not connected to CNI network %q", ctr.ID(), network)
}
- return nil
- })
-}
-
-// Get all network aliases for a single CNI network. Returns a map of alias to
-// container ID.
-func (s *BoltState) GetAllAliasesForNetwork(network string) (map[string]string, error) {
- if !s.valid {
- return nil, define.ErrDBClosed
- }
-
- if network == "" {
- return nil, errors.Wrapf(define.ErrInvalidArg, "network name must not be empty")
- }
-
- db, err := s.getDBCon()
- if err != nil {
- return nil, err
- }
- defer s.deferredCloseDBCon(db)
-
- aliases := make(map[string]string)
-
- err = db.View(func(tx *bolt.Tx) error {
- aliasBucket, err := getAliasesBucket(tx)
- if err != nil {
- return err
+ if err := ctrNetworksBkt.Delete([]byte(network)); err != nil {
+ return errors.Wrapf(err, "error removing container %s from network %s", ctr.ID(), network)
}
- dbAlias := aliasBucket.Bucket([]byte(network))
- if dbAlias == nil {
- // We can't tell if the network exists, or doesn't exist
- // So... Assume it exists, but has no aliases.
+ bktExists := ctrAliasesBkt.Bucket([]byte(network))
+ if bktExists == nil {
return nil
}
- return dbAlias.ForEach(func(alias, ctrId []byte) error {
- aliases[string(alias)] = string(ctrId)
- return nil
- })
- })
- if err != nil {
- return nil, err
- }
-
- return aliases, nil
-}
-
-// RemoveAllAliasesForNetwork removes all the aliases in a given CNI network, as
-// part of that network being removed.
-func (s *BoltState) RemoveAllAliasesForNetwork(network string) error {
- if !s.valid {
- return define.ErrDBClosed
- }
-
- if network == "" {
- return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty")
- }
-
- db, err := s.getDBCon()
- if err != nil {
- return err
- }
- defer s.deferredCloseDBCon(db)
-
- return db.Update(func(tx *bolt.Tx) error {
- allCtrsBucket, err := getAllCtrsBucket(tx)
- if err != nil {
- return err
+ if err := ctrAliasesBkt.DeleteBucket([]byte(network)); err != nil {
+ return errors.Wrapf(err, "error removing container %s network aliases for network %s", ctr.ID(), network)
}
- ctrBucket, err := getCtrBucket(tx)
- if err != nil {
- return err
- }
-
- allAliasesBucket, err := getAliasesBucket(tx)
- if err != nil {
- return err
- }
-
- checkAliasesBucketExists := allAliasesBucket.Bucket([]byte(network))
- if checkAliasesBucketExists != nil {
- if err := allAliasesBucket.DeleteBucket([]byte(network)); err != nil {
- return errors.Wrapf(err, "error removing network %s aliases bucket from DB", network)
- }
- }
-
- // Iterate through all containers and remove their aliases
- // bucket for the network.
- return allCtrsBucket.ForEach(func(ctrID, ctrName []byte) error {
- dbCtr := ctrBucket.Bucket(ctrID)
- if dbCtr == nil {
- // DB State is inconsistent... but we can't do
- // anything about it.
- // Log and move on.
- logrus.Errorf("Container %s listed in all containers, but has no bucket!", string(ctrID))
- return nil
- }
-
- dbCtrAliases := dbCtr.Bucket(aliasesBkt)
- if dbCtrAliases == nil {
- // Container has no aliases, this is OK.
- return nil
- }
-
- ctrNetAliases := dbCtrAliases.Bucket([]byte(network))
- if ctrNetAliases != nil {
- if err := dbCtrAliases.DeleteBucket([]byte(network)); err != nil {
- return errors.Wrapf(err, "error removing bucket for network aliases for network %s from container %s", network, string(ctrID))
- }
- }
- return nil
- })
+ return nil
})
}
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index a48de3092..c06fedd3e 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -354,14 +354,6 @@ func getExecBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
return bkt, nil
}
-func getAliasesBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
- bkt := tx.Bucket(aliasesBkt)
- if bkt == nil {
- return nil, errors.Wrapf(define.ErrDBBadConfig, "aliases bucket not found in DB")
- }
- return bkt, nil
-}
-
func getRuntimeConfigBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
bkt := tx.Bucket(runtimeConfigBkt)
if bkt == nil {
@@ -584,11 +576,6 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
return err
}
- allAliasesBkt, err := getAliasesBucket(tx)
- if err != nil {
- return err
- }
-
// If a pod was given, check if it exists
var podDB *bolt.Bucket
var podCtrs *bolt.Bucket
@@ -635,41 +622,20 @@ 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
}
- // If we have network aliases, check if they are already in use.
- for net, aliases := range ctr.config.NetworkAliases {
- // Aliases cannot conflict with container names.
- for _, alias := range aliases {
- aliasExist := namesBucket.Get([]byte(alias))
- if aliasExist != nil {
- return errors.Wrapf(define.ErrCtrExists, "alias %q conflicts with existing container/pod name", alias)
- }
- }
-
- netAliasesBkt := allAliasesBkt.Bucket([]byte(net))
- if netAliasesBkt != nil {
- for _, alias := range aliases {
- aliasExist := netAliasesBkt.Get([]byte(alias))
- if aliasExist != nil {
- return errors.Wrapf(define.ErrAliasExists, "network alias %q already exists for network %q", net, alias)
- }
- }
- }
- hasNet := false
- for _, testNet := range ctr.config.Networks {
- if testNet == net {
- hasNet = true
- break
- }
- }
- if !hasNet {
- return errors.Wrapf(define.ErrInvalidArg, "container %s has network aliases for network %q but is not part of that network", ctr.ID(), net)
+ // 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)
}
}
@@ -690,63 +656,6 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
return errors.Wrapf(err, "error adding container %s to all containers bucket in DB", ctr.ID())
}
- // Check aliases for all networks, remove conflicts with the
- // container name.
- for _, net := range ctr.config.Networks {
- netAliasesBkt := allAliasesBkt.Bucket([]byte(net))
- if netAliasesBkt == nil {
- continue
- }
-
- otherCtrID := netAliasesBkt.Get(ctrName)
- if otherCtrID == nil {
- continue
- }
-
- if err := netAliasesBkt.Delete(ctrName); err != nil {
- return errors.Wrapf(err, "error removing container %s name from network aliases for network %s", ctr.ID(), net)
- }
-
- // We now need to remove from the other container.
- // To do this, we work through the container bucket,
- // then its aliases bucket, then its aliases for this
- // specific network, then we remove the alias.
- // Just slightly ridiculous. Just slightly.
- otherCtr := ctrBucket.Bucket(otherCtrID)
- if otherCtr == nil {
- // The state is inconsistent, but we can't do
- // much...
- logrus.Errorf("Container %s referred to by network alias but not present in state", string(otherCtrID))
- continue
- }
- otherCtrAliases := otherCtr.Bucket(aliasesBkt)
- if otherCtrAliases == nil {
- logrus.Errorf("Container %s is missing aliases but but has an alias", string(otherCtrID))
- continue
- }
- otherCtrNetworkAliases := otherCtrAliases.Bucket([]byte(net))
- if otherCtrNetworkAliases == nil {
- logrus.Errorf("Container %s is missing network aliases bucket for network %s but has alias in that network", string(otherCtrID), net)
- }
- if otherCtrNetworkAliases.Get(ctrName) != nil {
- if err := otherCtrNetworkAliases.Delete(ctrName); err != nil {
- return errors.Wrapf(err, "error removing container %s name from network %s aliases of container %s", ctr.Name(), net, string(otherCtrID))
- }
- }
- }
-
- for net, aliases := range ctr.config.NetworkAliases {
- netAliasesBkt, err := allAliasesBkt.CreateBucketIfNotExists([]byte(net))
- if err != nil {
- return errors.Wrapf(err, "error creating network aliases bucket for network %q", net)
- }
- for _, alias := range aliases {
- if err := netAliasesBkt.Put([]byte(alias), ctrID); err != nil {
- return errors.Wrapf(err, "error adding container %s network alias %q to network %q", ctr.ID(), alias, net)
- }
- }
- }
-
newCtrBkt, err := ctrBucket.CreateBucket(ctrID)
if err != nil {
return errors.Wrapf(err, "error adding container %s bucket to DB", ctr.ID())
@@ -998,49 +907,6 @@ func (s *BoltState) removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error
return errors.Wrapf(define.ErrCtrExists, "container %s is a dependency of the following containers: %s", ctr.ID(), strings.Join(deps, ", "))
}
- // Does the container have any network aliases?
- ctrNetAliasesBkt := ctrExists.Bucket(aliasesBkt)
- if ctrNetAliasesBkt != nil {
- allAliasesBkt, err := getAliasesBucket(tx)
- if err != nil {
- return err
- }
- ctrNetworksBkt := ctrExists.Bucket(networksBkt)
- // Internal state mismatch if this doesn't exist - we'll just
- // assume there are no aliases in that case.
- if ctrNetworksBkt != nil {
- // This is a little gross. Iterate through all networks
- // the container is joined to. Check if we have aliases
- // for them. If we do have such aliases, remove all of
- // then from the global aliases table for that network.
- err = ctrNetworksBkt.ForEach(func(network, v []byte) error {
- netAliasesBkt := ctrNetAliasesBkt.Bucket(network)
- if netAliasesBkt == nil {
- return nil
- }
- netAllAliasesBkt := allAliasesBkt.Bucket(network)
- if netAllAliasesBkt == nil {
- // Again the state is inconsistent here,
- // but the best we can do is try and
- // recover by ignoring it.
- return nil
- }
- return netAliasesBkt.ForEach(func(alias, v []byte) error {
- // We don't want to hard-fail on a
- // missing alias, so continue if we hit
- // errors.
- if err := netAllAliasesBkt.Delete(alias); err != nil {
- logrus.Errorf("Error removing alias %q from network %q when removing container %s", string(alias), string(network), ctr.ID())
- }
- return nil
- })
- })
- if err != nil {
- return err
- }
- }
- }
-
if err := ctrBucket.DeleteBucket(ctrID); err != nil {
return errors.Wrapf(define.ErrInternal, "error deleting container %s from DB", ctr.ID())
}
diff --git a/libpod/container.go b/libpod/container.go
index ea5a6e09c..580fa7b3d 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -1085,3 +1085,31 @@ func (c *Container) Timezone() string {
func (c *Container) Umask() string {
return c.config.Umask
}
+
+// Networks gets all the networks this container is connected to.
+// Please do NOT use ctr.config.Networks, as this can be changed from those
+// values at runtime via network connect and disconnect.
+// If the container is configured to use CNI and this function returns an empty
+// array, the container will still be connected to the default network.
+func (c *Container) Networks() ([]string, error) {
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if err := c.syncContainer(); err != nil {
+ return nil, err
+ }
+ }
+
+ return c.networks()
+}
+
+// Unlocked accessor for networks
+func (c *Container) networks() ([]string, error) {
+ networks, err := c.runtime.state.GetNetworks(c)
+ if err != nil && errors.Cause(err) == define.ErrNoSuchNetwork {
+ return c.config.Networks, nil
+ }
+
+ return networks, err
+}
diff --git a/libpod/container_config.go b/libpod/container_config.go
index d73fbb42f..cc3ad25ea 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -236,6 +236,9 @@ type ContainerNetworkConfig struct {
// 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.
+ // 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"`
// Network mode specified for the default network.
NetMode namespaces.NetworkMode `json:"networkMode,omitempty"`
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 0aeaae43d..108954bad 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -641,12 +641,17 @@ func (c *Container) removeIPv4Allocations() error {
cniDefaultNetwork = c.runtime.netPlugin.GetDefaultNetworkName()
}
+ networks, err := c.networks()
+ if err != nil {
+ return err
+ }
+
switch {
- case len(c.config.Networks) > 0 && len(c.config.Networks) != len(c.state.NetworkStatus):
- return errors.Wrapf(define.ErrInternal, "network mismatch: asked to join %d CNI networks but got %d CNI results", len(c.config.Networks), len(c.state.NetworkStatus))
- case len(c.config.Networks) == 0 && len(c.state.NetworkStatus) != 1:
+ case len(networks) > 0 && len(networks) != len(c.state.NetworkStatus):
+ return errors.Wrapf(define.ErrInternal, "network mismatch: asked to join %d CNI networks but got %d CNI results", len(networks), len(c.state.NetworkStatus))
+ case len(networks) == 0 && len(c.state.NetworkStatus) != 1:
return errors.Wrapf(define.ErrInternal, "network mismatch: did not specify CNI networks but joined more than one (%d)", len(c.state.NetworkStatus))
- case len(c.config.Networks) == 0 && cniDefaultNetwork == "":
+ case len(networks) == 0 && cniDefaultNetwork == "":
return errors.Wrapf(define.ErrInternal, "could not retrieve name of CNI default network")
}
@@ -656,11 +661,11 @@ func (c *Container) removeIPv4Allocations() error {
continue
}
candidate := ""
- if len(c.config.Networks) > 0 {
+ if len(networks) > 0 {
// CNI returns networks in order we passed them.
// So our index into results should be our index
// into networks.
- candidate = filepath.Join(cniNetworksDir, c.config.Networks[index], ctrIP.Address.IP.String())
+ candidate = filepath.Join(cniNetworksDir, networks[index], ctrIP.Address.IP.String())
} else {
candidate = filepath.Join(cniNetworksDir, cniDefaultNetwork, ctrIP.Address.IP.String())
}
@@ -1503,6 +1508,7 @@ func (c *Container) mountStorage() (_ string, deferredErr error) {
// config.
// Returns the volume that was mounted.
func (c *Container) mountNamedVolume(v *ContainerNamedVolume, mountpoint string) (*Volume, error) {
+ logrus.Debugf("Going to mount named volume %s", v.Name)
vol, err := c.runtime.state.Volume(v.Name)
if err != nil {
return nil, errors.Wrapf(err, "error retrieving named volume %s for container %s", v.Name, c.ID())
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 38b3a6686..775965477 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -575,6 +575,8 @@ type InspectAdditionalNetwork struct {
// Links is presently unused and maintained exclusively for
// compatibility.
Links []string `json:"Links"`
+ // Aliases are any network aliases the container has in this network.
+ Aliases []string `json:"Aliases,omitempty"`
}
// InspectNetworkSettings holds information about the network settings of the
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index 27c5febf4..471827b7c 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -33,9 +33,6 @@ var (
// ErrNoAliases indicates that the container does not have any network
// aliases.
ErrNoAliases = errors.New("no aliases for container")
- // ErrNoAliasesForNetwork indicates that the container has no aliases
- // for a specific network.
- ErrNoAliasesForNetwork = errors.New("no aliases for network")
// ErrCtrExists indicates a container with the same name or ID already
// exists
@@ -49,9 +46,9 @@ var (
// ErrExecSessionExists indicates an exec session with the same ID
// already exists.
ErrExecSessionExists = errors.New("exec session already exists")
- // ErrAliasExists indicates that a network alias with the same name
- // already exists in the network.
- ErrAliasExists = errors.New("alias already exists")
+ // ErrNetworkExists indicates that a network with the given name already
+ // exists.
+ ErrNetworkExists = errors.New("network already exists")
// ErrCtrStateInvalid indicates a container is in an improper state for
// the requested operation
diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go
index ba4c70c6b..6c0cde531 100644
--- a/libpod/in_memory_state.go
+++ b/libpod/in_memory_state.go
@@ -31,8 +31,7 @@ type InMemoryState struct {
ctrExecSessions map[string][]string
// Maps pod ID to a map of container ID to container struct.
podContainers map[string]map[string]*Container
- // Maps network name to alias to container ID
- networkAliases map[string]map[string]string
+ ctrNetworks map[string][]string
// Maps container ID to network name to list of aliases.
ctrNetworkAliases map[string]map[string][]string
// Global name registry - ensures name uniqueness and performs lookups.
@@ -69,7 +68,7 @@ func NewInMemoryState() (State, error) {
state.podContainers = make(map[string]map[string]*Container)
- state.networkAliases = make(map[string]map[string]string)
+ state.ctrNetworks = make(map[string][]string)
state.ctrNetworkAliases = make(map[string]map[string][]string)
state.nameIndex = registrar.NewRegistrar()
@@ -293,7 +292,7 @@ func (s *InMemoryState) AddContainer(ctr *Container) error {
}
// Check network aliases
- for network, aliases := range ctr.config.NetworkAliases {
+ for network := range ctr.config.NetworkAliases {
inNet := false
for _, net := range ctr.config.Networks {
if net == network {
@@ -304,19 +303,6 @@ func (s *InMemoryState) AddContainer(ctr *Container) error {
if !inNet {
return errors.Wrapf(define.ErrInvalidArg, "container %s has network aliases for network %q but is not joined to network", ctr.ID(), network)
}
-
- allNetAliases, ok := s.networkAliases[network]
- if ok {
- for _, alias := range aliases {
- // Check if alias is a name
- if _, err := s.nameIndex.Get(alias); err == nil {
- return define.ErrInvalidArg
- }
- if _, ok := allNetAliases[alias]; ok {
- return define.ErrAliasExists
- }
- }
- }
}
// There are potential race conditions with this
@@ -375,46 +361,17 @@ func (s *InMemoryState) AddContainer(ctr *Container) error {
s.addCtrToVolDependsMap(ctr.ID(), vol.Name)
}
- for _, network := range ctr.config.Networks {
- allNetAliases, ok := s.networkAliases[network]
- if !ok {
- continue
- }
- otherCtrID, ok := allNetAliases[ctr.Name()]
- if !ok {
- continue
- }
- delete(allNetAliases, ctr.Name())
-
- otherCtrAliases, ok := s.ctrNetworkAliases[otherCtrID]
- if !ok {
- continue
- }
- otherCtrNetAliases, ok := otherCtrAliases[network]
- if !ok {
- continue
- }
- newAliases := []string{}
- for _, alias := range otherCtrNetAliases {
- if alias != ctr.Name() {
- newAliases = append(newAliases, alias)
- }
+ // Add networks
+ newNets := make([]string, 0, len(ctr.config.Networks))
+ for _, net := range ctr.config.Networks {
+ if net == "" {
+ return define.ErrInvalidArg
}
- otherCtrAliases[network] = newAliases
+ newNets = append(newNets, net)
}
+ s.ctrNetworks[ctr.ID()] = newNets
// Add network aliases
- for network, aliases := range ctr.config.NetworkAliases {
- allNetAliases, ok := s.networkAliases[network]
- if !ok {
- allNetAliases = make(map[string]string)
- s.networkAliases[network] = allNetAliases
- }
-
- for _, alias := range aliases {
- allNetAliases[alias] = ctr.ID()
- }
- }
s.ctrNetworkAliases[ctr.ID()] = ctr.config.NetworkAliases
return nil
@@ -480,18 +437,12 @@ func (s *InMemoryState) RemoveContainer(ctr *Container) error {
}
// Remove our network aliases
- ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()]
- if ok {
- for network, aliases := range ctrAliases {
- netAliases, ok := s.networkAliases[network]
- if ok {
- for _, alias := range aliases {
- delete(netAliases, alias)
- }
- }
- }
+ if _, ok := s.ctrNetworkAliases[ctr.ID()]; ok {
delete(s.ctrNetworkAliases, ctr.ID())
}
+ if _, ok := s.ctrNetworks[ctr.ID()]; ok {
+ delete(s.ctrNetworks, ctr.ID())
+ }
return nil
}
@@ -569,6 +520,26 @@ func (s *InMemoryState) AllContainers() ([]*Container, error) {
return ctrs, nil
}
+// Get all networks this container is present in.
+func (s *InMemoryState) GetNetworks(ctr *Container) ([]string, error) {
+ if !ctr.valid {
+ return nil, define.ErrCtrRemoved
+ }
+
+ ctr, ok := s.containers[ctr.ID()]
+ if !ok {
+ ctr.valid = false
+ return nil, define.ErrNoSuchCtr
+ }
+
+ ctrNetworks, ok := s.ctrNetworks[ctr.ID()]
+ if !ok {
+ return nil, define.ErrNoSuchNetwork
+ }
+
+ return ctrNetworks, nil
+}
+
// GetNetworkAliases returns network aliases for the given container in the
// given network.
func (s *InMemoryState) GetNetworkAliases(ctr *Container, network string) ([]string, error) {
@@ -582,6 +553,7 @@ func (s *InMemoryState) GetNetworkAliases(ctr *Container, network string) ([]str
ctr, ok := s.containers[ctr.ID()]
if !ok {
+ ctr.valid = false
return nil, define.ErrNoSuchCtr
}
@@ -615,6 +587,7 @@ func (s *InMemoryState) GetAllNetworkAliases(ctr *Container) (map[string][]strin
ctr, ok := s.containers[ctr.ID()]
if !ok {
+ ctr.valid = false
return nil, define.ErrNoSuchCtr
}
@@ -626,9 +599,8 @@ func (s *InMemoryState) GetAllNetworkAliases(ctr *Container) (map[string][]strin
return ctrAliases, nil
}
-// SetNetworkAliases sets network aliases for the given container in the given
-// network.
-func (s *InMemoryState) SetNetworkAliases(ctr *Container, network string, aliases []string) error {
+// NetworkConnect connects to the given network
+func (s *InMemoryState) NetworkConnect(ctr *Container, network string, aliases []string) error {
if !ctr.valid {
return define.ErrCtrRemoved
}
@@ -639,55 +611,37 @@ func (s *InMemoryState) SetNetworkAliases(ctr *Container, network string, aliase
ctr, ok := s.containers[ctr.ID()]
if !ok {
+ ctr.valid = false
return define.ErrNoSuchCtr
}
inNet := false
- for _, net := range ctr.config.Networks {
+ ctrNetworks, ok := s.ctrNetworks[ctr.ID()]
+ if !ok {
+ return define.ErrNoSuchNetwork
+ }
+ for _, net := range ctrNetworks {
if net == network {
inNet = true
}
}
- if !inNet {
- return define.ErrInvalidArg
+ if inNet {
+ return define.ErrNoSuchNetwork
}
+ s.ctrNetworks[ctr.ID()] = append(ctrNetworks, network)
ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()]
if !ok {
ctrAliases = make(map[string][]string)
s.ctrNetworkAliases[ctr.ID()] = ctrAliases
}
- netAliases, ok := ctrAliases[network]
- if !ok {
- netAliases = []string{}
- ctrAliases[network] = netAliases
- }
-
- allAliases, ok := s.networkAliases[network]
- if !ok {
- allAliases = make(map[string]string)
- s.networkAliases[network] = allAliases
- }
-
- for _, alias := range netAliases {
- delete(allAliases, alias)
- }
-
- for _, newAlias := range aliases {
- if _, ok := allAliases[newAlias]; ok {
- return define.ErrAliasExists
- }
- allAliases[newAlias] = ctr.ID()
- }
-
ctrAliases[network] = aliases
return nil
}
-// RemoveNetworkAliases removes network aliases from the given container in the
-// given network.
-func (s *InMemoryState) RemoveNetworkAliases(ctr *Container, network string) error {
+// Disconnect from the given network and remove all aliases in it.
+func (s *InMemoryState) NetworkDisconnect(ctr *Container, network string) error {
if !ctr.valid {
return define.ErrCtrRemoved
}
@@ -698,73 +652,36 @@ func (s *InMemoryState) RemoveNetworkAliases(ctr *Container, network string) err
ctr, ok := s.containers[ctr.ID()]
if !ok {
+ ctr.valid = false
return define.ErrNoSuchCtr
}
+ ctrNetworks, ok := s.ctrNetworks[ctr.ID()]
+ if !ok {
+ return define.ErrNoSuchNetwork
+ }
inNet := false
- for _, net := range ctr.config.Networks {
+ remainingNets := make([]string, 0, len(ctrNetworks))
+ for _, net := range ctrNetworks {
if net == network {
inNet = true
+ break
+ } else {
+ remainingNets = append(remainingNets, net)
}
}
if !inNet {
- return define.ErrInvalidArg
+ return define.ErrNoSuchNetwork
}
+ s.ctrNetworks[ctr.ID()] = remainingNets
ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()]
if !ok {
ctrAliases = make(map[string][]string)
s.ctrNetworkAliases[ctr.ID()] = ctrAliases
}
- netAliases, ok := ctrAliases[network]
- if !ok {
- netAliases = []string{}
- ctrAliases[network] = netAliases
- }
-
- allAliases, ok := s.networkAliases[network]
- if !ok {
- allAliases = make(map[string]string)
- s.networkAliases[network] = allAliases
- }
-
- for _, alias := range netAliases {
- delete(allAliases, alias)
- }
-
- return nil
-}
-
-// GetAllAliasesForNetwork gets all the aliases for a single network.
-func (s *InMemoryState) GetAllAliasesForNetwork(network string) (map[string]string, error) {
- if network == "" {
- return nil, errors.Wrapf(define.ErrInvalidArg, "network names must not be empty")
- }
-
- allAliases, ok := s.networkAliases[network]
- if !ok {
- // Can't tell if the network exists.
- // Assume it does.
- return map[string]string{}, nil
- }
-
- return allAliases, nil
-}
-
-// RemoveAllAliasesForNetwork removes all the aliases for a given network.
-func (s *InMemoryState) RemoveAllAliasesForNetwork(network string) error {
- if network == "" {
- return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty")
- }
-
- if _, ok := s.networkAliases[network]; ok {
- delete(s.networkAliases, network)
- }
-
- for _, ctrAliases := range s.ctrNetworkAliases {
- if _, ok := ctrAliases[network]; ok {
- delete(ctrAliases, network)
- }
+ if _, ok := ctrAliases[network]; ok {
+ delete(ctrAliases, network)
}
return nil
@@ -1422,7 +1339,7 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
}
// Check network aliases
- for network, aliases := range ctr.config.NetworkAliases {
+ for network := range ctr.config.NetworkAliases {
inNet := false
for _, net := range ctr.config.Networks {
if net == network {
@@ -1433,19 +1350,6 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
if !inNet {
return errors.Wrapf(define.ErrInvalidArg, "container %s has network aliases for network %q but is not joined to network", ctr.ID(), network)
}
-
- allNetAliases, ok := s.networkAliases[network]
- if ok {
- for _, alias := range aliases {
- // Check if alias is a name
- if _, err := s.nameIndex.Get(alias); err == nil {
- return define.ErrInvalidArg
- }
- if _, ok := allNetAliases[alias]; ok {
- return define.ErrAliasExists
- }
- }
- }
}
// Retrieve pod containers list
@@ -1525,46 +1429,17 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
s.addCtrToVolDependsMap(ctr.ID(), vol.Name)
}
- for _, network := range ctr.config.Networks {
- allNetAliases, ok := s.networkAliases[network]
- if !ok {
- continue
- }
- otherCtrID, ok := allNetAliases[ctr.Name()]
- if !ok {
- continue
- }
- delete(allNetAliases, ctr.Name())
-
- otherCtrAliases, ok := s.ctrNetworkAliases[otherCtrID]
- if !ok {
- continue
- }
- otherCtrNetAliases, ok := otherCtrAliases[network]
- if !ok {
- continue
- }
- newAliases := []string{}
- for _, alias := range otherCtrNetAliases {
- if alias != ctr.Name() {
- newAliases = append(newAliases, alias)
- }
+ // Add networks
+ newNets := make([]string, 0, len(ctr.config.Networks))
+ for _, net := range ctr.config.Networks {
+ if net == "" {
+ return define.ErrInvalidArg
}
- otherCtrAliases[network] = newAliases
+ newNets = append(newNets, net)
}
+ s.ctrNetworks[ctr.ID()] = newNets
// Add network aliases
- for network, aliases := range ctr.config.NetworkAliases {
- allNetAliases, ok := s.networkAliases[network]
- if !ok {
- allNetAliases = make(map[string]string)
- s.networkAliases[network] = allNetAliases
- }
-
- for _, alias := range aliases {
- allNetAliases[alias] = ctr.ID()
- }
- }
s.ctrNetworkAliases[ctr.ID()] = ctr.config.NetworkAliases
return nil
@@ -1648,18 +1523,12 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
}
// Remove our network aliases
- ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()]
- if ok {
- for network, aliases := range ctrAliases {
- netAliases, ok := s.networkAliases[network]
- if ok {
- for _, alias := range aliases {
- delete(netAliases, alias)
- }
- }
- }
+ if _, ok := s.ctrNetworkAliases[ctr.ID()]; ok {
delete(s.ctrNetworkAliases, ctr.ID())
}
+ if _, ok := s.ctrNetworks[ctr.ID()]; ok {
+ delete(s.ctrNetworks, ctr.ID())
+ }
return nil
}
diff --git a/libpod/network/config.go b/libpod/network/config.go
index a08e684d8..ce8a4446c 100644
--- a/libpod/network/config.go
+++ b/libpod/network/config.go
@@ -131,8 +131,9 @@ func (f FirewallConfig) Bytes() ([]byte, error) {
// DNSNameConfig describes the dns container name resolution plugin config
type DNSNameConfig struct {
- PluginType string `json:"type"`
- DomainName string `json:"domainName"`
+ PluginType string `json:"type"`
+ DomainName string `json:"domainName"`
+ Capabilities map[string]bool `json:"capabilities"`
}
// Bytes outputs the configuration as []byte
diff --git a/libpod/network/create.go b/libpod/network/create.go
index bf11631bf..c11904ecf 100644
--- a/libpod/network/create.go
+++ b/libpod/network/create.go
@@ -15,6 +15,7 @@ import (
"github.com/pkg/errors"
)
+// Create the CNI network
func Create(name string, options entities.NetworkCreateOptions, r *libpod.Runtime) (*entities.NetworkCreateReport, error) {
var fileName string
if err := isSupportedDriver(options.Driver); err != nil {
@@ -41,60 +42,120 @@ func Create(name string, options entities.NetworkCreateOptions, r *libpod.Runtim
return &entities.NetworkCreateReport{Filename: fileName}, nil
}
+// validateBridgeOptions validate the bridge networking options
+func validateBridgeOptions(options entities.NetworkCreateOptions) error {
+ subnet := &options.Subnet
+ ipRange := &options.Range
+ gateway := options.Gateway
+ // if IPv6 is set an IPv6 subnet MUST be specified
+ if options.IPv6 && ((subnet.IP == nil) || (subnet.IP != nil && !IsIPv6(subnet.IP))) {
+ return errors.Errorf("ipv6 option requires an IPv6 --subnet to be provided")
+ }
+ // range and gateway depend on subnet
+ if subnet.IP == nil && (ipRange.IP != nil || gateway != nil) {
+ return errors.Errorf("every ip-range or gateway must have a corresponding subnet")
+ }
+
+ // if a range is given, we need to ensure it is "in" the network range.
+ if ipRange.IP != nil {
+ firstIP, err := FirstIPInSubnet(ipRange)
+ if err != nil {
+ return errors.Wrapf(err, "failed to get first IP address from ip-range")
+ }
+ lastIP, err := LastIPInSubnet(ipRange)
+ if err != nil {
+ return errors.Wrapf(err, "failed to get last IP address from ip-range")
+ }
+ if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) {
+ return errors.Errorf("the ip range %s does not fall within the subnet range %s", ipRange.String(), subnet.String())
+ }
+ }
+
+ // if network is provided and if gateway is provided, make sure it is "in" network
+ if gateway != nil && !subnet.Contains(gateway) {
+ return errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String())
+ }
+
+ return nil
+
+}
+
// createBridge creates a CNI network
func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
isGateway := true
ipMasq := true
- subnet := &options.Subnet
- ipRange := options.Range
runtimeConfig, err := r.GetConfig()
if err != nil {
return "", err
}
- // if range is provided, make sure it is "in" network
- if subnet.IP != nil {
- // if network is provided, does it conflict with existing CNI or live networks
- err = ValidateUserNetworkIsAvailable(runtimeConfig, subnet)
- } else {
- // if no network is provided, figure out network
- subnet, err = GetFreeNetwork(runtimeConfig)
- }
+
+ // validate options
+ err = validateBridgeOptions(options)
if err != nil {
return "", err
}
+
+ // For compatibility with the docker implementation:
+ // if IPv6 is enabled (it really means dual-stack) then an IPv6 subnet has to be provided, and one free network is allocated for IPv4
+ // if IPv6 is not specified the subnet may be specified and can be either IPv4 or IPv6 (podman, unlike docker, allows IPv6 only networks)
+ // If not subnet is specified an IPv4 subnet will be allocated
+ subnet := &options.Subnet
+ ipRange := &options.Range
gateway := options.Gateway
- if gateway == nil {
- // if no gateway is provided, provide it as first ip of network
- gateway = CalcGatewayIP(subnet)
- }
- // if network is provided and if gateway is provided, make sure it is "in" network
- if options.Subnet.IP != nil && options.Gateway != nil {
- if !subnet.Contains(gateway) {
- return "", errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String())
+ var ipamRanges [][]IPAMLocalHostRangeConf
+ var routes []IPAMRoute
+ if subnet.IP != nil {
+ // if network is provided, does it conflict with existing CNI or live networks
+ err = ValidateUserNetworkIsAvailable(runtimeConfig, subnet)
+ if err != nil {
+ return "", err
}
- }
- if options.Internal {
- isGateway = false
- ipMasq = false
- }
-
- // if a range is given, we need to ensure it is "in" the network range.
- if options.Range.IP != nil {
- if options.Subnet.IP == nil {
- return "", errors.New("you must define a subnet range to define an ip-range")
+ // obtain CNI subnet default route
+ defaultRoute, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP))
+ if err != nil {
+ return "", err
}
- firstIP, err := FirstIPInSubnet(&options.Range)
+ routes = append(routes, defaultRoute)
+ // obtain CNI range
+ ipamRange, err := NewIPAMLocalHostRange(subnet, ipRange, gateway)
if err != nil {
return "", err
}
- lastIP, err := LastIPInSubnet(&options.Range)
+ ipamRanges = append(ipamRanges, ipamRange)
+ }
+ // if no network is provided or IPv6 flag used, figure out the IPv4 network
+ if options.IPv6 || len(routes) == 0 {
+ subnetV4, err := GetFreeNetwork(runtimeConfig)
if err != nil {
return "", err
}
- if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) {
- return "", errors.Errorf("the ip range %s does not fall within the subnet range %s", options.Range.String(), subnet.String())
+ // obtain IPv4 default route
+ defaultRoute, err := NewIPAMDefaultRoute(false)
+ if err != nil {
+ return "", err
}
+ routes = append(routes, defaultRoute)
+ // the CNI bridge plugin does not need to set
+ // the range or gateway options explicitly
+ ipamRange, err := NewIPAMLocalHostRange(subnetV4, nil, nil)
+ if err != nil {
+ return "", err
+ }
+ ipamRanges = append(ipamRanges, ipamRange)
+ }
+
+ // create CNI config
+ ipamConfig, err := NewIPAMHostLocalConf(routes, ipamRanges)
+ if err != nil {
+ return "", err
}
+
+ if options.Internal {
+ isGateway = false
+ ipMasq = false
+ }
+
+ // obtain host bridge name
bridgeDeviceName, err := GetFreeDeviceName(runtimeConfig)
if err != nil {
return "", err
@@ -113,20 +174,9 @@ func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreate
name = bridgeDeviceName
}
+ // create CNI plugin configuration
ncList := NewNcList(name, version.Current())
var plugins []CNIPlugins
- var routes []IPAMRoute
-
- defaultRoute, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP))
- if err != nil {
- return "", err
- }
- routes = append(routes, defaultRoute)
- ipamConfig, err := NewIPAMHostLocalConf(subnet, routes, ipRange, gateway)
- if err != nil {
- return "", err
- }
-
// TODO need to iron out the role of isDefaultGW and IPMasq
bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig)
plugins = append(plugins, bridge)
diff --git a/libpod/network/create_test.go b/libpod/network/create_test.go
new file mode 100644
index 000000000..16188e497
--- /dev/null
+++ b/libpod/network/create_test.go
@@ -0,0 +1,131 @@
+package network
+
+import (
+ "net"
+ "testing"
+
+ "github.com/containers/podman/v2/pkg/domain/entities"
+)
+
+func Test_validateBridgeOptions(t *testing.T) {
+
+ tests := []struct {
+ name string
+ subnet net.IPNet
+ ipRange net.IPNet
+ gateway net.IP
+ isIPv6 bool
+ wantErr bool
+ }{
+ {
+ name: "IPv4 subnet only",
+ subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
+ },
+ {
+ name: "IPv4 subnet and range",
+ subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
+ ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
+ },
+ {
+ name: "IPv4 subnet and gateway",
+ subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
+ gateway: net.ParseIP("192.168.0.10"),
+ },
+ {
+ name: "IPv4 subnet, range and gateway",
+ subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
+ ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
+ gateway: net.ParseIP("192.168.0.10"),
+ },
+ {
+ name: "IPv6 subnet only",
+ subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
+ },
+ {
+ name: "IPv6 subnet and range",
+ subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
+ ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
+ isIPv6: true,
+ },
+ {
+ name: "IPv6 subnet and gateway",
+ subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
+ gateway: net.ParseIP("2001:DB8::2"),
+ isIPv6: true,
+ },
+ {
+ name: "IPv6 subnet, range and gateway",
+ subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
+ ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
+ gateway: net.ParseIP("2001:DB8::2"),
+ isIPv6: true,
+ },
+ {
+ name: "IPv6 subnet, range and gateway without IPv6 option (PODMAN SUPPORTS IT UNLIKE DOCKEr)",
+ subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
+ ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
+ gateway: net.ParseIP("2001:DB8::2"),
+ isIPv6: false,
+ },
+ {
+ name: "range provided but not subnet",
+ ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
+ wantErr: true,
+ },
+ {
+ name: "gateway provided but not subnet",
+ gateway: net.ParseIP("192.168.0.10"),
+ wantErr: true,
+ },
+ {
+ name: "IPv4 subnet but IPv6 required",
+ subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
+ ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
+ gateway: net.ParseIP("192.168.0.10"),
+ isIPv6: true,
+ wantErr: true,
+ },
+ {
+ name: "IPv6 required but IPv4 options used",
+ subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
+ ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
+ gateway: net.ParseIP("192.168.0.10"),
+ isIPv6: true,
+ wantErr: true,
+ },
+ {
+ name: "IPv6 required but not subnet provided",
+ isIPv6: true,
+ wantErr: true,
+ },
+ {
+ name: "range out of the subnet",
+ subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
+ ipRange: net.IPNet{IP: net.ParseIP("2001:1:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
+ gateway: net.ParseIP("2001:DB8::2"),
+ isIPv6: true,
+ wantErr: true,
+ },
+ {
+ name: "gateway out of the subnet",
+ subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
+ gateway: net.ParseIP("2001::2"),
+ isIPv6: true,
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ options := entities.NetworkCreateOptions{
+ Subnet: tt.subnet,
+ Range: tt.ipRange,
+ Gateway: tt.gateway,
+ IPv6: tt.isIPv6,
+ }
+ if err := validateBridgeOptions(options); (err != nil) != tt.wantErr {
+ t.Errorf("validateBridgeOptions() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
diff --git a/libpod/network/files.go b/libpod/network/files.go
index a2090491f..846e5c62d 100644
--- a/libpod/network/files.go
+++ b/libpod/network/files.go
@@ -14,6 +14,7 @@ import (
"github.com/pkg/errors"
)
+// GetCNIConfDir get CNI configuration directory
func GetCNIConfDir(configArg *config.Config) string {
if len(configArg.Network.NetworkConfigDir) < 1 {
dc, err := config.DefaultConfig()
diff --git a/libpod/network/netconflist.go b/libpod/network/netconflist.go
index 8187fdb39..111f1715c 100644
--- a/libpod/network/netconflist.go
+++ b/libpod/network/netconflist.go
@@ -42,8 +42,7 @@ func NewHostLocalBridge(name string, isGateWay, isDefaultGW, ipMasq bool, ipamCo
}
// NewIPAMHostLocalConf creates a new IPAMHostLocal configfuration
-func NewIPAMHostLocalConf(subnet *net.IPNet, routes []IPAMRoute, ipRange net.IPNet, gw net.IP) (IPAMHostLocalConf, error) {
- var ipamRanges [][]IPAMLocalHostRangeConf
+func NewIPAMHostLocalConf(routes []IPAMRoute, ipamRanges [][]IPAMLocalHostRangeConf) (IPAMHostLocalConf, error) {
ipamConf := IPAMHostLocalConf{
PluginType: "host-local",
Routes: routes,
@@ -51,22 +50,19 @@ func NewIPAMHostLocalConf(subnet *net.IPNet, routes []IPAMRoute, ipRange net.IPN
//ResolveConf: "",
//DataDir: ""
}
- IPAMRange, err := newIPAMLocalHostRange(subnet, &ipRange, &gw)
- if err != nil {
- return ipamConf, err
- }
- ipamRanges = append(ipamRanges, IPAMRange)
+
ipamConf.Ranges = ipamRanges
return ipamConf, nil
}
-func newIPAMLocalHostRange(subnet *net.IPNet, ipRange *net.IPNet, gw *net.IP) ([]IPAMLocalHostRangeConf, error) { //nolint:interfacer
+// NewIPAMLocalHostRange create a new IPAM range
+func NewIPAMLocalHostRange(subnet *net.IPNet, ipRange *net.IPNet, gw net.IP) ([]IPAMLocalHostRangeConf, error) { //nolint:interfacer
var ranges []IPAMLocalHostRangeConf
hostRange := IPAMLocalHostRangeConf{
Subnet: subnet.String(),
}
// an user provided a range, we add it here
- if ipRange.IP != nil {
+ if ipRange != nil && ipRange.IP != nil {
first, err := FirstIPInSubnet(ipRange)
if err != nil {
return nil, err
@@ -126,9 +122,12 @@ func NewFirewallPlugin() FirewallConfig {
// NewDNSNamePlugin creates the dnsname config with a given
// domainname
func NewDNSNamePlugin(domainName string) DNSNameConfig {
+ caps := make(map[string]bool, 1)
+ caps["aliases"] = true
return DNSNameConfig{
- PluginType: "dnsname",
- DomainName: domainName,
+ PluginType: "dnsname",
+ DomainName: domainName,
+ Capabilities: caps,
}
}
diff --git a/libpod/network/netconflist_test.go b/libpod/network/netconflist_test.go
index 5893bf985..6bf1a9777 100644
--- a/libpod/network/netconflist_test.go
+++ b/libpod/network/netconflist_test.go
@@ -1,6 +1,7 @@
package network
import (
+ "net"
"reflect"
"testing"
)
@@ -36,3 +37,72 @@ func TestNewIPAMDefaultRoute(t *testing.T) {
})
}
}
+
+func TestNewIPAMLocalHostRange(t *testing.T) {
+ tests := []struct {
+ name string
+ subnet *net.IPNet
+ ipRange *net.IPNet
+ gw net.IP
+ want []IPAMLocalHostRangeConf
+ }{
+ {
+ name: "IPv4 subnet",
+ subnet: &net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
+ want: []IPAMLocalHostRangeConf{
+ {
+ Subnet: "192.168.0.0/24",
+ },
+ },
+ },
+ {
+ name: "IPv4 subnet, range and gateway",
+ subnet: &net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
+ ipRange: &net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
+ gw: net.ParseIP("192.168.0.10"),
+ want: []IPAMLocalHostRangeConf{
+ {
+ Subnet: "192.168.0.0/24",
+ RangeStart: "192.168.0.129",
+ RangeEnd: "192.168.0.255",
+ Gateway: "192.168.0.10",
+ },
+ },
+ },
+ {
+ name: "IPv6 subnet",
+ subnet: &net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
+ want: []IPAMLocalHostRangeConf{
+ {
+ Subnet: "2001:db8::/48",
+ },
+ },
+ },
+ {
+ name: "IPv6 subnet, range and gateway",
+ subnet: &net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
+ ipRange: &net.IPNet{IP: net.ParseIP("2001:DB8:1:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
+ gw: net.ParseIP("2001:DB8::2"),
+ want: []IPAMLocalHostRangeConf{
+ {
+ Subnet: "2001:db8::/48",
+ RangeStart: "2001:db8:1:1::1",
+ RangeEnd: "2001:db8:1:1:ffff:ffff:ffff:ffff",
+ Gateway: "2001:db8::2",
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := NewIPAMLocalHostRange(tt.subnet, tt.ipRange, tt.gw)
+ if err != nil {
+ t.Errorf("no error expected: %v", err)
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("NewIPAMLocalHostRange() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 28dca8dd8..1a7740085 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -104,7 +104,18 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
podName := getCNIPodName(ctr)
- podNetwork := r.getPodNetwork(ctr.ID(), podName, ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP, requestedMAC)
+ networks, err := ctr.networks()
+ if err != nil {
+ return nil, err
+ }
+ podNetwork := r.getPodNetwork(ctr.ID(), podName, ctrNS.Path(), networks, ctr.config.PortMappings, requestedIP, requestedMAC)
+ aliases, err := ctr.runtime.state.GetAllNetworkAliases(ctr)
+ if err != nil {
+ return nil, err
+ }
+ if len(aliases) > 0 {
+ podNetwork.Aliases = aliases
+ }
results, err := r.netPlugin.SetUpPod(podNetwork)
if err != nil {
@@ -202,7 +213,11 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
if ctr.config.NetMode.IsSlirp4netns() {
return r.setupSlirp4netns(ctr)
}
- if len(ctr.config.Networks) > 0 {
+ networks, err := ctr.networks()
+ if err != nil {
+ return err
+ }
+ if len(networks) > 0 {
// set up port forwarder for CNI-in-slirp4netns
netnsPath := ctr.state.NetNS.Path()
// TODO: support slirp4netns port forwarder as well
@@ -718,6 +733,11 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID())
+ networks, err := ctr.networks()
+ if err != nil {
+ return err
+ }
+
// rootless containers do not use the CNI plugin directly
if !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() {
var requestedIP net.IP
@@ -738,7 +758,7 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
requestedMAC = ctr.config.StaticMAC
}
- podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP, requestedMAC)
+ podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), networks, ctr.config.PortMappings, requestedIP, requestedMAC)
if err := r.netPlugin.TearDownPod(podNetwork); err != nil {
return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", ctr.ID())
@@ -746,7 +766,7 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
}
// CNI-in-slirp4netns
- if rootless.IsRootless() && len(ctr.config.Networks) != 0 {
+ if rootless.IsRootless() && len(networks) != 0 {
if err := DeallocRootlessCNI(context.Background(), ctr); err != nil {
return errors.Wrapf(err, "error tearing down CNI-in-slirp4netns for container %s", ctr.ID())
}
@@ -832,13 +852,18 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
settings := new(define.InspectNetworkSettings)
settings.Ports = makeInspectPortBindings(c.config.PortMappings)
+ networks, err := c.networks()
+ if err != nil {
+ return nil, err
+ }
+
// We can't do more if the network is down.
if c.state.NetNS == nil {
// We still want to make dummy configurations for each CNI net
// the container joined.
- if len(c.config.Networks) > 0 {
- settings.Networks = make(map[string]*define.InspectAdditionalNetwork, len(c.config.Networks))
- for _, net := range c.config.Networks {
+ if len(networks) > 0 {
+ settings.Networks = make(map[string]*define.InspectAdditionalNetwork, len(networks))
+ for _, net := range networks {
cniNet := new(define.InspectAdditionalNetwork)
cniNet.NetworkID = net
settings.Networks[net] = cniNet
@@ -857,16 +882,16 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
}
// If we have CNI networks - handle that here
- if len(c.config.Networks) > 0 {
- if len(c.config.Networks) != len(c.state.NetworkStatus) {
- return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d CNI networks but have information on %d networks", len(c.config.Networks), len(c.state.NetworkStatus))
+ if len(networks) > 0 {
+ if len(networks) != len(c.state.NetworkStatus) {
+ return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d CNI networks but have information on %d networks", len(networks), len(c.state.NetworkStatus))
}
settings.Networks = make(map[string]*define.InspectAdditionalNetwork)
// CNI results should be in the same order as the list of
// networks we pass into CNI.
- for index, name := range c.config.Networks {
+ for index, name := range networks {
cniResult := c.state.NetworkStatus[index]
addedNet := new(define.InspectAdditionalNetwork)
addedNet.NetworkID = name
@@ -875,6 +900,13 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
if err != nil {
return nil, err
}
+
+ aliases, err := c.runtime.state.GetNetworkAliases(c, name)
+ if err != nil {
+ return nil, err
+ }
+ addedNet.Aliases = aliases
+
addedNet.InspectBasicNetworkConfig = basicConfig
settings.Networks[name] = addedNet
diff --git a/libpod/rootless_cni_linux.go b/libpod/rootless_cni_linux.go
index 3d4ff6e86..1d6158cc2 100644
--- a/libpod/rootless_cni_linux.go
+++ b/libpod/rootless_cni_linux.go
@@ -40,8 +40,12 @@ const (
//
// AllocRootlessCNI does not lock c. c should be already locked.
func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes.Result, error) {
- if len(c.config.Networks) == 0 {
- return nil, nil, errors.New("allocRootlessCNI shall not be called when len(c.config.Networks) == 0")
+ networks, err := c.networks()
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(networks) == 0 {
+ return nil, nil, errors.New("rootless CNI networking requires that the container has joined at least one CNI network")
}
l, err := getRootlessCNIInfraLock(c.runtime)
if err != nil {
@@ -54,8 +58,8 @@ func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes.
return nil, nil, err
}
k8sPodName := getCNIPodName(c) // passed to CNI as K8S_POD_NAME
- cniResults := make([]*cnitypes.Result, len(c.config.Networks))
- for i, nw := range c.config.Networks {
+ cniResults := make([]*cnitypes.Result, len(networks))
+ for i, nw := range networks {
cniRes, err := rootlessCNIInfraCallAlloc(infra, c.ID(), nw, k8sPodName)
if err != nil {
return nil, nil, err
@@ -77,8 +81,12 @@ func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes.
//
// DeallocRootlessCNI does not lock c. c should be already locked.
func DeallocRootlessCNI(ctx context.Context, c *Container) error {
- if len(c.config.Networks) == 0 {
- return errors.New("deallocRootlessCNI shall not be called when len(c.config.Networks) == 0")
+ networks, err := c.networks()
+ if err != nil {
+ return err
+ }
+ if len(networks) == 0 {
+ return errors.New("rootless CNI networking requires that the container has joined at least one CNI network")
}
l, err := getRootlessCNIInfraLock(c.runtime)
if err != nil {
@@ -91,7 +99,7 @@ func DeallocRootlessCNI(ctx context.Context, c *Container) error {
return nil
}
var errs *multierror.Error
- for _, nw := range c.config.Networks {
+ for _, nw := range networks {
err := rootlessCNIInfraCallDelloc(infra, c.ID(), nw)
if err != nil {
errs = multierror.Append(errs, err)
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index c84268889..14b537ca2 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -345,8 +345,15 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
// Lock all named volumes we are adding ourself to, to ensure we can't
// use a volume being removed.
+ volsLocked := make(map[string]bool)
for _, namedVol := range ctrNamedVolumes {
toLock := namedVol
+ // Ensure that we don't double-lock a named volume that is used
+ // more than once.
+ if volsLocked[namedVol.Name()] {
+ continue
+ }
+ volsLocked[namedVol.Name()] = true
toLock.lock.Lock()
defer toLock.lock.Unlock()
}
diff --git a/libpod/state.go b/libpod/state.go
index 183f773b5..074d21740 100644
--- a/libpod/state.go
+++ b/libpod/state.go
@@ -98,20 +98,18 @@ type State interface {
// returned.
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)
- // Set network aliases for the given container in the given network.
- SetNetworkAliases(ctr *Container, network string, aliases []string) error
- // Remove network aliases for the given container in the given network.
- RemoveNetworkAliases(ctr *Container, network string) error
- // GetAllAliasesForNetwork returns all the aliases for a given
- // network. Returns a map of alias to container ID.
- GetAllAliasesForNetwork(network string) (map[string]string, error)
- // RemoveAllAliasesForNetwork removes all the aliases for a given
- // network.
- RemoveAllAliasesForNetwork(network string) error
+ // Add the container to the given network, adding the given aliases
+ // (if present).
+ NetworkConnect(ctr *Container, network string, aliases []string) 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
// Return a container config from the database by full ID
GetContainerConfig(id string) (*ContainerConfig, error)
diff --git a/libpod/state_test.go b/libpod/state_test.go
index cf41270bf..da28f3d3f 100644
--- a/libpod/state_test.go
+++ b/libpod/state_test.go
@@ -1345,224 +1345,6 @@ func TestAddContainerNetworkAliasesButNoMatchingNetwork(t *testing.T) {
})
}
-func TestAddContainerNetworkAliasConflictWithName(t *testing.T) {
- runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- testCtr1, err := getTestCtr1(manager)
- assert.NoError(t, err)
-
- netName := "testnet"
- testCtr1.config.Networks = []string{netName}
- testCtr1.config.NetworkAliases = make(map[string][]string)
- testCtr1.config.NetworkAliases[netName] = []string{"alias1"}
-
- testCtr2, err := getTestCtr2(manager)
- assert.NoError(t, err)
-
- testCtr2.config.Networks = []string{netName}
- testCtr2.config.NetworkAliases = make(map[string][]string)
- testCtr2.config.NetworkAliases[netName] = []string{testCtr1.Name()}
-
- err = state.AddContainer(testCtr1)
- assert.NoError(t, err)
-
- err = state.AddContainer(testCtr2)
- assert.Error(t, err)
- })
-}
-
-func TestAddContainerNetworkAliasConflictWithAlias(t *testing.T) {
- runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- testCtr1, err := getTestCtr1(manager)
- assert.NoError(t, err)
-
- netName := "testnet"
- aliasName := "alias1"
- testCtr1.config.Networks = []string{netName}
- testCtr1.config.NetworkAliases = make(map[string][]string)
- testCtr1.config.NetworkAliases[netName] = []string{aliasName}
-
- testCtr2, err := getTestCtr2(manager)
- assert.NoError(t, err)
-
- testCtr2.config.Networks = []string{netName}
- testCtr2.config.NetworkAliases = make(map[string][]string)
- testCtr2.config.NetworkAliases[netName] = []string{aliasName}
-
- err = state.AddContainer(testCtr1)
- assert.NoError(t, err)
-
- err = state.AddContainer(testCtr2)
- assert.Error(t, err)
- })
-}
-
-func TestAddContainerNetworkAliasConflictWithAliasButDifferentNets(t *testing.T) {
- runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- testCtr1, err := getTestCtr1(manager)
- assert.NoError(t, err)
-
- netName := "testnet"
- aliasName := "alias1"
- testCtr1.config.Networks = []string{netName}
- testCtr1.config.NetworkAliases = make(map[string][]string)
- testCtr1.config.NetworkAliases[netName] = []string{aliasName}
-
- testCtr2, err := getTestCtr2(manager)
- assert.NoError(t, err)
-
- netName2 := "testnet2"
- testCtr2.config.Networks = []string{netName2}
- testCtr2.config.NetworkAliases = make(map[string][]string)
- testCtr2.config.NetworkAliases[netName2] = []string{aliasName}
-
- err = state.AddContainer(testCtr1)
- assert.NoError(t, err)
-
- err = state.AddContainer(testCtr2)
- assert.NoError(t, err)
- })
-}
-
-func TestAddContainerNameConflictsWithAliasRemovesAlias(t *testing.T) {
- runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- testCtr1, err := getTestCtr1(manager)
- assert.NoError(t, err)
-
- testCtr2, err := getTestCtr2(manager)
- assert.NoError(t, err)
-
- netName := "testnet"
- aliasName := testCtr2.Name()
- testCtr1.config.Networks = []string{netName}
- testCtr1.config.NetworkAliases = make(map[string][]string)
- testCtr1.config.NetworkAliases[netName] = []string{aliasName}
-
- testCtr2.config.Networks = []string{netName}
-
- err = state.AddContainer(testCtr1)
- assert.NoError(t, err)
-
- err = state.AddContainer(testCtr2)
- assert.NoError(t, err)
-
- aliases, err := state.GetNetworkAliases(testCtr1, netName)
- assert.NoError(t, err)
- assert.Equal(t, 0, len(aliases))
- })
-}
-
-func TestNetworkAliasAddAndRemoveSingleContainer(t *testing.T) {
- runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- testCtr, err := getTestCtr1(manager)
- assert.NoError(t, err)
-
- netName := "testnet"
- testCtr.config.Networks = []string{netName}
- testCtr.config.NetworkAliases = make(map[string][]string)
- testCtr.config.NetworkAliases[netName] = []string{"alias1"}
-
- startAliases, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 0, len(startAliases))
-
- err = state.AddContainer(testCtr)
- assert.NoError(t, err)
-
- oneAlias, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(oneAlias))
- assert.Equal(t, testCtr.ID(), oneAlias["alias1"])
-
- allAliases, err := state.GetAllNetworkAliases(testCtr)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(allAliases))
- netAliases, ok := allAliases[netName]
- assert.True(t, ok)
- assert.Equal(t, 1, len(netAliases))
- assert.Equal(t, "alias1", netAliases[0])
-
- ctrNetAliases, err := state.GetNetworkAliases(testCtr, netName)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(ctrNetAliases))
- assert.Equal(t, "alias1", ctrNetAliases[0])
-
- err = state.RemoveContainer(testCtr)
- assert.NoError(t, err)
-
- noAliases, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 0, len(noAliases))
- })
-}
-
-func TestNetworkAliasAddAndRemoveTwoContainers(t *testing.T) {
- runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- testCtr1, err := getTestCtr1(manager)
- assert.NoError(t, err)
-
- netName := "testnet"
- testCtr1.config.Networks = []string{netName}
- testCtr1.config.NetworkAliases = make(map[string][]string)
- testCtr1.config.NetworkAliases[netName] = []string{"alias1"}
-
- testCtr2, err := getTestCtr2(manager)
- assert.NoError(t, err)
-
- testCtr2.config.Networks = []string{netName}
- testCtr2.config.NetworkAliases = make(map[string][]string)
- testCtr2.config.NetworkAliases[netName] = []string{"alias2"}
-
- startAliases, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 0, len(startAliases))
-
- err = state.AddContainer(testCtr1)
- assert.NoError(t, err)
-
- oneAlias, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(oneAlias))
- assert.Equal(t, testCtr1.ID(), oneAlias["alias1"])
-
- err = state.AddContainer(testCtr2)
- assert.NoError(t, err)
-
- twoAliases, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 2, len(twoAliases))
- assert.Equal(t, testCtr1.ID(), twoAliases["alias1"])
- assert.Equal(t, testCtr2.ID(), twoAliases["alias2"])
-
- allAliases, err := state.GetAllNetworkAliases(testCtr1)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(allAliases))
- netAliases, ok := allAliases[netName]
- assert.True(t, ok)
- assert.Equal(t, 1, len(netAliases))
- assert.Equal(t, "alias1", netAliases[0])
-
- ctrNetAliases, err := state.GetNetworkAliases(testCtr1, netName)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(ctrNetAliases))
- assert.Equal(t, "alias1", ctrNetAliases[0])
-
- err = state.RemoveContainer(testCtr2)
- assert.NoError(t, err)
-
- oneAlias, err = state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(oneAlias))
- assert.Equal(t, testCtr1.ID(), oneAlias["alias1"])
-
- err = state.RemoveContainer(testCtr1)
- assert.NoError(t, err)
-
- noAliases, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 0, len(noAliases))
- })
-}
-
func TestCannotUseBadIDAsDependency(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
testCtr, err := getTestCtr1(manager)