diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/boltdb_state.go | 295 | ||||
-rw-r--r-- | libpod/boltdb_state_internal.go | 148 | ||||
-rw-r--r-- | libpod/container.go | 28 | ||||
-rw-r--r-- | libpod/container_config.go | 3 | ||||
-rw-r--r-- | libpod/container_internal.go | 18 | ||||
-rw-r--r-- | libpod/define/container_inspect.go | 2 | ||||
-rw-r--r-- | libpod/define/errors.go | 9 | ||||
-rw-r--r-- | libpod/in_memory_state.go | 281 | ||||
-rw-r--r-- | libpod/networking_linux.go | 49 | ||||
-rw-r--r-- | libpod/rootless_cni_linux.go | 22 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 7 | ||||
-rw-r--r-- | libpod/state.go | 18 | ||||
-rw-r--r-- | libpod/state_test.go | 218 |
13 files changed, 293 insertions, 805 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index 0b9b353c7..be0adfe6a 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -94,7 +94,6 @@ func NewBoltState(path string, runtime *Runtime) (State, error) { volBkt, allVolsBkt, execBkt, - aliasesBkt, runtimeConfigBkt, } @@ -972,6 +971,58 @@ func (s *BoltState) AllContainers() ([]*Container, error) { return ctrs, nil } +// GetNetworks returns the CNI networks this container is a part of. +func (s *BoltState) GetNetworks(ctr *Container) ([]string, error) { + if !s.valid { + return nil, define.ErrDBClosed + } + + if !ctr.valid { + return nil, define.ErrCtrRemoved + } + + if s.namespace != "" && s.namespace != ctr.config.Namespace { + return nil, errors.Wrapf(define.ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace) + } + + ctrID := []byte(ctr.ID()) + + db, err := s.getDBCon() + if err != nil { + return nil, err + } + defer s.deferredCloseDBCon(db) + + networks := []string{} + + err = db.View(func(tx *bolt.Tx) error { + ctrBucket, err := getCtrBucket(tx) + if err != nil { + return err + } + + dbCtr := ctrBucket.Bucket(ctrID) + if dbCtr == nil { + ctr.valid = false + return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID()) + } + + ctrNetworkBkt := dbCtr.Bucket(networksBkt) + if ctrNetworkBkt == nil { + return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not joined to any CNI networks", ctr.ID()) + } + + return ctrNetworkBkt.ForEach(func(network, v []byte) error { + networks = append(networks, string(network)) + return nil + }) + }) + if err != nil { + return nil, err + } + return networks, nil +} + // GetNetworkAliases retrieves the network aliases for the given container in // the given CNI network. func (s *BoltState) GetNetworkAliases(ctr *Container, network string) ([]string, error) { @@ -1032,7 +1083,8 @@ func (s *BoltState) GetNetworkAliases(ctr *Container, network string) ([]string, netAliasesBkt := ctrAliasesBkt.Bucket([]byte(network)) if netAliasesBkt == nil { - return errors.Wrapf(define.ErrNoAliasesForNetwork, "container %s has no aliases for network %q", ctr.ID(), network) + // No aliases for this specific network. + return nil } return netAliasesBkt.ForEach(func(alias, v []byte) error { @@ -1120,10 +1172,9 @@ func (s *BoltState) GetAllNetworkAliases(ctr *Container) (map[string][]string, e return aliases, nil } -// SetNetworkAliases sets network aliases for the given container in the given -// network. All existing aliases for that network (if any exist) will be removed, -// to be replaced by the new aliases given. -func (s *BoltState) SetNetworkAliases(ctr *Container, network string, aliases []string) error { +// NetworkConnect adds the given container to the given network. If aliases are +// specified, those will be added to the given network. +func (s *BoltState) NetworkConnect(ctr *Container, network string, aliases []string) error { if !s.valid { return define.ErrDBClosed } @@ -1154,90 +1205,60 @@ func (s *BoltState) SetNetworkAliases(ctr *Container, network string, aliases [] return err } - allAliasesBucket, err := getAliasesBucket(tx) - if err != nil { - return err - } - - netAllAliasesBucket, err := allAliasesBucket.CreateBucketIfNotExists([]byte(network)) - if err != nil { - return errors.Wrapf(err, "error creating network aliases bucket for network %s", network) - } - dbCtr := ctrBucket.Bucket(ctrID) if dbCtr == nil { ctr.valid = false return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID()) } - ctrAliasesBkt := dbCtr.Bucket(aliasesBkt) - if ctrAliasesBkt == nil { - return errors.Wrapf(define.ErrNoAliases, "container %s has no network aliases", ctr.ID()) + ctrAliasesBkt, err := dbCtr.CreateBucketIfNotExists(aliasesBkt) + if err != nil { + return errors.Wrapf(err, "error creating aliases bucket for container %s", ctr.ID()) } ctrNetworksBkt := dbCtr.Bucket(networksBkt) if ctrNetworksBkt == nil { - return errors.Wrapf(define.ErrInvalidArg, "container %s is not connected to any CNI networks, so cannot add aliases", ctr.ID()) + ctrNetworksBkt, err = dbCtr.CreateBucket(networksBkt) + if err != nil { + return errors.Wrapf(err, "error creating networks bucket for container %s", ctr.ID()) + } + ctrNetworks := ctr.config.Networks + if len(ctrNetworks) == 0 { + ctrNetworks = []string{ctr.runtime.netPlugin.GetDefaultNetworkName()} + } + // Copy in all the container's CNI networks + for _, net := range ctrNetworks { + if err := ctrNetworksBkt.Put([]byte(net), ctrID); err != nil { + return errors.Wrapf(err, "error adding container %s network %s to DB", ctr.ID(), net) + } + } } netConnected := ctrNetworksBkt.Get([]byte(network)) - if netConnected == nil { - return errors.Wrapf(define.ErrInvalidArg, "container %s is not connected to CNI network %q, so cannot add aliases for this network", ctr.ID(), network) - } - - namesBucket, err := getNamesBucket(tx) - if err != nil { - return err + if netConnected != nil { + return errors.Wrapf(define.ErrNetworkExists, "container %s is already connected to CNI network %q", ctr.ID(), network) } - // Check if the container already has network aliases for this network. - netAliasesBkt := ctrAliasesBkt.Bucket([]byte(network)) - if netAliasesBkt != nil { - // We have aliases. Have to remove them. - forEachErr := netAliasesBkt.ForEach(func(alias, v []byte) error { - // Relies on errors.Wrapf(nil, ...) returning - // nil. - return errors.Wrapf(netAllAliasesBucket.Delete(alias), "error removing alias %q from network %q when changing aliases for container %s", string(alias), network, ctr.ID()) - }) - if forEachErr != nil { - return forEachErr - } + // Add the network + if err := ctrNetworksBkt.Put([]byte(network), ctrID); err != nil { + return errors.Wrapf(err, "error adding container %s to network %s in DB", ctr.ID(), network) } - if netAliasesBkt == nil { - newBkt, err := ctrAliasesBkt.CreateBucket([]byte(network)) - if err != nil { - return errors.Wrapf(err, "could not create bucket for network aliases for network %q", network) - } - netAliasesBkt = newBkt + ctrNetAliasesBkt, err := ctrAliasesBkt.CreateBucketIfNotExists([]byte(network)) + if err != nil { + return errors.Wrapf(err, "error adding container %s network aliases bucket for network %s", ctr.ID(), network) } - for _, alias := range aliases { - // Check if safe to use - aliasExists := netAllAliasesBucket.Get([]byte(alias)) - if aliasExists != nil { - return errors.Wrapf(define.ErrAliasExists, "network alias %q already exists in network %q (used by container %s)", alias, network, string(aliasExists)) - } - nameExists := namesBucket.Get([]byte(alias)) - if nameExists != nil { - return errors.Wrapf(define.ErrCtrExists, "a container or pod already uses the name %q, cannot add network alias for container %s", alias, ctr.ID()) - } - - // Add alias - if err := netAliasesBkt.Put([]byte(alias), ctrID); err != nil { - return errors.Wrapf(err, "error adding container %s network %q alias %q to DB", ctr.ID(), network, alias) - } - if err := netAllAliasesBucket.Put([]byte(alias), ctrID); err != nil { - return errors.Wrapf(err, "error adding container %s network %q alias %q to all aliases in DB", ctr.ID(), network, alias) + if err := ctrNetAliasesBkt.Put([]byte(alias), ctrID); err != nil { + return errors.Wrapf(err, "error adding container %s network alias %s for network %s", ctr.ID(), alias, network) } } - return nil }) } -// RemoveNetworkAliases removes network aliases of the given container in the -// given network. -func (s *BoltState) RemoveNetworkAliases(ctr *Container, network string) error { +// NetworkDisconnect disconnects the container from the given network, also +// removing any aliases in the network. +func (s *BoltState) NetworkDisconnect(ctr *Container, network string) error { if !s.valid { return define.ErrDBClosed } @@ -1268,16 +1289,6 @@ func (s *BoltState) RemoveNetworkAliases(ctr *Container, network string) error { return err } - allAliasesBucket, err := getAliasesBucket(tx) - if err != nil { - return err - } - - netAllAliasesBucket, err := allAliasesBucket.CreateBucketIfNotExists([]byte(network)) - if err != nil { - return errors.Wrapf(err, "error creating network aliases bucket for network %s", network) - } - dbCtr := ctrBucket.Bucket(ctrID) if dbCtr == nil { ctr.valid = false @@ -1291,141 +1302,27 @@ func (s *BoltState) RemoveNetworkAliases(ctr *Container, network string) error { ctrNetworksBkt := dbCtr.Bucket(networksBkt) if ctrNetworksBkt == nil { - return errors.Wrapf(define.ErrInvalidArg, "container %s is not connected to any CNI networks, so cannot add aliases", ctr.ID()) + return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not connected to any CNI networks, so cannot disconnect", ctr.ID()) } netConnected := ctrNetworksBkt.Get([]byte(network)) if netConnected == nil { - return errors.Wrapf(define.ErrInvalidArg, "container %s is not connected to CNI network %q, so cannot add aliases for this network", ctr.ID(), network) - } - - // Check if the container already has network aliases for this network. - netAliasesBkt := ctrAliasesBkt.Bucket([]byte(network)) - if netAliasesBkt != nil { - // We have aliases. Remove them. - forEachErr := netAliasesBkt.ForEach(func(alias, v []byte) error { - // Relies on errors.Wrapf(nil, ...) returning - // nil. - return errors.Wrapf(netAllAliasesBucket.Delete(alias), "error removing alias %q from network %q when changing aliases for container %s", string(alias), network, ctr.ID()) - }) - if forEachErr != nil { - return forEachErr - } + return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not connected to CNI network %q", ctr.ID(), network) } - return nil - }) -} - -// Get all network aliases for a single CNI network. Returns a map of alias to -// container ID. -func (s *BoltState) GetAllAliasesForNetwork(network string) (map[string]string, error) { - if !s.valid { - return nil, define.ErrDBClosed - } - - if network == "" { - return nil, errors.Wrapf(define.ErrInvalidArg, "network name must not be empty") - } - - db, err := s.getDBCon() - if err != nil { - return nil, err - } - defer s.deferredCloseDBCon(db) - - aliases := make(map[string]string) - - err = db.View(func(tx *bolt.Tx) error { - aliasBucket, err := getAliasesBucket(tx) - if err != nil { - return err + if err := ctrNetworksBkt.Delete([]byte(network)); err != nil { + return errors.Wrapf(err, "error removing container %s from network %s", ctr.ID(), network) } - dbAlias := aliasBucket.Bucket([]byte(network)) - if dbAlias == nil { - // We can't tell if the network exists, or doesn't exist - // So... Assume it exists, but has no aliases. + bktExists := ctrAliasesBkt.Bucket([]byte(network)) + if bktExists == nil { return nil } - return dbAlias.ForEach(func(alias, ctrId []byte) error { - aliases[string(alias)] = string(ctrId) - return nil - }) - }) - if err != nil { - return nil, err - } - - return aliases, nil -} - -// RemoveAllAliasesForNetwork removes all the aliases in a given CNI network, as -// part of that network being removed. -func (s *BoltState) RemoveAllAliasesForNetwork(network string) error { - if !s.valid { - return define.ErrDBClosed - } - - if network == "" { - return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty") - } - - db, err := s.getDBCon() - if err != nil { - return err - } - defer s.deferredCloseDBCon(db) - - return db.Update(func(tx *bolt.Tx) error { - allCtrsBucket, err := getAllCtrsBucket(tx) - if err != nil { - return err + if err := ctrAliasesBkt.DeleteBucket([]byte(network)); err != nil { + return errors.Wrapf(err, "error removing container %s network aliases for network %s", ctr.ID(), network) } - ctrBucket, err := getCtrBucket(tx) - if err != nil { - return err - } - - allAliasesBucket, err := getAliasesBucket(tx) - if err != nil { - return err - } - - checkAliasesBucketExists := allAliasesBucket.Bucket([]byte(network)) - if checkAliasesBucketExists != nil { - if err := allAliasesBucket.DeleteBucket([]byte(network)); err != nil { - return errors.Wrapf(err, "error removing network %s aliases bucket from DB", network) - } - } - - // Iterate through all containers and remove their aliases - // bucket for the network. - return allCtrsBucket.ForEach(func(ctrID, ctrName []byte) error { - dbCtr := ctrBucket.Bucket(ctrID) - if dbCtr == nil { - // DB State is inconsistent... but we can't do - // anything about it. - // Log and move on. - logrus.Errorf("Container %s listed in all containers, but has no bucket!", string(ctrID)) - return nil - } - - dbCtrAliases := dbCtr.Bucket(aliasesBkt) - if dbCtrAliases == nil { - // Container has no aliases, this is OK. - return nil - } - - ctrNetAliases := dbCtrAliases.Bucket([]byte(network)) - if ctrNetAliases != nil { - if err := dbCtrAliases.DeleteBucket([]byte(network)); err != nil { - return errors.Wrapf(err, "error removing bucket for network aliases for network %s from container %s", network, string(ctrID)) - } - } - return nil - }) + return nil }) } diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go index a48de3092..c06fedd3e 100644 --- a/libpod/boltdb_state_internal.go +++ b/libpod/boltdb_state_internal.go @@ -354,14 +354,6 @@ func getExecBucket(tx *bolt.Tx) (*bolt.Bucket, error) { return bkt, nil } -func getAliasesBucket(tx *bolt.Tx) (*bolt.Bucket, error) { - bkt := tx.Bucket(aliasesBkt) - if bkt == nil { - return nil, errors.Wrapf(define.ErrDBBadConfig, "aliases bucket not found in DB") - } - return bkt, nil -} - func getRuntimeConfigBucket(tx *bolt.Tx) (*bolt.Bucket, error) { bkt := tx.Bucket(runtimeConfigBkt) if bkt == nil { @@ -584,11 +576,6 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error { return err } - allAliasesBkt, err := getAliasesBucket(tx) - if err != nil { - return err - } - // If a pod was given, check if it exists var podDB *bolt.Bucket var podCtrs *bolt.Bucket @@ -635,41 +622,20 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error { return errors.Wrapf(err, "name \"%s\" is in use", ctr.Name()) } + allNets := make(map[string]bool) + // Check that we don't have any empty network names for _, net := range ctr.config.Networks { if net == "" { return errors.Wrapf(define.ErrInvalidArg, "network names cannot be an empty string") } + allNets[net] = true } - // If we have network aliases, check if they are already in use. - for net, aliases := range ctr.config.NetworkAliases { - // Aliases cannot conflict with container names. - for _, alias := range aliases { - aliasExist := namesBucket.Get([]byte(alias)) - if aliasExist != nil { - return errors.Wrapf(define.ErrCtrExists, "alias %q conflicts with existing container/pod name", alias) - } - } - - netAliasesBkt := allAliasesBkt.Bucket([]byte(net)) - if netAliasesBkt != nil { - for _, alias := range aliases { - aliasExist := netAliasesBkt.Get([]byte(alias)) - if aliasExist != nil { - return errors.Wrapf(define.ErrAliasExists, "network alias %q already exists for network %q", net, alias) - } - } - } - hasNet := false - for _, testNet := range ctr.config.Networks { - if testNet == net { - hasNet = true - break - } - } - if !hasNet { - return errors.Wrapf(define.ErrInvalidArg, "container %s has network aliases for network %q but is not part of that network", ctr.ID(), net) + // Each network we have aliases for, must exist in networks + for net := range ctr.config.NetworkAliases { + if !allNets[net] { + return errors.Wrapf(define.ErrNoSuchNetwork, "container %s has network aliases for network %q but is not part of that network", ctr.ID(), net) } } @@ -690,63 +656,6 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error { return errors.Wrapf(err, "error adding container %s to all containers bucket in DB", ctr.ID()) } - // Check aliases for all networks, remove conflicts with the - // container name. - for _, net := range ctr.config.Networks { - netAliasesBkt := allAliasesBkt.Bucket([]byte(net)) - if netAliasesBkt == nil { - continue - } - - otherCtrID := netAliasesBkt.Get(ctrName) - if otherCtrID == nil { - continue - } - - if err := netAliasesBkt.Delete(ctrName); err != nil { - return errors.Wrapf(err, "error removing container %s name from network aliases for network %s", ctr.ID(), net) - } - - // We now need to remove from the other container. - // To do this, we work through the container bucket, - // then its aliases bucket, then its aliases for this - // specific network, then we remove the alias. - // Just slightly ridiculous. Just slightly. - otherCtr := ctrBucket.Bucket(otherCtrID) - if otherCtr == nil { - // The state is inconsistent, but we can't do - // much... - logrus.Errorf("Container %s referred to by network alias but not present in state", string(otherCtrID)) - continue - } - otherCtrAliases := otherCtr.Bucket(aliasesBkt) - if otherCtrAliases == nil { - logrus.Errorf("Container %s is missing aliases but but has an alias", string(otherCtrID)) - continue - } - otherCtrNetworkAliases := otherCtrAliases.Bucket([]byte(net)) - if otherCtrNetworkAliases == nil { - logrus.Errorf("Container %s is missing network aliases bucket for network %s but has alias in that network", string(otherCtrID), net) - } - if otherCtrNetworkAliases.Get(ctrName) != nil { - if err := otherCtrNetworkAliases.Delete(ctrName); err != nil { - return errors.Wrapf(err, "error removing container %s name from network %s aliases of container %s", ctr.Name(), net, string(otherCtrID)) - } - } - } - - for net, aliases := range ctr.config.NetworkAliases { - netAliasesBkt, err := allAliasesBkt.CreateBucketIfNotExists([]byte(net)) - if err != nil { - return errors.Wrapf(err, "error creating network aliases bucket for network %q", net) - } - for _, alias := range aliases { - if err := netAliasesBkt.Put([]byte(alias), ctrID); err != nil { - return errors.Wrapf(err, "error adding container %s network alias %q to network %q", ctr.ID(), alias, net) - } - } - } - newCtrBkt, err := ctrBucket.CreateBucket(ctrID) if err != nil { return errors.Wrapf(err, "error adding container %s bucket to DB", ctr.ID()) @@ -998,49 +907,6 @@ func (s *BoltState) removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error return errors.Wrapf(define.ErrCtrExists, "container %s is a dependency of the following containers: %s", ctr.ID(), strings.Join(deps, ", ")) } - // Does the container have any network aliases? - ctrNetAliasesBkt := ctrExists.Bucket(aliasesBkt) - if ctrNetAliasesBkt != nil { - allAliasesBkt, err := getAliasesBucket(tx) - if err != nil { - return err - } - ctrNetworksBkt := ctrExists.Bucket(networksBkt) - // Internal state mismatch if this doesn't exist - we'll just - // assume there are no aliases in that case. - if ctrNetworksBkt != nil { - // This is a little gross. Iterate through all networks - // the container is joined to. Check if we have aliases - // for them. If we do have such aliases, remove all of - // then from the global aliases table for that network. - err = ctrNetworksBkt.ForEach(func(network, v []byte) error { - netAliasesBkt := ctrNetAliasesBkt.Bucket(network) - if netAliasesBkt == nil { - return nil - } - netAllAliasesBkt := allAliasesBkt.Bucket(network) - if netAllAliasesBkt == nil { - // Again the state is inconsistent here, - // but the best we can do is try and - // recover by ignoring it. - return nil - } - return netAliasesBkt.ForEach(func(alias, v []byte) error { - // We don't want to hard-fail on a - // missing alias, so continue if we hit - // errors. - if err := netAllAliasesBkt.Delete(alias); err != nil { - logrus.Errorf("Error removing alias %q from network %q when removing container %s", string(alias), string(network), ctr.ID()) - } - return nil - }) - }) - if err != nil { - return err - } - } - } - if err := ctrBucket.DeleteBucket(ctrID); err != nil { return errors.Wrapf(define.ErrInternal, "error deleting container %s from DB", ctr.ID()) } diff --git a/libpod/container.go b/libpod/container.go index ea5a6e09c..580fa7b3d 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -1085,3 +1085,31 @@ func (c *Container) Timezone() string { func (c *Container) Umask() string { return c.config.Umask } + +// Networks gets all the networks this container is connected to. +// Please do NOT use ctr.config.Networks, as this can be changed from those +// values at runtime via network connect and disconnect. +// If the container is configured to use CNI and this function returns an empty +// array, the container will still be connected to the default network. +func (c *Container) Networks() ([]string, error) { + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + + if err := c.syncContainer(); err != nil { + return nil, err + } + } + + return c.networks() +} + +// Unlocked accessor for networks +func (c *Container) networks() ([]string, error) { + networks, err := c.runtime.state.GetNetworks(c) + if err != nil && errors.Cause(err) == define.ErrNoSuchNetwork { + return c.config.Networks, nil + } + + return networks, err +} diff --git a/libpod/container_config.go b/libpod/container_config.go index d73fbb42f..cc3ad25ea 100644 --- a/libpod/container_config.go +++ b/libpod/container_config.go @@ -236,6 +236,9 @@ type ContainerNetworkConfig struct { // Will be appended to host's host file HostAdd []string `json:"hostsAdd,omitempty"` // Network names (CNI) to add container to. Empty to use default network. + // Please note that these can be altered at runtime. The actual list is + // stored in the DB and should be retrieved from there; this is only the + // set of networks the container was *created* with. Networks []string `json:"networks,omitempty"` // Network mode specified for the default network. NetMode namespaces.NetworkMode `json:"networkMode,omitempty"` diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 0aeaae43d..108954bad 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -641,12 +641,17 @@ func (c *Container) removeIPv4Allocations() error { cniDefaultNetwork = c.runtime.netPlugin.GetDefaultNetworkName() } + networks, err := c.networks() + if err != nil { + return err + } + switch { - case len(c.config.Networks) > 0 && len(c.config.Networks) != len(c.state.NetworkStatus): - return errors.Wrapf(define.ErrInternal, "network mismatch: asked to join %d CNI networks but got %d CNI results", len(c.config.Networks), len(c.state.NetworkStatus)) - case len(c.config.Networks) == 0 && len(c.state.NetworkStatus) != 1: + case len(networks) > 0 && len(networks) != len(c.state.NetworkStatus): + return errors.Wrapf(define.ErrInternal, "network mismatch: asked to join %d CNI networks but got %d CNI results", len(networks), len(c.state.NetworkStatus)) + case len(networks) == 0 && len(c.state.NetworkStatus) != 1: return errors.Wrapf(define.ErrInternal, "network mismatch: did not specify CNI networks but joined more than one (%d)", len(c.state.NetworkStatus)) - case len(c.config.Networks) == 0 && cniDefaultNetwork == "": + case len(networks) == 0 && cniDefaultNetwork == "": return errors.Wrapf(define.ErrInternal, "could not retrieve name of CNI default network") } @@ -656,11 +661,11 @@ func (c *Container) removeIPv4Allocations() error { continue } candidate := "" - if len(c.config.Networks) > 0 { + if len(networks) > 0 { // CNI returns networks in order we passed them. // So our index into results should be our index // into networks. - candidate = filepath.Join(cniNetworksDir, c.config.Networks[index], ctrIP.Address.IP.String()) + candidate = filepath.Join(cniNetworksDir, networks[index], ctrIP.Address.IP.String()) } else { candidate = filepath.Join(cniNetworksDir, cniDefaultNetwork, ctrIP.Address.IP.String()) } @@ -1503,6 +1508,7 @@ func (c *Container) mountStorage() (_ string, deferredErr error) { // config. // Returns the volume that was mounted. func (c *Container) mountNamedVolume(v *ContainerNamedVolume, mountpoint string) (*Volume, error) { + logrus.Debugf("Going to mount named volume %s", v.Name) vol, err := c.runtime.state.Volume(v.Name) if err != nil { return nil, errors.Wrapf(err, "error retrieving named volume %s for container %s", v.Name, c.ID()) diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go index 38b3a6686..775965477 100644 --- a/libpod/define/container_inspect.go +++ b/libpod/define/container_inspect.go @@ -575,6 +575,8 @@ type InspectAdditionalNetwork struct { // Links is presently unused and maintained exclusively for // compatibility. Links []string `json:"Links"` + // Aliases are any network aliases the container has in this network. + Aliases []string `json:"Aliases,omitempty"` } // InspectNetworkSettings holds information about the network settings of the diff --git a/libpod/define/errors.go b/libpod/define/errors.go index 27c5febf4..471827b7c 100644 --- a/libpod/define/errors.go +++ b/libpod/define/errors.go @@ -33,9 +33,6 @@ var ( // ErrNoAliases indicates that the container does not have any network // aliases. ErrNoAliases = errors.New("no aliases for container") - // ErrNoAliasesForNetwork indicates that the container has no aliases - // for a specific network. - ErrNoAliasesForNetwork = errors.New("no aliases for network") // ErrCtrExists indicates a container with the same name or ID already // exists @@ -49,9 +46,9 @@ var ( // ErrExecSessionExists indicates an exec session with the same ID // already exists. ErrExecSessionExists = errors.New("exec session already exists") - // ErrAliasExists indicates that a network alias with the same name - // already exists in the network. - ErrAliasExists = errors.New("alias already exists") + // ErrNetworkExists indicates that a network with the given name already + // exists. + ErrNetworkExists = errors.New("network already exists") // ErrCtrStateInvalid indicates a container is in an improper state for // the requested operation diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go index ba4c70c6b..6c0cde531 100644 --- a/libpod/in_memory_state.go +++ b/libpod/in_memory_state.go @@ -31,8 +31,7 @@ type InMemoryState struct { ctrExecSessions map[string][]string // Maps pod ID to a map of container ID to container struct. podContainers map[string]map[string]*Container - // Maps network name to alias to container ID - networkAliases map[string]map[string]string + ctrNetworks map[string][]string // Maps container ID to network name to list of aliases. ctrNetworkAliases map[string]map[string][]string // Global name registry - ensures name uniqueness and performs lookups. @@ -69,7 +68,7 @@ func NewInMemoryState() (State, error) { state.podContainers = make(map[string]map[string]*Container) - state.networkAliases = make(map[string]map[string]string) + state.ctrNetworks = make(map[string][]string) state.ctrNetworkAliases = make(map[string]map[string][]string) state.nameIndex = registrar.NewRegistrar() @@ -293,7 +292,7 @@ func (s *InMemoryState) AddContainer(ctr *Container) error { } // Check network aliases - for network, aliases := range ctr.config.NetworkAliases { + for network := range ctr.config.NetworkAliases { inNet := false for _, net := range ctr.config.Networks { if net == network { @@ -304,19 +303,6 @@ func (s *InMemoryState) AddContainer(ctr *Container) error { if !inNet { return errors.Wrapf(define.ErrInvalidArg, "container %s has network aliases for network %q but is not joined to network", ctr.ID(), network) } - - allNetAliases, ok := s.networkAliases[network] - if ok { - for _, alias := range aliases { - // Check if alias is a name - if _, err := s.nameIndex.Get(alias); err == nil { - return define.ErrInvalidArg - } - if _, ok := allNetAliases[alias]; ok { - return define.ErrAliasExists - } - } - } } // There are potential race conditions with this @@ -375,46 +361,17 @@ func (s *InMemoryState) AddContainer(ctr *Container) error { s.addCtrToVolDependsMap(ctr.ID(), vol.Name) } - for _, network := range ctr.config.Networks { - allNetAliases, ok := s.networkAliases[network] - if !ok { - continue - } - otherCtrID, ok := allNetAliases[ctr.Name()] - if !ok { - continue - } - delete(allNetAliases, ctr.Name()) - - otherCtrAliases, ok := s.ctrNetworkAliases[otherCtrID] - if !ok { - continue - } - otherCtrNetAliases, ok := otherCtrAliases[network] - if !ok { - continue - } - newAliases := []string{} - for _, alias := range otherCtrNetAliases { - if alias != ctr.Name() { - newAliases = append(newAliases, alias) - } + // Add networks + newNets := make([]string, 0, len(ctr.config.Networks)) + for _, net := range ctr.config.Networks { + if net == "" { + return define.ErrInvalidArg } - otherCtrAliases[network] = newAliases + newNets = append(newNets, net) } + s.ctrNetworks[ctr.ID()] = newNets // Add network aliases - for network, aliases := range ctr.config.NetworkAliases { - allNetAliases, ok := s.networkAliases[network] - if !ok { - allNetAliases = make(map[string]string) - s.networkAliases[network] = allNetAliases - } - - for _, alias := range aliases { - allNetAliases[alias] = ctr.ID() - } - } s.ctrNetworkAliases[ctr.ID()] = ctr.config.NetworkAliases return nil @@ -480,18 +437,12 @@ func (s *InMemoryState) RemoveContainer(ctr *Container) error { } // Remove our network aliases - ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()] - if ok { - for network, aliases := range ctrAliases { - netAliases, ok := s.networkAliases[network] - if ok { - for _, alias := range aliases { - delete(netAliases, alias) - } - } - } + if _, ok := s.ctrNetworkAliases[ctr.ID()]; ok { delete(s.ctrNetworkAliases, ctr.ID()) } + if _, ok := s.ctrNetworks[ctr.ID()]; ok { + delete(s.ctrNetworks, ctr.ID()) + } return nil } @@ -569,6 +520,26 @@ func (s *InMemoryState) AllContainers() ([]*Container, error) { return ctrs, nil } +// Get all networks this container is present in. +func (s *InMemoryState) GetNetworks(ctr *Container) ([]string, error) { + if !ctr.valid { + return nil, define.ErrCtrRemoved + } + + ctr, ok := s.containers[ctr.ID()] + if !ok { + ctr.valid = false + return nil, define.ErrNoSuchCtr + } + + ctrNetworks, ok := s.ctrNetworks[ctr.ID()] + if !ok { + return nil, define.ErrNoSuchNetwork + } + + return ctrNetworks, nil +} + // GetNetworkAliases returns network aliases for the given container in the // given network. func (s *InMemoryState) GetNetworkAliases(ctr *Container, network string) ([]string, error) { @@ -582,6 +553,7 @@ func (s *InMemoryState) GetNetworkAliases(ctr *Container, network string) ([]str ctr, ok := s.containers[ctr.ID()] if !ok { + ctr.valid = false return nil, define.ErrNoSuchCtr } @@ -615,6 +587,7 @@ func (s *InMemoryState) GetAllNetworkAliases(ctr *Container) (map[string][]strin ctr, ok := s.containers[ctr.ID()] if !ok { + ctr.valid = false return nil, define.ErrNoSuchCtr } @@ -626,9 +599,8 @@ func (s *InMemoryState) GetAllNetworkAliases(ctr *Container) (map[string][]strin return ctrAliases, nil } -// SetNetworkAliases sets network aliases for the given container in the given -// network. -func (s *InMemoryState) SetNetworkAliases(ctr *Container, network string, aliases []string) error { +// NetworkConnect connects to the given network +func (s *InMemoryState) NetworkConnect(ctr *Container, network string, aliases []string) error { if !ctr.valid { return define.ErrCtrRemoved } @@ -639,55 +611,37 @@ func (s *InMemoryState) SetNetworkAliases(ctr *Container, network string, aliase ctr, ok := s.containers[ctr.ID()] if !ok { + ctr.valid = false return define.ErrNoSuchCtr } inNet := false - for _, net := range ctr.config.Networks { + ctrNetworks, ok := s.ctrNetworks[ctr.ID()] + if !ok { + return define.ErrNoSuchNetwork + } + for _, net := range ctrNetworks { if net == network { inNet = true } } - if !inNet { - return define.ErrInvalidArg + if inNet { + return define.ErrNoSuchNetwork } + s.ctrNetworks[ctr.ID()] = append(ctrNetworks, network) ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()] if !ok { ctrAliases = make(map[string][]string) s.ctrNetworkAliases[ctr.ID()] = ctrAliases } - netAliases, ok := ctrAliases[network] - if !ok { - netAliases = []string{} - ctrAliases[network] = netAliases - } - - allAliases, ok := s.networkAliases[network] - if !ok { - allAliases = make(map[string]string) - s.networkAliases[network] = allAliases - } - - for _, alias := range netAliases { - delete(allAliases, alias) - } - - for _, newAlias := range aliases { - if _, ok := allAliases[newAlias]; ok { - return define.ErrAliasExists - } - allAliases[newAlias] = ctr.ID() - } - ctrAliases[network] = aliases return nil } -// RemoveNetworkAliases removes network aliases from the given container in the -// given network. -func (s *InMemoryState) RemoveNetworkAliases(ctr *Container, network string) error { +// Disconnect from the given network and remove all aliases in it. +func (s *InMemoryState) NetworkDisconnect(ctr *Container, network string) error { if !ctr.valid { return define.ErrCtrRemoved } @@ -698,73 +652,36 @@ func (s *InMemoryState) RemoveNetworkAliases(ctr *Container, network string) err ctr, ok := s.containers[ctr.ID()] if !ok { + ctr.valid = false return define.ErrNoSuchCtr } + ctrNetworks, ok := s.ctrNetworks[ctr.ID()] + if !ok { + return define.ErrNoSuchNetwork + } inNet := false - for _, net := range ctr.config.Networks { + remainingNets := make([]string, 0, len(ctrNetworks)) + for _, net := range ctrNetworks { if net == network { inNet = true + break + } else { + remainingNets = append(remainingNets, net) } } if !inNet { - return define.ErrInvalidArg + return define.ErrNoSuchNetwork } + s.ctrNetworks[ctr.ID()] = remainingNets ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()] if !ok { ctrAliases = make(map[string][]string) s.ctrNetworkAliases[ctr.ID()] = ctrAliases } - netAliases, ok := ctrAliases[network] - if !ok { - netAliases = []string{} - ctrAliases[network] = netAliases - } - - allAliases, ok := s.networkAliases[network] - if !ok { - allAliases = make(map[string]string) - s.networkAliases[network] = allAliases - } - - for _, alias := range netAliases { - delete(allAliases, alias) - } - - return nil -} - -// GetAllAliasesForNetwork gets all the aliases for a single network. -func (s *InMemoryState) GetAllAliasesForNetwork(network string) (map[string]string, error) { - if network == "" { - return nil, errors.Wrapf(define.ErrInvalidArg, "network names must not be empty") - } - - allAliases, ok := s.networkAliases[network] - if !ok { - // Can't tell if the network exists. - // Assume it does. - return map[string]string{}, nil - } - - return allAliases, nil -} - -// RemoveAllAliasesForNetwork removes all the aliases for a given network. -func (s *InMemoryState) RemoveAllAliasesForNetwork(network string) error { - if network == "" { - return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty") - } - - if _, ok := s.networkAliases[network]; ok { - delete(s.networkAliases, network) - } - - for _, ctrAliases := range s.ctrNetworkAliases { - if _, ok := ctrAliases[network]; ok { - delete(ctrAliases, network) - } + if _, ok := ctrAliases[network]; ok { + delete(ctrAliases, network) } return nil @@ -1422,7 +1339,7 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error { } // Check network aliases - for network, aliases := range ctr.config.NetworkAliases { + for network := range ctr.config.NetworkAliases { inNet := false for _, net := range ctr.config.Networks { if net == network { @@ -1433,19 +1350,6 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error { if !inNet { return errors.Wrapf(define.ErrInvalidArg, "container %s has network aliases for network %q but is not joined to network", ctr.ID(), network) } - - allNetAliases, ok := s.networkAliases[network] - if ok { - for _, alias := range aliases { - // Check if alias is a name - if _, err := s.nameIndex.Get(alias); err == nil { - return define.ErrInvalidArg - } - if _, ok := allNetAliases[alias]; ok { - return define.ErrAliasExists - } - } - } } // Retrieve pod containers list @@ -1525,46 +1429,17 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error { s.addCtrToVolDependsMap(ctr.ID(), vol.Name) } - for _, network := range ctr.config.Networks { - allNetAliases, ok := s.networkAliases[network] - if !ok { - continue - } - otherCtrID, ok := allNetAliases[ctr.Name()] - if !ok { - continue - } - delete(allNetAliases, ctr.Name()) - - otherCtrAliases, ok := s.ctrNetworkAliases[otherCtrID] - if !ok { - continue - } - otherCtrNetAliases, ok := otherCtrAliases[network] - if !ok { - continue - } - newAliases := []string{} - for _, alias := range otherCtrNetAliases { - if alias != ctr.Name() { - newAliases = append(newAliases, alias) - } + // Add networks + newNets := make([]string, 0, len(ctr.config.Networks)) + for _, net := range ctr.config.Networks { + if net == "" { + return define.ErrInvalidArg } - otherCtrAliases[network] = newAliases + newNets = append(newNets, net) } + s.ctrNetworks[ctr.ID()] = newNets // Add network aliases - for network, aliases := range ctr.config.NetworkAliases { - allNetAliases, ok := s.networkAliases[network] - if !ok { - allNetAliases = make(map[string]string) - s.networkAliases[network] = allNetAliases - } - - for _, alias := range aliases { - allNetAliases[alias] = ctr.ID() - } - } s.ctrNetworkAliases[ctr.ID()] = ctr.config.NetworkAliases return nil @@ -1648,18 +1523,12 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error { } // Remove our network aliases - ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()] - if ok { - for network, aliases := range ctrAliases { - netAliases, ok := s.networkAliases[network] - if ok { - for _, alias := range aliases { - delete(netAliases, alias) - } - } - } + if _, ok := s.ctrNetworkAliases[ctr.ID()]; ok { delete(s.ctrNetworkAliases, ctr.ID()) } + if _, ok := s.ctrNetworks[ctr.ID()]; ok { + delete(s.ctrNetworks, ctr.ID()) + } return nil } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 4bc9381bd..fed90cfc3 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -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 @@ -556,7 +564,7 @@ func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath strin if stdoutStr != "" { // err contains full debug log and too verbose, so return stdoutStr logrus.Debug(err) - return errors.Errorf("failed to expose ports via rootlessport: %q", stdoutStr) + return errors.Errorf("rootlessport " + strings.TrimSuffix(stdoutStr, "\n")) } return err } @@ -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 diff --git a/libpod/rootless_cni_linux.go b/libpod/rootless_cni_linux.go index 3d4ff6e86..1d6158cc2 100644 --- a/libpod/rootless_cni_linux.go +++ b/libpod/rootless_cni_linux.go @@ -40,8 +40,12 @@ const ( // // AllocRootlessCNI does not lock c. c should be already locked. func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes.Result, error) { - if len(c.config.Networks) == 0 { - return nil, nil, errors.New("allocRootlessCNI shall not be called when len(c.config.Networks) == 0") + networks, err := c.networks() + if err != nil { + return nil, nil, err + } + if len(networks) == 0 { + return nil, nil, errors.New("rootless CNI networking requires that the container has joined at least one CNI network") } l, err := getRootlessCNIInfraLock(c.runtime) if err != nil { @@ -54,8 +58,8 @@ func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes. return nil, nil, err } k8sPodName := getCNIPodName(c) // passed to CNI as K8S_POD_NAME - cniResults := make([]*cnitypes.Result, len(c.config.Networks)) - for i, nw := range c.config.Networks { + cniResults := make([]*cnitypes.Result, len(networks)) + for i, nw := range networks { cniRes, err := rootlessCNIInfraCallAlloc(infra, c.ID(), nw, k8sPodName) if err != nil { return nil, nil, err @@ -77,8 +81,12 @@ func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes. // // DeallocRootlessCNI does not lock c. c should be already locked. func DeallocRootlessCNI(ctx context.Context, c *Container) error { - if len(c.config.Networks) == 0 { - return errors.New("deallocRootlessCNI shall not be called when len(c.config.Networks) == 0") + networks, err := c.networks() + if err != nil { + return err + } + if len(networks) == 0 { + return errors.New("rootless CNI networking requires that the container has joined at least one CNI network") } l, err := getRootlessCNIInfraLock(c.runtime) if err != nil { @@ -91,7 +99,7 @@ func DeallocRootlessCNI(ctx context.Context, c *Container) error { return nil } var errs *multierror.Error - for _, nw := range c.config.Networks { + for _, nw := range networks { err := rootlessCNIInfraCallDelloc(infra, c.ID(), nw) if err != nil { errs = multierror.Append(errs, err) diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index c84268889..14b537ca2 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -345,8 +345,15 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai // Lock all named volumes we are adding ourself to, to ensure we can't // use a volume being removed. + volsLocked := make(map[string]bool) for _, namedVol := range ctrNamedVolumes { toLock := namedVol + // Ensure that we don't double-lock a named volume that is used + // more than once. + if volsLocked[namedVol.Name()] { + continue + } + volsLocked[namedVol.Name()] = true toLock.lock.Lock() defer toLock.lock.Unlock() } diff --git a/libpod/state.go b/libpod/state.go index 183f773b5..074d21740 100644 --- a/libpod/state.go +++ b/libpod/state.go @@ -98,20 +98,18 @@ type State interface { // returned. AllContainers() ([]*Container, error) + // Get networks the container is currently connected to. + GetNetworks(ctr *Container) ([]string, error) // Get network aliases for the given container in the given network. GetNetworkAliases(ctr *Container, network string) ([]string, error) // Get all network aliases for the given container. GetAllNetworkAliases(ctr *Container) (map[string][]string, error) - // Set network aliases for the given container in the given network. - SetNetworkAliases(ctr *Container, network string, aliases []string) error - // Remove network aliases for the given container in the given network. - RemoveNetworkAliases(ctr *Container, network string) error - // GetAllAliasesForNetwork returns all the aliases for a given - // network. Returns a map of alias to container ID. - GetAllAliasesForNetwork(network string) (map[string]string, error) - // RemoveAllAliasesForNetwork removes all the aliases for a given - // network. - RemoveAllAliasesForNetwork(network string) error + // Add the container to the given network, adding the given aliases + // (if present). + NetworkConnect(ctr *Container, network string, aliases []string) error + // Remove the container from the given network, removing all aliases for + // the container in that network in the process. + NetworkDisconnect(ctr *Container, network string) error // Return a container config from the database by full ID GetContainerConfig(id string) (*ContainerConfig, error) diff --git a/libpod/state_test.go b/libpod/state_test.go index cf41270bf..da28f3d3f 100644 --- a/libpod/state_test.go +++ b/libpod/state_test.go @@ -1345,224 +1345,6 @@ func TestAddContainerNetworkAliasesButNoMatchingNetwork(t *testing.T) { }) } -func TestAddContainerNetworkAliasConflictWithName(t *testing.T) { - runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) { - testCtr1, err := getTestCtr1(manager) - assert.NoError(t, err) - - netName := "testnet" - testCtr1.config.Networks = []string{netName} - testCtr1.config.NetworkAliases = make(map[string][]string) - testCtr1.config.NetworkAliases[netName] = []string{"alias1"} - - testCtr2, err := getTestCtr2(manager) - assert.NoError(t, err) - - testCtr2.config.Networks = []string{netName} - testCtr2.config.NetworkAliases = make(map[string][]string) - testCtr2.config.NetworkAliases[netName] = []string{testCtr1.Name()} - - err = state.AddContainer(testCtr1) - assert.NoError(t, err) - - err = state.AddContainer(testCtr2) - assert.Error(t, err) - }) -} - -func TestAddContainerNetworkAliasConflictWithAlias(t *testing.T) { - runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) { - testCtr1, err := getTestCtr1(manager) - assert.NoError(t, err) - - netName := "testnet" - aliasName := "alias1" - testCtr1.config.Networks = []string{netName} - testCtr1.config.NetworkAliases = make(map[string][]string) - testCtr1.config.NetworkAliases[netName] = []string{aliasName} - - testCtr2, err := getTestCtr2(manager) - assert.NoError(t, err) - - testCtr2.config.Networks = []string{netName} - testCtr2.config.NetworkAliases = make(map[string][]string) - testCtr2.config.NetworkAliases[netName] = []string{aliasName} - - err = state.AddContainer(testCtr1) - assert.NoError(t, err) - - err = state.AddContainer(testCtr2) - assert.Error(t, err) - }) -} - -func TestAddContainerNetworkAliasConflictWithAliasButDifferentNets(t *testing.T) { - runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) { - testCtr1, err := getTestCtr1(manager) - assert.NoError(t, err) - - netName := "testnet" - aliasName := "alias1" - testCtr1.config.Networks = []string{netName} - testCtr1.config.NetworkAliases = make(map[string][]string) - testCtr1.config.NetworkAliases[netName] = []string{aliasName} - - testCtr2, err := getTestCtr2(manager) - assert.NoError(t, err) - - netName2 := "testnet2" - testCtr2.config.Networks = []string{netName2} - testCtr2.config.NetworkAliases = make(map[string][]string) - testCtr2.config.NetworkAliases[netName2] = []string{aliasName} - - err = state.AddContainer(testCtr1) - assert.NoError(t, err) - - err = state.AddContainer(testCtr2) - assert.NoError(t, err) - }) -} - -func TestAddContainerNameConflictsWithAliasRemovesAlias(t *testing.T) { - runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) { - testCtr1, err := getTestCtr1(manager) - assert.NoError(t, err) - - testCtr2, err := getTestCtr2(manager) - assert.NoError(t, err) - - netName := "testnet" - aliasName := testCtr2.Name() - testCtr1.config.Networks = []string{netName} - testCtr1.config.NetworkAliases = make(map[string][]string) - testCtr1.config.NetworkAliases[netName] = []string{aliasName} - - testCtr2.config.Networks = []string{netName} - - err = state.AddContainer(testCtr1) - assert.NoError(t, err) - - err = state.AddContainer(testCtr2) - assert.NoError(t, err) - - aliases, err := state.GetNetworkAliases(testCtr1, netName) - assert.NoError(t, err) - assert.Equal(t, 0, len(aliases)) - }) -} - -func TestNetworkAliasAddAndRemoveSingleContainer(t *testing.T) { - runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) { - testCtr, err := getTestCtr1(manager) - assert.NoError(t, err) - - netName := "testnet" - testCtr.config.Networks = []string{netName} - testCtr.config.NetworkAliases = make(map[string][]string) - testCtr.config.NetworkAliases[netName] = []string{"alias1"} - - startAliases, err := state.GetAllAliasesForNetwork(netName) - assert.NoError(t, err) - assert.Equal(t, 0, len(startAliases)) - - err = state.AddContainer(testCtr) - assert.NoError(t, err) - - oneAlias, err := state.GetAllAliasesForNetwork(netName) - assert.NoError(t, err) - assert.Equal(t, 1, len(oneAlias)) - assert.Equal(t, testCtr.ID(), oneAlias["alias1"]) - - allAliases, err := state.GetAllNetworkAliases(testCtr) - assert.NoError(t, err) - assert.Equal(t, 1, len(allAliases)) - netAliases, ok := allAliases[netName] - assert.True(t, ok) - assert.Equal(t, 1, len(netAliases)) - assert.Equal(t, "alias1", netAliases[0]) - - ctrNetAliases, err := state.GetNetworkAliases(testCtr, netName) - assert.NoError(t, err) - assert.Equal(t, 1, len(ctrNetAliases)) - assert.Equal(t, "alias1", ctrNetAliases[0]) - - err = state.RemoveContainer(testCtr) - assert.NoError(t, err) - - noAliases, err := state.GetAllAliasesForNetwork(netName) - assert.NoError(t, err) - assert.Equal(t, 0, len(noAliases)) - }) -} - -func TestNetworkAliasAddAndRemoveTwoContainers(t *testing.T) { - runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) { - testCtr1, err := getTestCtr1(manager) - assert.NoError(t, err) - - netName := "testnet" - testCtr1.config.Networks = []string{netName} - testCtr1.config.NetworkAliases = make(map[string][]string) - testCtr1.config.NetworkAliases[netName] = []string{"alias1"} - - testCtr2, err := getTestCtr2(manager) - assert.NoError(t, err) - - testCtr2.config.Networks = []string{netName} - testCtr2.config.NetworkAliases = make(map[string][]string) - testCtr2.config.NetworkAliases[netName] = []string{"alias2"} - - startAliases, err := state.GetAllAliasesForNetwork(netName) - assert.NoError(t, err) - assert.Equal(t, 0, len(startAliases)) - - err = state.AddContainer(testCtr1) - assert.NoError(t, err) - - oneAlias, err := state.GetAllAliasesForNetwork(netName) - assert.NoError(t, err) - assert.Equal(t, 1, len(oneAlias)) - assert.Equal(t, testCtr1.ID(), oneAlias["alias1"]) - - err = state.AddContainer(testCtr2) - assert.NoError(t, err) - - twoAliases, err := state.GetAllAliasesForNetwork(netName) - assert.NoError(t, err) - assert.Equal(t, 2, len(twoAliases)) - assert.Equal(t, testCtr1.ID(), twoAliases["alias1"]) - assert.Equal(t, testCtr2.ID(), twoAliases["alias2"]) - - allAliases, err := state.GetAllNetworkAliases(testCtr1) - assert.NoError(t, err) - assert.Equal(t, 1, len(allAliases)) - netAliases, ok := allAliases[netName] - assert.True(t, ok) - assert.Equal(t, 1, len(netAliases)) - assert.Equal(t, "alias1", netAliases[0]) - - ctrNetAliases, err := state.GetNetworkAliases(testCtr1, netName) - assert.NoError(t, err) - assert.Equal(t, 1, len(ctrNetAliases)) - assert.Equal(t, "alias1", ctrNetAliases[0]) - - err = state.RemoveContainer(testCtr2) - assert.NoError(t, err) - - oneAlias, err = state.GetAllAliasesForNetwork(netName) - assert.NoError(t, err) - assert.Equal(t, 1, len(oneAlias)) - assert.Equal(t, testCtr1.ID(), oneAlias["alias1"]) - - err = state.RemoveContainer(testCtr1) - assert.NoError(t, err) - - noAliases, err := state.GetAllAliasesForNetwork(netName) - assert.NoError(t, err) - assert.Equal(t, 0, len(noAliases)) - }) -} - func TestCannotUseBadIDAsDependency(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) { testCtr, err := getTestCtr1(manager) |