Add network aliases for containers to DB

This adds the database backend for network aliases. Aliases are
additional names for a container that are used with the CNI
dnsname plugin - the container will be accessible by these names
in addition to its name. Aliases are allowed to change over time
as the container connects to and disconnects from networks.

Aliases are implemented as another bucket in the database to
register all aliases, plus two buckets for each container (one to
hold connected CNI networks, a second to hold its aliases). The
aliases are only unique per-network, to the global and
per-container aliases buckets have a sub-bucket for each CNI
network that has aliases, and the aliases are stored within that
sub-bucket. Aliases are formatted as alias (key) to container ID
(value) in both cases.

Three DB functions are defined for aliases: retrieving current
aliases for a given network, setting aliases for a given network,
and removing all aliases for a given network.

Signed-off-by: Matthew Heon <matthew.heon@pm.me>
This commit is contained in:
Matthew Heon
2020-10-26 17:17:45 -04:00
parent 51fa8ded9f
commit 6af7e54463
8 changed files with 734 additions and 22 deletions

View File

@ -31,6 +31,10 @@ 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
// 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.
nameIndex *registrar.Registrar
// Global ID registry - ensures ID uniqueness and performs lookups.
@ -65,6 +69,9 @@ func NewInMemoryState() (State, error) {
state.podContainers = make(map[string]map[string]*Container)
state.networkAliases = make(map[string]map[string]string)
state.ctrNetworkAliases = make(map[string]map[string][]string)
state.nameIndex = registrar.NewRegistrar()
state.idIndex = truncindex.NewTruncIndex([]string{})
@ -278,6 +285,29 @@ func (s *InMemoryState) AddContainer(ctr *Container) error {
return err
}
// Check network aliases
for network, aliases := range ctr.config.NetworkAliases {
inNet := false
for _, net := range ctr.config.Networks {
if net == network {
inNet = true
break
}
}
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 {
if _, ok := allNetAliases[alias]; ok {
return define.ErrAliasExists
}
}
}
}
// There are potential race conditions with this
// But in-memory state is intended purely for testing and not production
// use, so this should be fine.
@ -334,6 +364,20 @@ func (s *InMemoryState) AddContainer(ctr *Container) error {
s.addCtrToVolDependsMap(ctr.ID(), vol.Name)
}
// 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
}
@ -396,6 +440,20 @@ func (s *InMemoryState) RemoveContainer(ctr *Container) error {
s.removeCtrFromVolDependsMap(ctr.ID(), vol.Name)
}
// 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)
}
}
}
delete(s.ctrNetworkAliases, ctr.ID())
}
return nil
}
@ -472,6 +530,153 @@ func (s *InMemoryState) AllContainers() ([]*Container, error) {
return ctrs, nil
}
// GetNetworkAliases returns network aliases for the given container in the
// given network.
func (s *InMemoryState) GetNetworkAliases(ctr *Container, network string) ([]string, error) {
if !ctr.valid {
return nil, define.ErrCtrRemoved
}
if network == "" {
return nil, errors.Wrapf(define.ErrInvalidArg, "network names must not be empty")
}
ctr, ok := s.containers[ctr.ID()]
if !ok {
return nil, define.ErrNoSuchCtr
}
inNet := false
for _, net := range ctr.config.Networks {
if net == network {
inNet = true
}
}
if !inNet {
return nil, define.ErrInvalidArg
}
ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()]
if !ok {
return []string{}, nil
}
netAliases, ok := ctrAliases[network]
if !ok {
return []string{}, nil
}
return netAliases, nil
}
// SetNetworkAliases sets network aliases for the given container in the given
// network.
func (s *InMemoryState) SetNetworkAliases(ctr *Container, network string, aliases []string) error {
if !ctr.valid {
return define.ErrCtrRemoved
}
if network == "" {
return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty")
}
ctr, ok := s.containers[ctr.ID()]
if !ok {
return define.ErrNoSuchCtr
}
inNet := false
for _, net := range ctr.config.Networks {
if net == network {
inNet = true
}
}
if !inNet {
return define.ErrInvalidArg
}
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 {
if !ctr.valid {
return define.ErrCtrRemoved
}
if network == "" {
return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty")
}
ctr, ok := s.containers[ctr.ID()]
if !ok {
return define.ErrNoSuchCtr
}
inNet := false
for _, net := range ctr.config.Networks {
if net == network {
inNet = true
}
}
if !inNet {
return define.ErrInvalidArg
}
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
}
// GetContainerConfig returns a container config from the database by full ID
func (s *InMemoryState) GetContainerConfig(id string) (*ContainerConfig, error) {
ctr, err := s.LookupContainer(id)
@ -1116,6 +1321,29 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
return err
}
// Check network aliases
for network, aliases := range ctr.config.NetworkAliases {
inNet := false
for _, net := range ctr.config.Networks {
if net == network {
inNet = true
break
}
}
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 {
if _, ok := allNetAliases[alias]; ok {
return define.ErrAliasExists
}
}
}
}
// Retrieve pod containers list
podCtrs, ok := s.podContainers[pod.ID()]
if !ok {
@ -1188,6 +1416,25 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
s.addCtrToDependsMap(ctr.ID(), depCtr)
}
// Add container to volume dependencies
for _, vol := range ctr.config.NamedVolumes {
s.addCtrToVolDependsMap(ctr.ID(), vol.Name)
}
// 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
}
@ -1268,6 +1515,20 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
s.removeCtrFromDependsMap(ctr.ID(), depCtr)
}
// 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)
}
}
}
delete(s.ctrNetworkAliases, ctr.ID())
}
return nil
}