Add support for network connect / disconnect to DB

Convert the existing network aliases set/remove code to network
connect and disconnect. We can no longer modify aliases for an
existing network, but we can add and remove entire networks. As
part of this, we need to add a new function to retrieve current
aliases the container is connected to (we had a table for this
as of the first aliases PR, but it was not externally exposed).

At the same time, remove all deconflicting logic for aliases.
Docker does absolutely no checks of this nature, and allows two
containers to have the same aliases, aliases that conflict with
container names, etc - it's just left to DNS to return all the
IP addresses, and presumably we round-robin from there? Most
tests for the existing code had to be removed because of this.

Convert all uses of the old container config.Networks field,
which previously included all networks in the container, to use
the new DB table. This ensures we actually get an up-to-date list
of in-use networks. Also, add network aliases to the output of
`podman inspect`.

Signed-off-by: Matthew Heon <matthew.heon@pm.me>
This commit is contained in:
Matthew Heon
2020-11-10 14:54:09 -05:00
parent ea75312895
commit 8d56eb5342
12 changed files with 347 additions and 867 deletions

View File

@ -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)
if netConnected != nil {
return errors.Wrapf(define.ErrNetworkExists, "container %s is already connected to CNI network %q", ctr.ID(), network)
}
namesBucket, err := getNamesBucket(tx)
// 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)
}
ctrNetAliasesBkt, err := ctrAliasesBkt.CreateBucketIfNotExists([]byte(network))
if err != nil {
return err
return errors.Wrapf(err, "error adding container %s network aliases bucket for network %s", 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
}
}
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
}
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,142 +1302,28 @@ 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)
return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not 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. 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
}
if err := ctrNetworksBkt.Delete([]byte(network)); err != nil {
return errors.Wrapf(err, "error removing container %s from network %s", 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
}
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)
if err := ctrAliasesBkt.DeleteBucket([]byte(network)); err != nil {
return errors.Wrapf(err, "error removing container %s network aliases for network %s", ctr.ID(), network)
}
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
}
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
})
})
}
// GetContainerConfig returns a container config from the database by full ID

View File

@ -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())
}

View File

@ -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
}

View File

@ -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"`

View File

@ -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())
}

View File

@ -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

View File

@ -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

View File

@ -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
// Add networks
newNets := make([]string, 0, len(ctr.config.Networks))
for _, net := range ctr.config.Networks {
if net == "" {
return define.ErrInvalidArg
}
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)
}
}
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,74 +652,37 @@ 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)
}
}
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
// Add networks
newNets := make([]string, 0, len(ctr.config.Networks))
for _, net := range ctr.config.Networks {
if net == "" {
return define.ErrInvalidArg
}
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)
}
}
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
}

View File

@ -104,7 +104,11 @@ 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
@ -209,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
@ -725,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
@ -745,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())
@ -753,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())
}
@ -839,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
@ -864,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
@ -882,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

View File

@ -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)

View File

@ -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)

View File

@ -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)