Merge pull request #8298 from mheon/db_network_connect

Add support for network connect / disconnect to DB
This commit is contained in:
OpenShift Merge Robot
2020-11-12 19:40:31 +01:00
committed by GitHub
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,278 +1205,124 @@ 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())
}
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
}
// 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))
ctrNetworksBkt, err = dbCtr.CreateBucket(networksBkt)
if err != nil {
return errors.Wrapf(err, "could not create bucket for network aliases for network %q", network)
return errors.Wrapf(err, "error creating networks bucket for container %s", ctr.ID())
}
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))
ctrNetworks := ctr.config.Networks
if len(ctrNetworks) == 0 {
ctrNetworks = []string{ctr.runtime.netPlugin.GetDefaultNetworkName()}
}
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)
}
}
return nil
})
}
// RemoveNetworkAliases removes network aliases of the given container in the
// given network.
func (s *BoltState) RemoveNetworkAliases(ctr *Container, network string) error {
if !s.valid {
return define.ErrDBClosed
}
if !ctr.valid {
return define.ErrCtrRemoved
}
if network == "" {
return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty")
}
if s.namespace != "" && s.namespace != ctr.config.Namespace {
return 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 err
}
defer s.deferredCloseDBCon(db)
return db.Update(func(tx *bolt.Tx) error {
ctrBucket, err := getCtrBucket(tx)
if err != nil {
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())
}
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())
}
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 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.
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
}
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))
// 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.ErrNetworkExists, "container %s is already connected to CNI network %q", ctr.ID(), network)
}
// 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 errors.Wrapf(err, "error adding container %s network aliases bucket for network %s", ctr.ID(), network)
}
for _, alias := range aliases {
if err := ctrNetAliasesBkt.Put([]byte(alias), ctrID); err != nil {
return errors.Wrapf(err, "error adding container %s network alias %s for network %s", ctr.ID(), alias, network)
}
}
return nil
})
}
// 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
}
if !ctr.valid {
return define.ErrCtrRemoved
}
if network == "" {
return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty")
}
if s.namespace != "" && s.namespace != ctr.config.Namespace {
return 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 err
}
defer s.deferredCloseDBCon(db)
return db.Update(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())
}
ctrAliasesBkt := dbCtr.Bucket(aliasesBkt)
if ctrAliasesBkt == nil {
return errors.Wrapf(define.ErrNoAliases, "container %s has no network aliases", ctr.ID())
}
ctrNetworksBkt := dbCtr.Bucket(networksBkt)
if ctrNetworksBkt == nil {
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.ErrNoSuchNetwork, "container %s is not connected to CNI network %q", ctr.ID(), network)
}
if err := ctrNetworksBkt.Delete([]byte(network)); err != nil {
return errors.Wrapf(err, "error removing container %s from network %s", ctr.ID(), network)
}
bktExists := ctrAliasesBkt.Bucket([]byte(network))
if bktExists == nil {
return nil
})
}
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
})
}

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,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
// 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)